001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
005: *
006: * $Id: TxnMemoryTest.java,v 1.10.2.3 2008/01/07 15:14:34 cwl Exp $
007: */
008:
009: package com.sleepycat.je.txn;
010:
011: import java.io.File;
012: import java.io.IOException;
013: import java.util.Enumeration;
014:
015: import junit.framework.Test;
016: import junit.framework.TestCase;
017: import junit.framework.TestSuite;
018:
019: import com.sleepycat.bind.tuple.IntegerBinding;
020: import com.sleepycat.je.Cursor;
021: import com.sleepycat.je.Database;
022: import com.sleepycat.je.DatabaseConfig;
023: import com.sleepycat.je.DatabaseEntry;
024: import com.sleepycat.je.DatabaseException;
025: import com.sleepycat.je.DbInternal;
026: import com.sleepycat.je.Environment;
027: import com.sleepycat.je.EnvironmentConfig;
028: import com.sleepycat.je.OperationStatus;
029: import com.sleepycat.je.Transaction;
030: import com.sleepycat.je.config.EnvironmentParams;
031: import com.sleepycat.je.dbi.EnvironmentImpl;
032: import com.sleepycat.je.dbi.MemoryBudget;
033: import com.sleepycat.je.log.FileManager;
034: import com.sleepycat.je.tree.IN;
035: import com.sleepycat.je.tree.LN;
036: import com.sleepycat.je.txn.Txn;
037: import com.sleepycat.je.util.TestUtils;
038:
039: public class TxnMemoryTest extends TestCase {
040: private static final boolean DEBUG = false;
041: private static final String DB_NAME = "foo";
042:
043: private static final String LOCK_AUTOTXN = "lock-autotxn";
044: private static final String LOCK_USERTXN = "lock-usertxn";
045: private static final String LOCK_NOTXN = "lock-notxn";
046: private static final String[] LOCK_MODE = { LOCK_AUTOTXN,
047: LOCK_USERTXN, LOCK_NOTXN };
048: private static final String COMMIT = "commit";
049: private static final String ABORT = "abort";
050: private static final String[] END_MODE = { COMMIT, ABORT };
051:
052: private File envHome;
053: private Environment env;
054: private EnvironmentImpl envImpl;
055: private MemoryBudget mb;
056: private Database db;
057: private DatabaseEntry keyEntry = new DatabaseEntry();
058: private DatabaseEntry dataEntry = new DatabaseEntry();
059: private String lockMode;
060: private String endMode;
061:
062: private long beforeAction;
063: private long afterTxnsCreated;
064: private long afterAction;
065: private Transaction[] txns;
066:
067: private int numTxns = 2;
068: private int numRecordsPerTxn = 30;
069:
070: public static Test suite() {
071: TestSuite allTests = new TestSuite();
072: for (int i = 0; i < LOCK_MODE.length; i += 1) {
073: for (int eMode = 0; eMode < END_MODE.length; eMode++) {
074: TestSuite suite = new TestSuite(TxnMemoryTest.class);
075: Enumeration e = suite.tests();
076: while (e.hasMoreElements()) {
077: TxnMemoryTest test = (TxnMemoryTest) e
078: .nextElement();
079: test.init(LOCK_MODE[i], END_MODE[eMode]);
080: allTests.addTest(test);
081: }
082: }
083: }
084: return allTests;
085: }
086:
087: public TxnMemoryTest() {
088: envHome = new File(System.getProperty(TestUtils.DEST_DIR));
089: }
090:
091: private void init(String lockMode, String endMode) {
092: this .lockMode = lockMode;
093: this .endMode = endMode;
094: }
095:
096: public void setUp() throws IOException, DatabaseException {
097:
098: IN.ACCUMULATED_LIMIT = 0;
099: Txn.ACCUMULATED_LIMIT = 0;
100:
101: TestUtils.removeLogFiles("Setup", envHome, false);
102: TestUtils.removeFiles("Setup", envHome, FileManager.DEL_SUFFIX);
103: }
104:
105: public void tearDown() throws IOException, DatabaseException {
106:
107: /* Set test name for reporting; cannot be done in the ctor or setUp. */
108: setName(lockMode + '/' + endMode + ":" + getName());
109:
110: try {
111: if (env != null) {
112: env.close();
113: }
114: } catch (Throwable e) {
115: System.out.println("tearDown: " + e);
116: }
117:
118: try {
119: TestUtils.removeLogFiles("tearDown", envHome, true);
120: TestUtils.removeFiles("tearDown", envHome,
121: FileManager.DEL_SUFFIX);
122: } catch (Throwable e) {
123: System.out.println("tearDown: " + e);
124: }
125:
126: db = null;
127: env = null;
128: }
129:
130: /**
131: * Opens the environment and database.
132: */
133: private void openEnv() throws DatabaseException {
134:
135: EnvironmentConfig config = TestUtils.initEnvConfig();
136:
137: /*
138: * ReadCommitted isolation is not allowed by this test because we
139: * expect no locks/memory to be freed when using a transaction.
140: */
141: DbInternal.setTxnReadCommitted(config, false);
142:
143: /* Cleaner detail tracking adds to the memory budget; disable it. */
144: config.setConfigParam(EnvironmentParams.CLEANER_TRACK_DETAIL
145: .getName(), "false");
146:
147: config.setTransactional(true);
148: config.setAllowCreate(true);
149: env = new Environment(envHome, config);
150: envImpl = DbInternal.envGetEnvironmentImpl(env);
151: mb = envImpl.getMemoryBudget();
152:
153: DatabaseConfig dbConfig = new DatabaseConfig();
154: dbConfig.setTransactional(!lockMode.equals(LOCK_NOTXN));
155: dbConfig.setAllowCreate(true);
156: db = env.openDatabase(null, DB_NAME, dbConfig);
157: }
158:
159: /**
160: * Closes the environment and database.
161: */
162: private void closeEnv(boolean doCheckpoint)
163: throws DatabaseException {
164:
165: if (db != null) {
166: db.close();
167: db = null;
168: }
169: if (env != null) {
170: env.close();
171: env = null;
172: }
173: }
174:
175: /**
176: * Insert and then update some records. Measure memory usage at different
177: * points in this sequence, asserting that the memory usage count is
178: * properly decremented.
179: */
180: public void testWriteLocks() throws DatabaseException {
181:
182: loadData();
183:
184: /*
185: * Now update the database transactionally. This should not change
186: * the node related memory, but should add txn related cache
187: * consumption. If this is a user transaction, we should
188: * hold locks and consume more memory.
189: */
190: for (int t = 0; t < numTxns; t++) {
191: for (int i = 0; i < numRecordsPerTxn; i++) {
192: int value = i + (t * numRecordsPerTxn);
193: IntegerBinding.intToEntry(value, keyEntry);
194: IntegerBinding.intToEntry(value + 1, dataEntry);
195: assertEquals(db.put(txns[t], keyEntry, dataEntry),
196: OperationStatus.SUCCESS);
197: }
198: }
199: afterAction = mb.getCacheMemoryUsage();
200:
201: closeTxns(true);
202: }
203:
204: /**
205: * Insert and then scan some records. Measure memory usage at different
206: * points in this sequence, asserting that the memory usage count is
207: * properly decremented.
208: */
209: public void testReadLocks() throws DatabaseException {
210:
211: loadData();
212:
213: /*
214: * Now scan the database. Make sure all locking overhead is
215: * released.
216: */
217: for (int t = 0; t < numTxns; t++) {
218: Cursor c = db.openCursor(txns[t], null);
219: while (c.getNext(keyEntry, dataEntry, null) == OperationStatus.SUCCESS) {
220: }
221: c.close();
222: }
223: afterAction = mb.getCacheMemoryUsage();
224:
225: closeTxns(false);
226: }
227:
228: private void loadData() throws DatabaseException {
229:
230: openEnv();
231:
232: /* Build up a database to establish a given cache size. */
233: for (int t = 0; t < numTxns; t++) {
234: for (int i = 0; i < numRecordsPerTxn; i++) {
235:
236: int value = i + (t * numRecordsPerTxn);
237: IntegerBinding.intToEntry(value, keyEntry);
238: IntegerBinding.intToEntry(value, dataEntry);
239: assertEquals(db.put(null, keyEntry, dataEntry),
240: OperationStatus.SUCCESS);
241: }
242: }
243:
244: beforeAction = mb.getCacheMemoryUsage();
245:
246: /* Make some transactions. */
247: txns = new Transaction[numTxns];
248: if (lockMode.equals(LOCK_USERTXN)) {
249: for (int t = 0; t < numTxns; t++) {
250: txns[t] = env.beginTransaction(null, null);
251: }
252:
253: afterTxnsCreated = mb.getCacheMemoryUsage();
254: assertTrue("afterTxns=" + afterTxnsCreated
255: + "beforeUpdate=" + beforeAction,
256: (afterTxnsCreated > beforeAction));
257: }
258: }
259:
260: private void closeTxns(boolean writesDone) throws DatabaseException {
261:
262: assertTrue(afterAction > afterTxnsCreated);
263:
264: /*
265: * If this is not a user transactional lock, we should be done
266: * with all locking overhead. If it is a user transaction, we
267: * only release memory after locks are released at commit or
268: * abort.
269: */
270: if (lockMode.equals(LOCK_USERTXN)) {
271:
272: /*
273: * Note: expectedLockUsage is annoyingly fragile. If we change
274: * the lock implementation, this may not be the right number
275: * to check.
276: *
277: * Aborted transactions release more memory than just the lock
278: * related amount, because they actually null out LN references in
279: * the BINs.
280: */
281: long expectedLockUsage = (numRecordsPerTxn * numTxns * (LockManager.TOTAL_LOCK_OVERHEAD + MemoryBudget.LOCKINFO_OVERHEAD));
282:
283: long expectedFreedNodeMemory = 0;
284:
285: /*
286: * If this test aborted some writes, then there are rollbacks,
287: * which actually reduce the amount of memory held, because it
288: * causes LNs to get evicted.
289: */
290: if (endMode.equals(ABORT) && writesDone) {
291: LN sampleLN = new LN(dataEntry);
292: expectedFreedNodeMemory += ((numRecordsPerTxn * numTxns) * sampleLN
293: .getMemorySizeIncludedByParent());
294: }
295:
296: assertTrue((afterAction - afterTxnsCreated) >= expectedLockUsage);
297:
298: for (int t = 0; t < numTxns; t++) {
299: Transaction txn = txns[t];
300: if (endMode.equals(COMMIT)) {
301: txn.commit();
302: } else {
303: txn.abort();
304: }
305: }
306:
307: long afterTxnEnd = mb.getCacheMemoryUsage();
308:
309: assertTrue(
310: "lockMode=" + lockMode + " endMode=" + endMode
311: + " afterTxnEnd=" + afterTxnEnd
312: + " beforeAction=" + beforeAction
313: + " expectedFreed="
314: + expectedFreedNodeMemory,
315: (afterTxnEnd <= (beforeAction - expectedFreedNodeMemory)));
316: }
317: if (DEBUG) {
318: System.out.println("afterUpdate = " + afterAction
319: + " before=" + beforeAction);
320: }
321:
322: closeEnv(true);
323: }
324: }
|