30
33
import java.io.IOException;
31
34
import java.io.ObjectOutputStream;
32
35
import java.io.RandomAccessFile;
36
import java.lang.reflect.Field;
33
37
import java.util.ArrayList;
34
38
import java.util.Date;
35
39
import java.util.List;
36
40
import java.util.Random;
38
import org.slf4j.Logger;
39
import org.slf4j.LoggerFactory;
41
import java.util.concurrent.Callable;
41
43
import net.sf.ehcache.config.CacheConfiguration;
44
import net.sf.ehcache.config.Configuration;
45
import net.sf.ehcache.config.ConfigurationFactory;
42
46
import net.sf.ehcache.config.DiskStoreConfiguration;
47
import net.sf.ehcache.config.MemoryUnit;
48
import net.sf.ehcache.store.FrontEndCacheTier;
43
49
import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
44
50
import net.sf.ehcache.store.Primitive;
45
51
import net.sf.ehcache.store.Store;
46
import net.sf.ehcache.store.compound.CompoundStore;
47
import net.sf.ehcache.store.compound.impl.DiskPersistentStore;
48
import net.sf.ehcache.store.compound.impl.OverflowToDiskStore;
52
import net.sf.ehcache.store.disk.DiskStore;
53
import net.sf.ehcache.util.PropertyUtil;
54
import net.sf.ehcache.util.RetryAssert;
56
import org.hamcrest.core.Is;
50
57
import org.junit.After;
51
import org.junit.Ignore;
58
import org.junit.BeforeClass;
52
59
import org.junit.Test;
54
import static java.util.concurrent.TimeUnit.MILLISECONDS;
55
import static java.util.concurrent.TimeUnit.SECONDS;
60
import org.slf4j.Logger;
61
import org.slf4j.LoggerFactory;
58
64
* Test cases for the DiskStore.
60
66
* @author <a href="mailto:amurdoch@thoughtworks.com">Adam Murdoch</a>
61
67
* @author <a href="mailto:gluck@thoughtworks.com">Greg Luck</a>
62
* @version $Id: DiskStoreTest.java 2268 2010-04-16 19:50:30Z cdennis $
68
* @version $Id: DiskStoreTest.java 4828 2011-10-10 18:42:59Z cdennis $
64
70
* total time 149 old i/o
65
71
* total time 133, 131, 130 nio
92
103
* size-related characteristics without elements being deleted under us.
94
105
private Store createNonExpiringDiskStore() {
95
Cache cache = new Cache("test/NonPersistent", 1, true, true, 2, 1, false, 1);
106
Cache cache = new Cache("test/NonPersistent", 1, true, false, 2, 1, false, 1);
96
107
manager.addCache(cache);
97
108
return cache.getStore();
111
private Cache createDiskCache() {
112
Cache cache = new Cache(new CacheConfiguration().name("test/NonPersistent").maxEntriesLocalHeap(1).overflowToDisk(true)
113
.eternal(false).timeToLiveSeconds(2).timeToIdleSeconds(1).diskPersistent(false).diskExpiryThreadIntervalSeconds(1)
114
.diskSpoolBufferSizeMB(10));
115
manager.addCache(cache);
100
119
private Store createDiskStore() {
101
Cache cache = new Cache("test/NonPersistent", 1, true, false, 2, 1, false, 1);
102
manager.addCache(cache);
103
return cache.getStore();
120
return createDiskCache().getStore();
106
123
private Store createPersistentDiskStore(String cacheName) {
107
Cache cache = new Cache(cacheName, 10000, true, true, 5, 1, true, 600);
124
Cache cache = new Cache(cacheName, 10000, true, false, 5, 1, true, 600);
108
125
manager.addCache(cache);
109
126
return cache.getStore();
112
129
private Store createAutoPersistentDiskStore(String cacheName) {
113
Cache cache = new Cache(cacheName, 10000, true, true, 5, 1, true, 600);
114
manager2 = new CacheManager();
130
Cache cache = new Cache(cacheName, 10000, true, false, 5, 1, true, 600);
131
Configuration config = ConfigurationFactory.parseConfiguration().name("cm2");
132
manager2 = new CacheManager(config);
115
133
//manager.setDiskStorePath(System.getProperty("java.io.tmpdir") + File.separator + DiskStore.generateUniqueDirectory());
116
134
manager2.addCache(cache);
117
135
return cache.getStore();
125
143
private Store createCapacityLimitedDiskStore() {
126
144
Cache cache = new Cache("test/CapacityLimited", 1, MemoryStoreEvictionPolicy.LRU, true, null, true,
127
0, 0, false, 600, null, null, 50);
145
0, 0, false, 600, null, null, 50);
128
146
manager.addCache(cache);
129
147
return cache.getStore();
132
private Store createStripedDiskStore(int stripes) {
133
CacheConfiguration config = new CacheConfiguration("test/NonPersistentStriped_" + stripes, 10000).overflowToDisk(true).eternal(false)
134
.timeToLiveSeconds(2).timeToIdleSeconds(1).diskPersistent(false).diskExpiryThreadIntervalSeconds(1).diskAccessStripes(stripes);
150
private Cache createStripedDiskCache(int stripes) {
151
CacheConfiguration config = new CacheConfiguration("test/NonPersistentStriped_" + stripes, 10000).overflowToDisk(true)
152
.eternal(false).timeToLiveSeconds(2).timeToIdleSeconds(1).diskPersistent(false).diskExpiryThreadIntervalSeconds(1)
153
.diskAccessStripes(stripes).diskSpoolBufferSizeMB(10);
135
154
Cache cache = new Cache(config);
136
155
manager.addCache(cache);
137
return cache.getStore();
141
160
* Test to help debug DiskStore test
191
211
* Tests that the Disk Store can be changed
194
public void testSetDiskStorePath() throws IOException, InterruptedException {
195
Cache cache = new Cache("testChangePath", 10000, true, true, 5, 1, true, 600);
196
manager2 = new CacheManager();
214
public void testSetDiskStorePath() throws Exception {
215
Configuration config = ConfigurationFactory.parseConfiguration().name("cm2");
216
Cache cache = new Cache("testChangePath", 10000, true, false, 5, 1, true, 600);
217
manager2 = new CacheManager(config);
197
218
cache.setDiskStorePath(System.getProperty("java.io.tmpdir") + File.separator + "changedDiskStorePath");
198
219
manager2.addCache(cache);
199
DiskPersistentStore diskStore = (DiskPersistentStore) cache.getStore();
220
DiskStore diskStore = getDiskStore(cache.getStore());
200
221
File dataFile = diskStore.getDataFile();
201
222
assertTrue("File exists", dataFile.exists());
265
289
public void testPersistentNonOverflowToDiskStoreFromCacheManager() throws IOException, InterruptedException, CacheException {
266
290
//initialise with an instance CacheManager so that the following line actually does something
267
CacheManager manager = new CacheManager(AbstractCacheTest.TEST_CONFIG_DIR + "ehcache-disk.xml");
268
Ehcache cache = manager.getCache("persistentLongExpiryIntervalNonOverflowCache");
270
for (int i = 0; i < 100; i++) {
271
byte[] data = new byte[1024];
272
cache.put(new Element("key" + (i + 100), data));
274
assertEquals(100, cache.getSize());
278
manager = new CacheManager(AbstractCacheTest.TEST_CONFIG_DIR + "ehcache-disk.xml");
279
cache = manager.getCache("persistentLongExpiryIntervalNonOverflowCache");
281
//Now check that the DiskStore is involved in Cache methods it needs to be involved in.
282
assertEquals(100, cache.getSize());
283
assertEquals(100, cache.getDiskStoreSize());
284
assertEquals(100, cache.getKeysNoDuplicateCheck().size());
285
assertEquals(100, cache.getKeys().size());
286
assertEquals(100, cache.getKeysWithExpiryCheck().size());
288
//now check some of the Cache methods work
289
assertNotNull(cache.get("key100"));
290
assertNotNull(cache.getQuiet("key100"));
291
cache.remove("key100");
292
assertNull(cache.get("key100"));
293
assertNull(cache.getQuiet("key100"));
295
assertEquals(0, cache.getSize());
296
assertEquals(0, cache.getDiskStoreSize());
292
Configuration config = ConfigurationFactory.parseConfiguration(new File(AbstractCacheTest.TEST_CONFIG_DIR + "ehcache-disk.xml"))
294
CacheManager manager = new CacheManager(config);
295
final Ehcache cache = manager.getCache("persistentLongExpiryIntervalNonOverflowCache");
297
for (int i = 0; i < 100; i++) {
298
byte[] data = new byte[1024];
299
cache.put(new Element("key" + (i + 100), data));
301
assertEquals(100, cache.getSize());
307
Configuration config = ConfigurationFactory.parseConfiguration(new File(AbstractCacheTest.TEST_CONFIG_DIR + "ehcache-disk.xml"))
309
CacheManager manager = new CacheManager(config);
310
final Ehcache cache = manager.getCache("persistentLongExpiryIntervalNonOverflowCache");
312
//Now check that the DiskStore is involved in Cache methods it needs to be involved in.
313
RetryAssert.assertBy(500, MILLISECONDS, new Callable<Integer>() {
314
public Integer call() throws Exception {
315
return cache.getSize();
319
assertEquals(100, cache.getDiskStoreSize());
320
assertEquals(100, cache.getKeysNoDuplicateCheck().size());
321
assertEquals(100, cache.getKeys().size());
322
assertEquals(100, cache.getKeysWithExpiryCheck().size());
324
//now check some of the Cache methods work
325
assertNotNull(cache.get("key100"));
326
assertNotNull(cache.getQuiet("key100"));
327
cache.remove("key100");
328
assertNull(cache.get("key100"));
329
assertNull(cache.getQuiet("key100"));
331
assertEquals(0, cache.getSize());
332
assertEquals(0, cache.getDiskStoreSize());
302
339
* Tests that we can save and load a persistent store in a repeatable way
305
public void testLoadPersistentStore() throws IOException, InterruptedException {
342
public void testLoadPersistentStore() throws Exception {
307
344
String cacheName = "testLoadPersistent";
308
345
Store store = createPersistentDiskStore(cacheName);
428
465
byte[] data = new byte[1024];
429
466
diskStore.put(new Element("key" + (i + 100), data));
432
assertEquals(ELEMENT_ON_DISK_SIZE * 100, diskStore.getOnDiskSizeInBytes());
433
468
assertEquals(100, diskStore.getSize());
434
469
manager.removeCache(cacheName);
436
File indexFile = ((DiskPersistentStore) diskStore).getIndexFile();
471
File dataFile = diskStore.getDataFile();
472
assertTrue(dataFile.length() >= 100 * ELEMENT_ON_DISK_SIZE);
474
File indexFile = diskStore.getIndexFile();
437
475
FileOutputStream fout = new FileOutputStream(indexFile);
438
476
//corrupt the index file
439
477
fout.write(new byte[]{'q', 'w', 'e', 'r', 't', 'y'});
441
diskStore = createPersistentDiskStore(cacheName);
442
File dataFile = ((DiskPersistentStore) diskStore).getDataFile();
480
diskStore = getDiskStore(createPersistentDiskStore(cacheName));
443
481
assertTrue("File exists", dataFile.exists());
445
483
//Make sure the data file got recreated since the index was corrupt
716
749
assertNull(diskStore.get("key2"));
753
public void testPersistentChangingPoolSizeBetweenRestarts() throws Exception {
754
String diskStorePath = System.getProperty("java.io.tmpdir") + File.separatorChar + "testPersistentChangingPoolSizeBetweenRestarts";
755
manager = new CacheManager(
757
.diskStore(new DiskStoreConfiguration().path(diskStorePath)).name("cm2")
761
manager.addCache(new Cache(
762
new CacheConfiguration("persistentCache", 0)
763
.overflowToDisk(true)
764
.diskPersistent(true)
767
Cache cache = manager.getCache("persistentCache");
769
for (int i = 0; i < 500; i++) {
770
cache.put(new Element(i, new byte[1024]));
773
assertEquals(500, cache.getSize());
777
manager = new CacheManager(
779
.diskStore(new DiskStoreConfiguration().path(diskStorePath)
783
manager.addCache(new Cache(
784
new CacheConfiguration("persistentCache", 0)
785
.overflowToDisk(true)
786
.diskPersistent(true)
787
.maxBytesLocalDisk(100, MemoryUnit.KILOBYTES)
790
cache = manager.getCache("persistentCache");
792
assertTrue(cache.getSize() <= 100);
720
799
* Tests removing an entry, after it has been written
723
802
public void testRemoveSlow() throws Exception {
724
final CompoundStore diskStore = (CompoundStore) createDiskStore();
803
final DiskStore diskStore = getDiskStore(createDiskStore());
727
806
final String value = "value";
1010
1075
public void testReadRemoveMultipleThreadsMultipleStripes() throws Exception {
1011
1076
for (int stripes = 0; stripes < 10; stripes++) {
1012
1077
final Random random = new Random();
1013
final Store diskStore = createStripedDiskStore(stripes);
1015
diskStore.put(new Element("key", "value"));
1017
// Run a set of threads that get, put and remove an entry
1018
final List executables = new ArrayList();
1019
for (int i = 0; i < 5; i++) {
1020
final Executable executable = new Executable() {
1021
public void execute() throws Exception {
1022
for (int i = 0; i < 100; i++) {
1023
diskStore.put(new Element("key" + random.nextInt(100), "value"));
1027
executables.add(executable);
1029
for (int i = 0; i < 5; i++) {
1030
final Executable executable = new Executable() {
1031
public void execute() throws Exception {
1032
for (int i = 0; i < 100; i++) {
1033
diskStore.remove("key" + random.nextInt(100));
1037
executables.add(executable);
1040
runThreads(executables);
1078
final Cache cache = createStripedDiskCache(stripes);
1080
cache.put(new Element("key", "value"));
1082
// Run a set of threads that get, put and remove an entry
1083
final List executables = new ArrayList();
1084
for (int i = 0; i < 5; i++) {
1085
final Executable executable = new Executable() {
1086
public void execute() throws Exception {
1087
for (int i = 0; i < 100; i++) {
1088
cache.put(new Element("key" + random.nextInt(100), "value"));
1092
executables.add(executable);
1094
for (int i = 0; i < 5; i++) {
1095
final Executable executable = new Executable() {
1096
public void execute() throws Exception {
1097
for (int i = 0; i < 100; i++) {
1098
cache.remove("key" + random.nextInt(100));
1102
executables.add(executable);
1105
runThreads(executables);
1107
manager.removeCache(cache.getName());
1045
1113
* Tests how data is written to a random access file.
1069
1137
return new RandomAccessFile(dataFile, "rw");
1073
* Test overflow to disk = true, using 100000 records.
1074
* 15 seconds v1.38 DiskStore
1075
* 2 seconds v1.42 DiskStore
1076
* Adjusted for change to laptop
1079
public void testOverflowToDiskWithLargeNumberofCacheEntries() throws Exception {
1081
//Set size so the second element overflows to disk.
1082
//Cache cache = new Cache("test", 1000, MemoryStoreEvictionPolicy.LRU, true, null, true, 500, 500, false, 1, null);
1083
Cache cache = new Cache(new CacheConfiguration("test", 1000)
1084
.memoryStoreEvictionPolicy("LRU")
1086
.overflowToDisk(true)
1087
.timeToLiveSeconds(1)
1088
.diskAccessStripes(1)
1089
.diskExpiryThreadIntervalSeconds(60));
1090
manager.addCache(cache);
1092
StopWatch stopWatch = new StopWatch();
1093
for (; i < 100000; i++) {
1094
cache.put(new Element("" + i,
1095
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
1096
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
1097
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
1098
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
1099
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"));
1101
long time = stopWatch.getElapsedTime();
1102
LOG.info("time: " + time);
1103
assertTrue(4 < time);
1108
1142
* This test is designed to be used with a profiler to explore the ways in which DiskStore
1130
* Test overflow to disk = true, using 100000 records.
1131
* 35 seconds v1.38 DiskStore
1132
* 26 seconds v1.42 DiskStore
1135
public void testOverflowToDiskWithLargeNumberofCacheEntriesAndGets() throws Exception {
1137
//Set size so the second element overflows to disk.
1138
//Cache cache = new Cache("test", 1000, MemoryStoreEvictionPolicy.LRU, true, null, true, 500, 500, false, 60, null);
1139
Cache cache = new Cache(new CacheConfiguration("test", 1000)
1140
.memoryStoreEvictionPolicy("LRU")
1142
.overflowToDisk(true)
1143
.timeToLiveSeconds(1)
1144
.diskAccessStripes(5)
1145
.diskExpiryThreadIntervalSeconds(60));
1146
manager.addCache(cache);
1147
Random random = new Random();
1148
StopWatch stopWatch = new StopWatch();
1149
for (int i = 0; i < 100000; i++) {
1150
cache.put(new Element("" + i,
1151
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
1152
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
1153
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
1154
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
1155
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"));
1157
cache.get("" + random.nextInt(100000));
1161
long elapsed = stopWatch.getElapsedTime();
1162
LOG.info("Elapsed time: " + elapsed / 1000);
1164
assertEquals(100000, cache.getSize());
1165
assertTrue(23 < elapsed);
1166
//Some entries may be in the Memory Store and Disk Store. cache.getSize removes dupes. a look at the
1167
//disk store size directly does not.
1168
assertTrue(99000 <= cache.getDiskStoreSize());
1172
* Runs out of memory at 5,099,999 elements with the standard 64MB VM size on 32 bit architectures.
1173
* Around 3,099,999 for AMD64. Why? See abstract citation below from
1174
* http://www3.interscience.wiley.com/cgi-bin/abstract/111082816/ABSTRACT?CRETRY=1&SRETRY=0
1176
* By running the PowerPC machine in both 32-bit and 64-bit mode we are able to compare 32-bit and 64-bit VMs.
1177
* We conclude that the space an object takes in the heap in 64-bit mode is 39.3% larger on average than in
1178
* 32-bit mode. We identify three reasons for this: (i) the larger pointer size, (ii) the increased header
1179
* and (iii) the increased alignment. The minimally required heap size is 51.1% larger on average in 64-bit
1180
* than in 32-bit mode. From our experimental setup using hardware performance monitors, we observe that 64-bit
1181
* computing typically results in a significantly larger number of data cache misses at all levels of the memory
1182
* hierarchy. In addition, we observe that when a sufficiently large heap is available, the IBM JDK 1.4.0 VM is
1183
* 1.7% slower on average in 64-bit mode than in 32-bit mode. Copyright © 2005 John Wiley & Sons, Ltd.
1185
* The reason that it is not infinite is because of a small amount of memory used (about 12 bytes) used for
1186
* the disk store index in this case.
1192
public void testMaximumCacheEntriesIn64MBWithOverflowToDisk() throws Exception {
1194
Cache cache = new Cache("test", 1000, MemoryStoreEvictionPolicy.LRU, true, null, true, 500, 500, false, 1, null);
1195
manager.addCache(cache);
1196
StopWatch stopWatch = new StopWatch();
1199
Integer index = null;
1201
for (; i < 100; i++) {
1202
for (j = 0; j < 100000; j++) {
1203
index = Integer.valueOf(((1000000 * i) + j));
1204
cache.put(new Element(index,
1205
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
1206
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
1207
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
1208
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
1209
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"));
1211
//wait to write entries
1212
int size = cache.getSize();
1215
long elapsed = stopWatch.getElapsedTime();
1216
LOG.info("Elapsed time: " + elapsed / 1000);
1218
} catch (OutOfMemoryError e) {
1219
LOG.info("All heap consumed after " + index + " entries created.");
1220
int expectedMax = 3090000;
1221
assertTrue("Achieved " + index.intValue() + " which was less than the expected value of " + expectedMax,
1222
index.intValue() >= expectedMax);
1227
* Perf test used by Venkat Subramani
1228
* Get took 119s with Cache svn21
1230
* The change was to stop adding DiskStore retrievals into the MemoryStore. This made sense when the only
1231
* policy was LRU. In the new version an Element, once evicted from the MemoryStore, stays in the DiskStore
1232
* until expiry or removal. This avoids a lot of serialization overhead.
1235
* 235 with get. 91 for 1.2.3. 169 with remove.
1242
public void testLargePutGetPerformanceWithOverflowToDisk() throws Exception {
1244
Cache cache = new Cache("test", 1000, MemoryStoreEvictionPolicy.LRU, true, null, true, 500, 500, false, 10000, null);
1245
manager.addCache(cache);
1246
StopWatch stopWatch = new StopWatch();
1250
for (; i < 5; i++) {
1251
for (j = 0; j < 100000; j++) {
1252
index = Integer.valueOf(((1000000 * i) + j));
1253
cache.put(new Element(index,
1254
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
1255
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
1256
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
1257
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
1258
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"));
1261
long elapsed = stopWatch.getElapsedTime();
1262
long putTime = ((elapsed / 1000) - 10);
1263
LOG.info("Put Elapsed time: " + putTime);
1264
assertTrue(putTime < 20);
1266
//wait for Disk Store to finish spooling
1267
while (cache.getStore().bufferFull()) {
1270
Random random = new Random();
1271
StopWatch getStopWatch = new StopWatch();
1272
long getStart = stopWatch.getElapsedTime();
1274
for (int k = 0; k < 1000000; k++) {
1275
Integer key = Integer.valueOf(random.nextInt(500000));
1279
long getElapsedTime = getStopWatch.getElapsedTime();
1280
int time = (int) ((getElapsedTime - getStart) / 1000);
1281
LOG.info("Get Elapsed time: " + time);
1283
assertTrue(time < 180);
1289
1164
* Java is not consistent with trailing file separators, believe it or not!
1290
1165
* http://www.rationalpi.com/blog/replyToComment.action?entry=1146628709626&comment=1155660875090
1291
1166
* Can we fix c:\temp\\greg?