blob: c89c4d006ceba070810c5a668c3c5009cee19be6 [file] [log] [blame]
/*=========================================================================
* Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
* This product is protected by U.S. and international copyright
* and intellectual property laws. Pivotal products are covered by
* one or more patents listed at http://www.pivotal.io/patents.
*=========================================================================
*/
package com.gemstone.gemfire.internal.cache;
import static org.junit.Assert.fail;
import java.io.File;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import com.gemstone.gemfire.cache.Scope;
import com.gemstone.gemfire.test.junit.categories.IntegrationTest;
import static org.junit.Assert.*;
/**
* This is a bugtest for bug 37500.
*
* @author Dinesh Patel
* @author Mitul Bid
*
*/
@Category(IntegrationTest.class)
public class Bug37500JUnitTest extends DiskRegionTestingBase
{
/** The disk region configuration object for the test */
private DiskRegionProperties diskProps = new DiskRegionProperties();
/** The key for entry1 */
static final String KEY1 = "KEY1";
/** The key for entry2 */
static final String KEY2 = "KEY2";
/** Boolean to indicate the roller thread to proceed */
static volatile boolean proceedForRolling = false;
/**
* Boolean to decide whether we want to allow roller to run ( used via
* CacheObserver callback
*/
static volatile boolean notifyRoller = false;
/**
* This test does the following: <br>
* 1. Create a disk-region with following configurations :
* <li>dirSize = 2000 bytes
* <li>maxOplogSize = 500 bytes
* <li>rolling = true
* <li>syncMode = true
* <li>approx size on disk for operations = 440 bytes<br>
*
* 2.Make Roller go into WAIT state via CacheObserverAdapter.beforeGoingToCompact
* callback<br>
* 3.Put 440 bytes , it will go in oplog1 <br>
* 4.Put another 440 bytes ,it will go in oplog1<br>
* 5.Put 440 bytes , switching will be caused, it will go in oplog2, Roller
* will remained blocked (step 2)<br>
* 6.Put 440 bytes , it will go in oplog2, oplog2 will now be full<br>
* 7.Notify the Roller and put 440 bytes , this will try further switching.
* The put will fail with exception due to bug 37500. The put thread takes an
* entry level lock for entry2 ( the one with KEY2) and tries to write to disk
* but there is no free space left, so it goes into wait, expecting Roller to
* free up the space. The roller, which has now been notified to run, tries to
* roll entry2 for which it seeks entry level lock which has been acquired by
* put-thread. So the put thread eventually comes out of the wait with
* DiskAccessException<br>
*
* Another scenario for this bug is, once the disk space was getting exhausted ,
* the entry operation threads which had already taken a lock on Entry got
* stuck trying to seek the Oplog Lock. The switching thread had acquired the
* Oplog.lock & was waiting for the roller thread to free disk space. Since
* the roller needed to acquire Entry lock to roll, it was unable to do so
* because of entry operation threads. This would cause the entry operation
* threads to get DiskAccessException after completing the stipulated wait.
* The Roller was able to free space only when it has rolled all the relevant
* entries which could happen only when the entry operation threads released
* the entry lock after getting DiskAccessException.
*
*
* @throws Exception
*/
@Test
public void testBug37500() throws Exception
{
final int MAX_OPLOG_SIZE = 1000;
diskProps.setMaxOplogSize(MAX_OPLOG_SIZE);
diskProps.setPersistBackup(true);
diskProps.setRolling(true);
diskProps.setSynchronous(false);
File testdir = new File("bug37500-diskDir");
testdir.mkdir();
testdir.deleteOnExit();
diskProps.setDiskDirsAndSizes(new File[] { testdir }, new int[] { 2000 });
LocalRegion.ISSUE_CALLBACKS_TO_CACHE_OBSERVER = true;
region = DiskRegionHelperFactory.getSyncPersistOnlyRegion(cache, diskProps, Scope.LOCAL);
CacheObserver old = CacheObserverHolder
.setInstance(new CacheObserverAdapter() {
public void beforeGoingToCompact()
{
if (!proceedForRolling) {
synchronized (Bug37500JUnitTest.class) {
if (!proceedForRolling) {
try {
cache.getLogger().info(
"beforeGoingToCompact :: going into wait");
Bug37500JUnitTest.class.wait();
}
catch (InterruptedException e) {
cache.getLogger().info("Roller interrupted");
fail("interrupted");
}
cache.getLogger().info(
"beforeGoingToCompact :: coming out of wait");
}
}
}
}
public void beforeSwitchingOplog()
{
if (notifyRoller) {
cache.getLogger().info(
"beforeSwitchingOplog :: going to notify Roller");
synchronized (Bug37500JUnitTest.class) {
proceedForRolling = true;
Bug37500JUnitTest.class.notify();
cache.getLogger().info(
"beforeSwitchingOplog :: notified the Roller");
}
}
}
});
cache.getLogger().info("goin to put no. 1");
// put 440 bytes , it will go in oplog1
region.put(KEY1, new byte[420]);
cache.getLogger().info("goin to put no. 2");
// put another 440 bytes ,it will go in oplog1
region.put(KEY2, new byte[420]);
cache.getLogger().info("goin to put no. 3");
// put 440 bytes , switching will be caused, it will go in oplog2 (value
// size increased to 432 as key wont be written to disk for UPDATE)
region.put(KEY1, new byte[432]);
cache.getLogger().info("goin to put no. 4");
// put 440 bytes , it will go in oplog2
region.put(KEY1, new byte[432]);
notifyRoller = true;
cache.getLogger().info("goin to put no. 5");
// put 440 bytes , this will try further switching
region.put(KEY2, new byte[432]);
LocalRegion.ISSUE_CALLBACKS_TO_CACHE_OBSERVER = false;
CacheObserverHolder.setInstance(old);
closeDown();
}
}