/*
* 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 org.junit.experimental.categories.Category;
import org.junit.Test;
import static org.junit.Assert.*;
import org.apache.geode.test.dunit.cache.internal.JUnit4CacheTestCase;
import org.apache.geode.test.dunit.internal.JUnit4DistributedTestCase;
import org.apache.geode.test.junit.categories.DistributedTest;
import org.apache.geode.SystemFailure;
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.ExpirationAction;
import org.apache.geode.cache.ExpirationAttributes;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionEvent;
import org.apache.geode.cache.Scope;
import org.apache.geode.cache.util.CacheListenerAdapter;
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.ThreadUtils;
import org.apache.geode.test.dunit.VM;
import org.apache.geode.test.dunit.Wait;
import org.apache.geode.test.dunit.WaitCriterion;
/**
* Make sure entry expiration does not happen during gii for bug 35214
*
* @since GemFire 5.0
*/
@Category(DistributedTest.class)
public class Bug35214DUnitTest extends JUnit4CacheTestCase {
protected volatile int expirationCount = 0;
private final static int ENTRY_COUNT = 100;
protected static volatile boolean callbackFailure;
public Bug35214DUnitTest() {
super();
}
private VM getOtherVm() {
Host host = Host.getHost(0);
return host.getVM(0);
}
private void initOtherVm() {
VM vm = getOtherVm();
vm.invoke(new CacheSerializableRunnable("init") {
public void run2() throws CacheException {
getCache();
AttributesFactory af = new AttributesFactory();
af.setScope(Scope.DISTRIBUTED_ACK);
Region r1 = createRootRegion("r1", af.create());
for (int i = 1; i <= ENTRY_COUNT; i++) {
r1.put("key" + i, "value" + i);
}
}
});
}
private AsyncInvocation updateOtherVm() throws Throwable {
VM vm = getOtherVm();
AsyncInvocation otherUpdater = vm.invokeAsync(new CacheSerializableRunnable("update") {
public void run2() throws CacheException {
Region r1 = getRootRegion("r1");
// let the main guys gii get started; we want to do updates
// during his gii
{
// wait for profile of getInitialImage cache to show up
org.apache.geode.internal.cache.CacheDistributionAdvisor adv =
((org.apache.geode.internal.cache.DistributedRegion) r1)
.getCacheDistributionAdvisor();
int numProfiles;
int expectedProfiles = 1;
for (;;) {
numProfiles = adv.adviseInitialImage(null).getReplicates().size();
if (numProfiles < expectedProfiles) {
// getLogWriter().info("PROFILE CHECK: Found " + numProfiles +
// " getInitialImage Profiles (waiting for " + expectedProfiles + ")");
// pause(5);
} else {
LogWriterUtils.getLogWriter()
.info("PROFILE CHECK: Found " + numProfiles + " getInitialImage Profiles (OK)");
break;
}
}
}
// start doing updates of the keys to see if we can get deadlocked
int updateCount = 1;
do {
for (int i = 1; i <= ENTRY_COUNT; i++) {
String key = "key" + i;
if (r1.containsKey(key)) {
r1.destroy(key);
} else {
r1.put(key, "value" + i + "uc" + updateCount);
}
}
} while (updateCount++ < 20);
// do one more loop with no destroys
for (int i = 1; i <= ENTRY_COUNT; i++) {
String key = "key" + i;
if (!r1.containsKey(key)) {
r1.put(key, "value" + i + "uc" + updateCount);
}
}
}
});
// FIXME this thread does not terminate
// DistributedTestCase.join(otherUpdater, 5 * 60 * 1000, getLogWriter());
// if(otherUpdater.exceptionOccurred()){
// fail("otherUpdater failed", otherUpdater.getException());
// }
return otherUpdater;
}
////////////////////// Test Methods //////////////////////
protected boolean afterRegionCreateSeen = false;
protected static void callbackAssertTrue(String msg, boolean cond) {
if (cond)
return;
callbackFailure = true;
// Throws ignored error, but...
assertTrue(msg, cond);
}
/**
* make sure entries do not expire during a GII
*/
@Test
public void testNoEntryExpireDuringGII() throws Exception {
initOtherVm();
AsyncInvocation updater = null;
try {
updater = updateOtherVm();
} catch (VirtualMachineError e) {
SystemFailure.initiateFailure(e);
throw e;
} catch (Throwable e1) {
Assert.fail("failed due to " + e1, e1);
}
System.setProperty(LocalRegion.EXPIRY_MS_PROPERTY, "true");
org.apache.geode.internal.cache.InitialImageOperation.slowImageProcessing = 30;
callbackFailure = false;
try {
AttributesFactory af = new AttributesFactory();
af.setDataPolicy(DataPolicy.REPLICATE);
af.setScope(Scope.DISTRIBUTED_ACK);
af.setStatisticsEnabled(true);
af.setEntryIdleTimeout(new ExpirationAttributes(1, ExpirationAction.INVALIDATE));
CacheListener cl1 = new CacheListenerAdapter() {
public void afterRegionCreate(RegionEvent re) {
afterRegionCreateSeen = true;
}
public void afterInvalidate(EntryEvent e) {
callbackAssertTrue("afterregionCreate not seen", afterRegionCreateSeen);
// make sure region is initialized
callbackAssertTrue("not initialized", ((LocalRegion) e.getRegion()).isInitialized());
expirationCount++;
org.apache.geode.internal.cache.InitialImageOperation.slowImageProcessing = 0;
}
};
af.addCacheListener(cl1);
final Region r1 = createRootRegion("r1", af.create());
ThreadUtils.join(updater, 60 * 1000);
WaitCriterion ev = new WaitCriterion() {
public boolean done() {
return r1.values().size() == 0;
}
public String description() {
return "region never became empty";
}
};
Wait.waitForCriterion(ev, 2 * 1000, 200, true);
{
assertEquals(0, r1.values().size());
assertEquals(ENTRY_COUNT, r1.keys().size());
}
} finally {
org.apache.geode.internal.cache.InitialImageOperation.slowImageProcessing = 0;
System.getProperties().remove(LocalRegion.EXPIRY_MS_PROPERTY);
assertEquals(null, System.getProperty(LocalRegion.EXPIRY_MS_PROPERTY));
}
assertFalse("Errors in callbacks; check logs for details", callbackFailure);
}
}