001: /*
002: This source file is part of Smyle, a database library.
003: For up-to-date information, see http://www.drjava.de/smyle
004: Copyright (C) 2001 Stefan Reich (doc@drjava.de)
005:
006: This library is free software; you can redistribute it and/or
007: modify it under the terms of the GNU Lesser General Public
008: License as published by the Free Software Foundation; either
009: version 2.1 of the License, or (at your option) any later version.
010:
011: This library is distributed in the hope that it will be useful,
012: but WITHOUT ANY WARRANTY; without even the implied warranty of
013: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: Lesser General Public License for more details.
015:
016: You should have received a copy of the GNU Lesser General Public
017: License along with this library; if not, write to the Free Software
018: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019:
020: For full license text, see doc/license/lgpl.txt in this distribution
021: */
022:
023: package drjava.smyle.tests;
024:
025: import java.io.*;
026: import junit.framework.*;
027: import java.util.*;
028: import drjava.util.*;
029: import drjava.gjutil.*;
030: import drjava.smyle.*;
031: import drjava.smyle.testtypes.*;
032: import drjava.smyle.core.*;
033: import drjava.smyle.meta.*;
034: import org.artsProject.mcop.core.*;
035: import org.artsProject.util.*;
036:
037: /** tests garbage collection */
038: public class GCTest extends DiskStoreTestBase {
039: public GCTest(String name) {
040: super (name);
041: }
042:
043: public void setUp() throws Exception {
044: super .setUp();
045: store.setWriteLatency(0);
046: }
047:
048: void prepareGC() throws Exception {
049: addPersons(table);
050: // The next line is for growing the chunk table
051: addMorePersons(table);
052: removeMorePersons(table);
053: snapshot.commit();
054: gcCheckpointSmall();
055:
056: getNewSnapshot();
057: addMorePersons(table);
058: snapshot.commit();
059: gcCheckpointBig();
060:
061: // remove the last 5 persons again
062: getNewSnapshot();
063: removeMorePersons(table);
064: snapshot.commit();
065: }
066:
067: /** complements prepareGC() */
068: void assertGCWorked() throws Exception {
069: // Note: By requiring an exact size match (rather than a <=) with the
070: // store size before any GC, we implicitely assert
071: // something else too: that a series of adds produces no garbage
072: // (which it did in an early version).
073: gcCheckpointSmallAgain(24 * 16, true); // allow some chunk table slack
074:
075: rotate();
076:
077: // assert table is still intact
078: assertPersons(table);
079: }
080:
081: void assertNoGC() throws Exception {
082: gcCheckpointSmallAgain(4 * 16, false); // allow some chunk table slack
083: }
084:
085: public void testGC() throws Exception {
086: prepareGC();
087:
088: // We don't take a new snapshot here; so we automatically test
089: // whether the current store state is respected by GC
090: // (even if there's no snapshot of it)
091: store.collectGarbage();
092: assertGCWorked();
093: }
094:
095: public void testGCRespectsSnapshots() throws Exception {
096: addPersons(table);
097: snapshot.commit();
098: getImmutableSnapshot();
099: Table oldTable = table;
100:
101: getNewSnapshot();
102: table.clear();
103: snapshot.commit();
104:
105: store.collectGarbage();
106: assertPersons(oldTable);
107: }
108:
109: public void testGCRespectsModifiedUncommittedSnapshots()
110: throws Exception {
111: if (store.exclusiveWriteLocking())
112: return;
113:
114: Table oldTable = table;
115: addPersons(oldTable);
116:
117: getImmutableSnapshot();
118: store.collectGarbage();
119: assertPersons(oldTable);
120: }
121:
122: void gcCheckpointSmall() {
123: bytes1 = disk.totalBytes();
124: }
125:
126: void gcCheckpointBig() {
127: bytes2 = disk.totalBytes();
128: assertTrue(bytes2 + " > " + bytes1, bytes2 > bytes1);
129: }
130:
131: void gcCheckpointSmallAgain(int increaseAllowed, boolean flag) {
132: long bytes3 = disk.totalBytes();
133: assertEquals("Store should be at most " + bytes1 + "+"
134: + increaseAllowed + " bytes large, actual size: "
135: + bytes3 + " (files: " + disk.numberOfFiles()
136: + "), max: " + bytes2, flag, bytes3 <= bytes1
137: + increaseAllowed);
138: }
139:
140: public void testGCAfterCrash() throws Exception {
141: ((DefaultChunkManager) store.getChunkManager())
142: .setNaturalFileSize(32);
143: addPersons(table);
144: snapshot.commit();
145: gcCheckpointSmall();
146:
147: getNewSnapshot();
148: addMorePersons(table);
149: gcCheckpointBig();
150:
151: // simulate a crash by not committing and keeping the old store open
152: createStore();
153: gcCheckpointSmallAgain(16 * 16, true);
154:
155: // assert table is still intact and there was a GC
156: assertPersons(table);
157: }
158:
159: public void testGCIgnoresFreedSnapshots() throws Exception {
160: testMemoryGC(true);
161: }
162:
163: public void testGCPerformsMemoryGC() throws Exception {
164: testMemoryGC(false);
165: }
166:
167: void testMemoryGC(boolean callMemoryGC) throws Exception {
168: //if (store.exclusiveWriteLocking()) return;
169:
170: ((DefaultChunkManager) store.getChunkManager())
171: .setNaturalFileSize(32);
172: commitAndGetNewSnapshot();
173: gcCheckpointSmall();
174:
175: addPersons(table);
176: gcCheckpointBig();
177:
178: // acquire immutable snapshot
179: Snapshot oldSnapshot = store.snapshot();
180:
181: removePersons(table);
182: snapshot.commit();
183: snapshot = null;
184: // table is now small again; but old snapshot still references
185: // obsolete data
186:
187: oldSnapshot = null; // drop reference to old snapshot
188: if (callMemoryGC)
189: System.gc();
190: store.collectGarbage();
191: gcCheckpointSmallAgain(8 * 16, true);
192: }
193:
194: public void testGCIgnoresForgottenSnapshots() throws Exception {
195: ((DefaultChunkManager) store.getChunkManager())
196: .setNaturalFileSize(32);
197: commitAndGetNewSnapshot();
198: gcCheckpointSmall();
199:
200: addPersons(table);
201: gcCheckpointBig();
202:
203: snapshot.forget();
204: getImmutableSnapshot();
205:
206: store.collectGarbage();
207: gcCheckpointSmallAgain(7 * 16, true);
208: }
209:
210: public void testGCOnClose() throws Exception {
211: ((DefaultChunkManager) store.getChunkManager())
212: .setNaturalFileSize(32);
213: // The next line is for growing the chunk table
214: addMorePersons(table);
215: removeMorePersons(table);
216: commitAndGetNewSnapshot();
217: gcCheckpointSmall();
218:
219: addPersons(table);
220: commitAndGetNewSnapshot();
221: Snapshot bigSnapshot = store.snapshot(); // retain reference, don't forget()
222: gcCheckpointBig();
223:
224: table.clear();
225: commitAndGetNewSnapshot();
226:
227: store.close();
228: gcCheckpointSmallAgain(4 * 16, true); // allow some chunk table slack
229:
230: store = null;
231: }
232:
233: public void testGCReusedChunks() throws Exception {
234: table.add(john);
235: snapshot.commit();
236: getImmutableSnapshot();
237: Table oldTable = table;
238:
239: getNewSnapshot();
240: table.clear();
241: table.add(anne);
242: snapshot.commit();
243:
244: store.collectGarbage();
245: assertEquals(john, oldTable.first());
246: assertEquals(anne, table.first());
247: }
248:
249: public void testGCAcquiresWriteLock() throws Exception {
250: if (!store.exclusiveWriteLocking())
251: return;
252:
253: try {
254: store.logTo(null);
255: store.collectGarbage();
256: fail("No exception");
257: } catch (MultipleMutableSnapshotsException e) {
258: // ok
259: }
260: }
261:
262: public void testAutoGC() throws Exception {
263: store.setWriteLatency(0);
264: store.setGCFrequency(1001);
265: store.setClusterSize(1000);
266: prepareGC();
267: //assertTrue("Total bytes written: "+disk.totalBytesWritten(), disk.totalBytesWritten() >= 1000);
268: assertGCWorked();
269:
270: // assert it works twice too
271:
272: // set parameters again because prepareGC rotated store
273: store.setWriteLatency(0);
274: store.setGCFrequency(1001);
275: store.setClusterSize(1000);
276: prepareGC();
277: //assertTrue("Total bytes written: "+disk.totalBytesWritten(), disk.totalBytesWritten() >= 1000*2);
278:
279: // assertGCWorked won't do here because tables has 10 elements, not 5
280: gcCheckpointSmallAgain(4 * 16, true);
281: }
282:
283: public void testNoAutoGC() throws Exception {
284: store.setGCFrequency(10000);
285: store.setClusterSize(1000);
286: prepareGC();
287: assertTrue("Total bytes written: " + disk.totalBytesWritten(),
288: disk.totalBytesWritten() < 10000);
289: assertNoGC();
290: }
291: }
|