1 8 9 package com.sleepycat.je.test; 10 11 import java.io.File ; 12 import java.io.IOException ; 13 14 import junit.framework.Test; 15 import junit.framework.TestCase; 16 import junit.framework.TestSuite; 17 18 import com.sleepycat.bind.tuple.IntegerBinding; 19 import com.sleepycat.je.Cursor; 20 import com.sleepycat.je.Database; 21 import com.sleepycat.je.DatabaseConfig; 22 import com.sleepycat.je.DatabaseEntry; 23 import com.sleepycat.je.DatabaseException; 24 import com.sleepycat.je.DeadlockException; 25 import com.sleepycat.je.Environment; 26 import com.sleepycat.je.EnvironmentConfig; 27 import com.sleepycat.je.LockStats; 28 import com.sleepycat.je.OperationStatus; 29 import com.sleepycat.je.Transaction; 30 import com.sleepycat.je.config.EnvironmentParams; 31 import com.sleepycat.je.junit.JUnitThread; 32 import com.sleepycat.je.log.FileManager; 33 import com.sleepycat.je.util.TestUtils; 34 35 39 public class PhantomRestartTest extends TestCase { 40 41 66 private static Spec[] SPECS = { 67 68 73 new Spec("First", 2, 1, new Oper() { 74 void doOper(int insertedKey) throws DatabaseException { 75 status = cursor.getFirst(key, data, null); 76 checkStatus(OperationStatus.SUCCESS); 77 checkKey(insertedKey); 78 } 79 }), 80 81 86 new Spec("Last", 1, 2, new Oper() { 87 void doOper(int insertedKey) throws DatabaseException { 88 status = cursor.getLast(key, data, null); 89 checkStatus(OperationStatus.SUCCESS); 90 checkKey(insertedKey); 91 } 92 }), 93 94 99 new Spec("Search", 2, 1, new Oper() { 100 void doOper(int insertedKey) throws DatabaseException { 101 setKey(1); 102 status = dups ? cursor.getSearchBoth(key, data, null) 103 : cursor.getSearchKey(key, data, null); 104 checkStatus((insertedKey == 1) ? OperationStatus.SUCCESS 105 : OperationStatus.NOTFOUND); 106 } 107 }), 108 109 115 new Spec("SearchRange", 2, 1, new Oper() { 116 void doOper(int insertedKey) throws DatabaseException { 117 setKey(0); 118 status = dups ? cursor.getSearchBothRange(key, data, null) 119 : cursor.getSearchKeyRange(key, data, null); 120 checkStatus(OperationStatus.SUCCESS); 121 checkKey(insertedKey); 122 } 123 }), 124 125 130 new Spec("Next", 1, 2, new Oper() { 131 void doOper(int insertedKey) throws DatabaseException { 132 status = cursor.getFirst(key, data, null); 133 checkStatus(OperationStatus.SUCCESS); 134 checkKey(1); 135 status = cursor.getNext(key, data, null); 136 checkStatus((insertedKey == 2) ? OperationStatus.SUCCESS 137 : OperationStatus.NOTFOUND); 138 } 139 }), 140 141 146 new Spec("Prev", 2, 1, new Oper() { 147 void doOper(int insertedKey) throws DatabaseException { 148 status = cursor.getLast(key, data, null); 149 checkStatus(OperationStatus.SUCCESS); 150 checkKey(2); 151 status = cursor.getPrev(key, data, null); 152 checkStatus((insertedKey == 1) ? OperationStatus.SUCCESS 153 : OperationStatus.NOTFOUND); 154 } 155 }), 156 157 162 }; 163 164 private static abstract class Oper { 165 166 PhantomRestartTest test; 167 boolean dups; 168 Cursor cursor; 169 DatabaseEntry key; 170 DatabaseEntry data; 171 OperationStatus status; 172 173 void init(PhantomRestartTest test, Cursor cursor) { 174 this.test = test; 175 this.cursor = cursor; 176 this.dups = test.dups; 177 this.key = new DatabaseEntry(); 178 this.data = new DatabaseEntry(); 179 this.status = null; 180 } 181 182 void checkStatus(OperationStatus expected) { 183 TestCase.assertEquals(expected, status); 184 } 185 186 void setKey(int val) { 187 if (dups) { 188 IntegerBinding.intToEntry(100, key); 189 IntegerBinding.intToEntry(val, data); 190 } else { 191 IntegerBinding.intToEntry(val, key); 192 } 193 } 194 195 void checkKey(int expected) { 196 if (dups) { 197 TestCase.assertEquals(100, IntegerBinding.entryToInt(key)); 198 TestCase.assertEquals 199 (expected, IntegerBinding.entryToInt(data)); 200 } else { 201 TestCase.assertEquals 202 (expected, IntegerBinding.entryToInt(key)); 203 } 204 } 205 206 abstract void doOper(int insertedKey) 207 throws DatabaseException; 208 } 209 210 private static class Spec { 211 212 String name; 213 int insertKey1; 214 int insertKey2; 215 Oper oper; 216 217 Spec(String name, int insertKey1, int insertKey2, Oper oper) { 218 this.name = name; 219 this.insertKey1 = insertKey1; 220 this.insertKey2 = insertKey2; 221 this.oper = oper; 222 } 223 } 224 225 public static Test suite() 226 throws Exception { 227 228 TestSuite suite = new TestSuite(); 229 for (int i = 0; i < SPECS.length; i += 1) { 230 for (int j = 0; j < 2; j += 1) { 231 boolean dups = (j != 0); 232 suite.addTest(new PhantomRestartTest(SPECS[i], dups)); 233 } 234 } 235 return suite; 236 } 237 238 private static final int MAX_INSERT_MILLIS = 5000; 239 240 private File envHome; 241 private Environment env; 242 private Database db; 243 private JUnitThread writerThread; 244 private JUnitThread readerThread; 245 private boolean dups; 246 private Spec spec; 247 248 public PhantomRestartTest(Spec spec, boolean dups) { 249 super(spec.name + (dups ? "-Dups" : "")); 250 this.spec = spec; 251 this.dups = dups; 252 envHome = new File (System.getProperty(TestUtils.DEST_DIR)); 253 } 254 255 public void setUp() 256 throws IOException { 257 258 TestUtils.removeLogFiles("Setup", envHome, false); 259 TestUtils.removeFiles("Setup", envHome, FileManager.DEL_SUFFIX); 260 } 261 262 public void tearDown() 263 throws Exception { 264 265 try { 266 if (env != null) { 267 env.close(); 268 } 269 } catch (Throwable e) { 270 System.out.println("tearDown: " + e); 271 } 272 273 try { 274 TestUtils.removeLogFiles("tearDown", envHome, true); 276 TestUtils.removeFiles("tearDown", envHome, FileManager.DEL_SUFFIX); 277 } catch (Throwable e) { 279 System.out.println("tearDown: " + e); 280 } 281 282 envHome = null; 283 env = null; 284 db = null; 285 286 if (writerThread != null) { 287 while (writerThread.isAlive()) { 288 writerThread.interrupt(); 289 Thread.yield(); 290 } 291 writerThread = null; 292 } 293 294 if (readerThread != null) { 295 while (readerThread.isAlive()) { 296 readerThread.interrupt(); 297 Thread.yield(); 298 } 299 readerThread = null; 300 } 301 } 302 303 306 private void openEnv() 307 throws DatabaseException { 308 309 EnvironmentConfig envConfig = TestUtils.initEnvConfig(); 310 envConfig.setAllowCreate(true); 311 envConfig.setTransactional(true); 312 envConfig.setTxnSerializableIsolation(true); 313 314 315 envConfig.setConfigParam 316 (EnvironmentParams.ENV_RUN_EVICTOR.getName(), "false"); 317 envConfig.setConfigParam 318 (EnvironmentParams.ENV_RUN_CLEANER.getName(), "false"); 319 envConfig.setConfigParam 320 (EnvironmentParams.ENV_RUN_CHECKPOINTER.getName(), "false"); 321 envConfig.setConfigParam 322 (EnvironmentParams.ENV_RUN_INCOMPRESSOR.getName(), "false"); 323 324 env = new Environment(envHome, envConfig); 325 326 DatabaseConfig dbConfig = new DatabaseConfig(); 327 dbConfig.setAllowCreate(true); 328 dbConfig.setTransactional(true); 329 dbConfig.setSortedDuplicates(dups); 330 db = env.openDatabase(null, "PhantomRestartTest", dbConfig); 331 } 332 333 336 private void closeEnv() 337 throws DatabaseException { 338 339 if (db != null) { 340 db.close(); 341 db = null; 342 } 343 if (env != null) { 344 env.close(); 345 env = null; 346 } 347 } 348 349 public void runTest() 350 throws DatabaseException, InterruptedException { 351 352 openEnv(); 353 354 355 if (dups) { 356 357 364 insert(100, 0); 365 insert(100, 1); 366 DatabaseEntry key = new DatabaseEntry(); 367 IntegerBinding.intToEntry(100, key); 368 db.delete(null, key); 369 370 371 insert(100, spec.insertKey1); 372 } else { 373 insert(spec.insertKey1, 0); 374 } 375 376 377 Transaction readerTxn = env.beginTransaction(null, null); 378 Cursor cursor = db.openCursor(readerTxn, null); 379 spec.oper.init(this, cursor); 380 spec.oper.doOper(spec.insertKey1); 381 382 383 if (dups) { 384 startInsert(100, spec.insertKey2); 385 } else { 386 startInsert(spec.insertKey2, 0); 387 } 388 389 390 startReadOper(spec.insertKey2); 391 392 393 cursor.close(); 394 readerTxn.commitNoSync(); 395 waitForInsert(); 396 waitForReadOper(); 397 398 399 readerTxn = env.beginTransaction(null, null); 400 cursor = db.openCursor(readerTxn, null); 401 spec.oper.init(this, cursor); 402 spec.oper.doOper(spec.insertKey2); 403 cursor.close(); 404 readerTxn.commit(); 405 406 closeEnv(); 407 } 408 409 412 private void insert(int keyVal, int dataVal) 413 throws DatabaseException { 414 415 DatabaseEntry key = new DatabaseEntry(); 416 DatabaseEntry data = new DatabaseEntry(); 417 IntegerBinding.intToEntry(keyVal, key); 418 IntegerBinding.intToEntry(dataVal, data); 419 OperationStatus status; 420 Transaction writerTxn = env.beginTransaction(null, null); 421 try { 422 if (dups) { 423 status = db.putNoDupData(writerTxn, key, data); 424 } else { 425 status = db.putNoOverwrite(writerTxn, key, data); 426 } 427 } catch (DeadlockException e) { 428 writerTxn.abort(); 429 throw e; 430 } 431 assertEquals(OperationStatus.SUCCESS, status); 432 writerTxn.commitNoSync(); 433 } 434 435 438 private void startInsert(final int keyVal, final int dataVal) 439 throws DatabaseException, InterruptedException { 440 441 LockStats origStats = env.getLockStats(null); 442 443 writerThread = new JUnitThread("Writer") { 444 public void testBody() 445 throws DatabaseException { 446 DatabaseEntry key = new DatabaseEntry(); 447 DatabaseEntry data = new DatabaseEntry(); 448 IntegerBinding.intToEntry(keyVal, key); 449 IntegerBinding.intToEntry(dataVal, data); 450 Transaction writerTxn = env.beginTransaction(null, null); 451 OperationStatus status; 452 if (dups) { 453 status = db.putNoDupData(writerTxn, key, data); 454 } else { 455 status = db.putNoOverwrite(writerTxn, key, data); 456 } 457 assertEquals(OperationStatus.SUCCESS, status); 458 writerTxn.commitNoSync(); 459 } 460 }; 461 462 writerThread.start(); 463 waitForBlock(origStats); 464 } 465 466 469 private void waitForInsert() { 470 471 try { 472 writerThread.finishTest(); 473 } catch (Throwable e) { 474 e.printStackTrace(); 475 fail(e.toString()); 476 } finally { 477 writerThread = null; 478 } 479 } 480 481 484 private void startReadOper(final int operKeyParam) 485 throws DatabaseException, InterruptedException { 486 487 LockStats origStats = env.getLockStats(null); 488 489 readerThread = new JUnitThread("Reader") { 490 public void testBody() 491 throws DatabaseException { 492 Transaction readerTxn = env.beginTransaction(null, null); 493 Cursor cursor = db.openCursor(readerTxn, null); 494 spec.oper.init(PhantomRestartTest.this, cursor); 495 spec.oper.doOper(operKeyParam); 496 cursor.close(); 497 readerTxn.commitNoSync(); 498 } 499 }; 500 501 readerThread.start(); 502 waitForBlock(origStats); 503 } 504 505 508 private void waitForBlock(LockStats origStats) 509 throws DatabaseException, InterruptedException { 510 511 long startTime = System.currentTimeMillis(); 512 while (true) { 513 514 515 Thread.yield(); 516 Thread.sleep(10); 517 if (System.currentTimeMillis() - startTime > MAX_INSERT_MILLIS) { 518 fail("Timeout"); 519 } 520 521 522 LockStats stats = env.getLockStats(null); 523 if (stats.getNWaiters() > origStats.getNWaiters()) { 524 break; 525 } 526 } 527 } 528 529 532 private void waitForReadOper() { 533 534 try { 535 readerThread.finishTest(); 536 } catch (Throwable e) { 537 e.printStackTrace(); 538 fail(e.toString()); 539 } finally { 540 readerThread = null; 541 } 542 } 543 } 544 | Popular Tags |