/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.geode.cache30;
import static org.apache.geode.distributed.ConfigurationProperties.*;
import static org.junit.Assert.*;
import java.util.Map;
import java.util.Properties;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.apache.geode.cache.AttributesFactory;
import org.apache.geode.cache.CacheException;
import org.apache.geode.cache.CacheListener;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.EntryEvent;
import org.apache.geode.cache.EntryNotFoundException;
import org.apache.geode.cache.RegionAttributes;
import org.apache.geode.cache.RegionFactory;
import org.apache.geode.cache.RegionShortcut;
import org.apache.geode.cache.Scope;
import org.apache.geode.cache.util.CacheListenerAdapter;
import org.apache.geode.distributed.internal.InternalDistributedSystem;
import org.apache.geode.internal.cache.LocalRegion;
import org.apache.geode.test.dunit.Assert;
import org.apache.geode.test.dunit.AsyncInvocation;
import org.apache.geode.test.dunit.Host;
import org.apache.geode.test.dunit.LogWriterUtils;
import org.apache.geode.test.dunit.SerializableRunnable;
import org.apache.geode.test.dunit.VM;
import org.apache.geode.test.dunit.Wait;
import org.apache.geode.test.junit.categories.DistributedTest;
import org.apache.geode.test.junit.categories.FlakyTest;
@Category(DistributedTest.class)
public class DistributedNoAckRegionCCEDUnitTest extends DistributedNoAckRegionDUnitTest {
static volatile boolean ListenerBlocking;
@Override
public Properties getDistributedSystemProperties() {
Properties p = super.getDistributedSystemProperties();
p.put(CONSERVE_SOCKETS, "false");
if (distributedSystemID > 0) {
p.put(DISTRIBUTED_SYSTEM_ID, "" + distributedSystemID);
}
p.put(SOCKET_BUFFER_SIZE, "" + 2000000);
return p;
}
/**
* Returns region attributes for a <code>GLOBAL</code> region
*/
protected RegionAttributes getRegionAttributes() {
AttributesFactory factory = new AttributesFactory();
factory.setScope(Scope.DISTRIBUTED_NO_ACK);
factory.setDataPolicy(DataPolicy.REPLICATE);
factory.setConcurrencyChecksEnabled(true);
return factory.create();
}
protected RegionAttributes getRegionAttributes(String type) {
RegionAttributes ra = getCache().getRegionAttributes(type);
if (ra == null) {
throw new IllegalStateException("The region shortcut " + type + " has been removed.");
}
AttributesFactory factory = new AttributesFactory(ra);
factory.setScope(Scope.DISTRIBUTED_NO_ACK);
factory.setConcurrencyChecksEnabled(true);
return factory.create();
}
@Override
public void sendSerialMessageToAll() {
try {
org.apache.geode.distributed.internal.SerialAckedMessage msg =
new org.apache.geode.distributed.internal.SerialAckedMessage();
msg.send(InternalDistributedSystem.getConnectedInstance().getDM()
.getNormalDistributionManagerIds(), false);
} catch (Exception e) {
throw new RuntimeException("Unable to send serial message due to exception", e);
}
}
@Override
@Test
public void testLocalDestroy() throws InterruptedException {
// replicates don't allow local destroy
}
@Override
@Test
public void testEntryTtlLocalDestroy() throws InterruptedException {
// replicates don't allow local destroy
}
@Test
public void testClearWithManyEventsInFlight() throws Exception {
Host host = Host.getHost(0);
VM vm0 = host.getVM(0);
VM vm1 = host.getVM(1);
VM vm2 = host.getVM(2);
VM vm3 = host.getVM(3);
// create replicated regions in VM 0 and 1, then perform concurrent ops
// on the same key while creating the region in VM2. Afterward make
// sure that all three regions are consistent
final String name = this.getUniqueName() + "-CC";
createRegionWithAttribute(vm0, name, false);
createRegionWithAttribute(vm1, name, false);
createRegionWithAttribute(vm2, name, false);
createRegionWithAttribute(vm3, name, false);
vm0.invoke(() -> DistributedNoAckRegionCCEDUnitTest.addBlockingListener());
vm1.invoke(() -> DistributedNoAckRegionCCEDUnitTest.addBlockingListener());
vm2.invoke(() -> DistributedNoAckRegionCCEDUnitTest.addBlockingListener());
AsyncInvocation vm0Ops = vm0.invokeAsync(() -> DistributedNoAckRegionCCEDUnitTest.doManyOps());
AsyncInvocation vm1Ops = vm1.invokeAsync(() -> DistributedNoAckRegionCCEDUnitTest.doManyOps());
AsyncInvocation vm2Ops = vm2.invokeAsync(() -> DistributedNoAckRegionCCEDUnitTest.doManyOps());
// pause to let a bunch of operations build up
Wait.pause(5000);
AsyncInvocation a0 = vm3.invokeAsync(() -> DistributedNoAckRegionCCEDUnitTest.clearRegion());
vm0.invoke(() -> DistributedNoAckRegionCCEDUnitTest.unblockListener());
vm1.invoke(() -> DistributedNoAckRegionCCEDUnitTest.unblockListener());
vm2.invoke(() -> DistributedNoAckRegionCCEDUnitTest.unblockListener());
waitForAsyncProcessing(a0, "");
waitForAsyncProcessing(vm0Ops, "");
waitForAsyncProcessing(vm1Ops, "");
waitForAsyncProcessing(vm2Ops, "");
// if (a0failed && a1failed) {
// fail("neither member saw event conflation - check stats for " + name);
// }
Wait.pause(2000);// this test has with noack, thus we should wait before validating entries
// check consistency of the regions
Map r0Contents = (Map) vm0.invoke(() -> this.getCCRegionContents());
Map r1Contents = (Map) vm1.invoke(() -> this.getCCRegionContents());
Map r2Contents = (Map) vm2.invoke(() -> this.getCCRegionContents());
Map r3Contents = (Map) vm3.invoke(() -> this.getCCRegionContents());
for (int i = 0; i < 10; i++) {
String key = "cckey" + i;
assertEquals("region contents are not consistent", r0Contents.get(key), r1Contents.get(key));
assertEquals("region contents are not consistent", r1Contents.get(key), r2Contents.get(key));
assertEquals("region contents are not consistent", r2Contents.get(key), r3Contents.get(key));
for (int subi = 1; subi < 3; subi++) {
String subkey = key + "-" + subi;
assertEquals("region contents are not consistent", r0Contents.get(subkey),
r1Contents.get(subkey));
assertEquals("region contents are not consistent", r1Contents.get(subkey),
r2Contents.get(subkey));
assertEquals("region contents are not consistent", r2Contents.get(subkey),
r3Contents.get(subkey));
}
}
}
static void addBlockingListener() {
ListenerBlocking = true;
CCRegion.getAttributesMutator().addCacheListener(new CacheListenerAdapter() {
public void afterCreate(EntryEvent event) {
onEvent(event);
}
private void onEvent(EntryEvent event) {
boolean blocked = false;
if (event.isOriginRemote()) {
synchronized (this) {
while (ListenerBlocking) {
LogWriterUtils.getLogWriter()
.info("blocking cache operations for " + event.getDistributedMember());
blocked = true;
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
LogWriterUtils.getLogWriter().info("blocking cache listener interrupted");
return;
}
}
}
if (blocked) {
LogWriterUtils.getLogWriter()
.info("allowing cache operations for " + event.getDistributedMember());
}
}
}
@Override
public void close() {
LogWriterUtils.getLogWriter().info("closing blocking listener");
ListenerBlocking = false;
synchronized (this) {
notifyAll();
}
}
@Override
public void afterUpdate(EntryEvent event) {
onEvent(event);
}
@Override
public void afterInvalidate(EntryEvent event) {
onEvent(event);
}
@Override
public void afterDestroy(EntryEvent event) {
onEvent(event);
}
});
}
static void doManyOps() {
// do not include putAll, which requires an Ack to detect failures
doOpsLoopNoFlush(5000, false, false);
}
static void unblockListener() {
CacheListener listener = CCRegion.getCacheListener();
ListenerBlocking = false;
synchronized (listener) {
listener.notifyAll();
}
}
static void clearRegion() {
CCRegion.clear();
}
/**
* This test creates a server cache in vm0 and a peer cache in vm1. It then tests to see if GII
* transferred tombstones to vm1 like it's supposed to. A client cache is created in vm2 and the
* same sort of check is performed for register-interest.
*/
@Test
public void testGIISendsTombstones() throws Exception {
versionTestGIISendsTombstones();
}
protected void do_version_recovery_if_necessary(final VM vm0, final VM vm1, final VM vm2,
final Object[] params) {
// do nothing here
}
/**
* This tests the concurrency versioning system to ensure that event conflation happens correctly
* and that the statistic is being updated properly
*/
@Test
public void testConcurrentEvents() throws Exception {
versionTestConcurrentEvents();
}
@Test
public void testClearWithConcurrentEvents() throws Exception {
// need to figure out how to flush clear() ops for verification steps
}
@Test
public void testClearWithConcurrentEventsAsync() throws Exception {
// need to figure out how to flush clear() ops for verification steps
}
@Test
public void testClearOnNonReplicateWithConcurrentEvents() throws Exception {
// need to figure out how to flush clear() ops for verification steps
}
@Test
public void testTombstones() throws Exception {
// for (int i=0; i<1000; i++) {
// System.out.println("starting run #"+i);
versionTestTombstones();
// if (i < 999) {
// tearDown();
// setUp();
// }
// }
}
@Test
public void testOneHopKnownIssues() {
Host host = Host.getHost(0);
VM vm0 = host.getVM(0);
VM vm1 = host.getVM(1);
VM vm2 = host.getVM(2);
VM vm3 = host.getVM(3); // this VM, but treat as a remote for uniformity
// create an empty region in vm0 and replicated regions in VM 1 and 3,
// then perform concurrent ops
// on the same key while creating the region in VM2. Afterward make
// sure that all three regions are consistent
final String name = this.getUniqueName() + "-CC";
SerializableRunnable createRegion = new SerializableRunnable("Create Region") {
public void run() {
try {
final RegionFactory f;
int vmNumber = VM.getCurrentVMNum();
switch (vmNumber) {
case 0:
f = getCache().createRegionFactory(
getRegionAttributes(RegionShortcut.REPLICATE_PROXY.toString()));
break;
case 1:
f = getCache()
.createRegionFactory(getRegionAttributes(RegionShortcut.REPLICATE.toString()));
f.setDataPolicy(DataPolicy.NORMAL);
break;
default:
f = getCache().createRegionFactory(getRegionAttributes());
break;
}
CCRegion = (LocalRegion) f.create(name);
} catch (CacheException ex) {
Assert.fail("While creating region", ex);
}
}
};
vm0.invoke(createRegion); // empty
vm1.invoke(createRegion); // normal
vm2.invoke(createRegion); // replicate
// case 1: entry already invalid on vm2 (replicate) is invalidated by vm0 (empty)
final String invalidationKey = "invalidationKey";
final String destroyKey = "destroyKey";
SerializableRunnable test =
new SerializableRunnable("case 1: second invalidation not applied or distributed") {
public void run() {
CCRegion.put(invalidationKey, "initialValue");
int invalidationCount = CCRegion.getCachePerfStats().getInvalidates();
CCRegion.invalidate(invalidationKey);
CCRegion.invalidate(invalidationKey);
assertEquals(invalidationCount + 1, CCRegion.getCachePerfStats().getInvalidates());
// also test destroy() while we're at it. It should throw an exception
int destroyCount = CCRegion.getCachePerfStats().getDestroys();
CCRegion.destroy(invalidationKey);
try {
CCRegion.destroy(invalidationKey);
fail("expected an EntryNotFoundException");
} catch (EntryNotFoundException e) {
// expected
}
assertEquals(destroyCount + 1, CCRegion.getCachePerfStats().getDestroys());
}
};
vm0.invoke(test);
// now do the same with the datapolicy=normal region
test.setName("case 2: second invalidation not applied or distributed");
vm1.invoke(test);
}
/**
* This tests the concurrency versioning system to ensure that event conflation happens correctly
* and that the statistic is being updated properly
*/
@Category(FlakyTest.class) // GEODE-976: time sensitive, thread sleeps, relies on stat values
@Test
public void testConcurrentEventsOnEmptyRegion() {
versionTestConcurrentEventsOnEmptyRegion();
}
/**
* This tests the concurrency versioning system to ensure that event conflation happens correctly
* and that the statistic is being updated properly
*/
@Test
public void testConcurrentEventsOnNonReplicatedRegion() {
versionTestConcurrentEventsOnNonReplicatedRegion();
}
@Test
public void testGetAllWithVersions() {
versionTestGetAllWithVersions();
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// these methods can be uncommented to inhibit test execution
// when new tests are added
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// @Override
// public void testNonblockingGetInitialImage() throws Throwable {
// }
// @Override
// public void testConcurrentOperations() throws Exception {
// }
//
// @Override
// public void testDistributedUpdate() {
// }
//
// @Override
// public void testDistributedGet() {
// }
//
// @Override
// public void testDistributedPutNoUpdate() throws InterruptedException {
// }
//
// @Override
// public void testDefinedEntryUpdated() {
// }
//
// @Override
// public void testDistributedDestroy() throws InterruptedException {
// }
//
// @Override
// public void testDistributedRegionDestroy() throws InterruptedException {
// }
//
// @Override
// public void testLocalRegionDestroy() throws InterruptedException {
// }
//
// @Override
// public void testDistributedInvalidate() {
// }
//
// @Override
// public void testDistributedInvalidate4() throws InterruptedException {
// }
//
// @Override
// public void testDistributedRegionInvalidate() throws InterruptedException {
// }
//
// @Override
// public void testRemoteCacheListener() throws InterruptedException {
// }
//
// @Override
// public void testRemoteCacheListenerInSubregion() throws InterruptedException {
// }
//
// @Override
// public void testRemoteCacheLoader() throws InterruptedException {
// }
//
// @Override
// public void testRemoteCacheLoaderArg() throws InterruptedException {
// }
//
// @Override
// public void testRemoteCacheLoaderException() throws InterruptedException {
// }
//
// @Override
// public void testCacheLoaderWithNetSearch() throws CacheException {
// }
//
// @Override
// public void testCacheLoaderWithNetLoad() throws CacheException {
// }
//
// @Override
// public void testNoRemoteCacheLoader() throws InterruptedException {
// }
//
// @Override
// public void testNoLoaderWithInvalidEntry() {
// }
//
// @Override
// public void testRemoteCacheWriter() throws InterruptedException {
// }
//
// @Override
// public void testLocalAndRemoteCacheWriters() throws InterruptedException {
// }
//
// @Override
// public void testCacheLoaderModifyingArgument() throws InterruptedException {
// }
//
// @Override
// public void testRemoteLoaderNetSearch() throws CacheException {
// }
//
// @Override
// public void testLocalCacheLoader() {
// }
//
// @Override
// public void testDistributedPut() throws Exception {
// }
//
// @Override
// public void testReplicate() throws InterruptedException {
// }
//
// @Override
// public void testDeltaWithReplicate() throws InterruptedException {
// }
//
// @Override
// public void testGetInitialImage() {
// }
//
// @Override
// public void testLargeGetInitialImage() {
// }
//
// @Override
// public void testMirroredDataFromNonMirrored() throws InterruptedException {
// }
//
// @Override
// public void testNoMirroredDataToNonMirrored() throws InterruptedException {
// }
//
// @Override
// public void testMirroredLocalLoad() {
// }
//
// @Override
// public void testMirroredNetLoad() {
// }
//
// @Override
// public void testNoRegionKeepAlive() throws InterruptedException {
// }
//
// @Override
// public void testNetSearchObservesTtl() throws InterruptedException {
// }
//
// @Override
// public void testNetSearchObservesIdleTime() throws InterruptedException {
// }
//
// @Override
// public void testEntryTtlDestroyEvent() throws InterruptedException {
// }
//
// @Override
// public void testUpdateResetsIdleTime() throws InterruptedException {
// }
// @Override
// public void testTXNonblockingGetInitialImage() throws Throwable {
// }
//
// @Override
// public void testNBRegionInvalidationDuringGetInitialImage() throws Throwable {
// }
//
// @Override
// public void testNBRegionDestructionDuringGetInitialImage() throws Throwable {
// }
//
// @Override
// public void testNoDataSerializer() {
// }
//
// @Override
// public void testNoInstantiator() {
// }
//
// @Override
// public void testTXSimpleOps() throws Exception {
// }
//
// @Override
// public void testTXUpdateLoadNoConflict() throws Exception {
// }
//
// @Override
// public void testTXMultiRegion() throws Exception {
// }
//
// @Override
// public void testTXRmtMirror() throws Exception {
// }
}