Search in sources :

Example 1 with EventType

use of org.cojen.tupl.diag.EventType in project Tupl by cojen.

the class TxnPrepareTest method reopenNoHandler.

@Test
public void reopenNoHandler() throws Exception {
    // When database is reopened without a recovery handler, the recovered transactions
    // aren't lost.
    var recovery = new PrepareHandler() {

        volatile byte[] message;

        volatile boolean prepareCommit;

        @Override
        public void prepare(Transaction txn, byte[] message) throws IOException {
            this.message = message;
            txn.commit();
        }

        @Override
        public void prepareCommit(Transaction txn, byte[] message) throws IOException {
            prepareCommit = true;
            prepare(txn, message);
        }
    };
    DatabaseConfig config = newConfig(recovery);
    Database db = newTempDatabase(config);
    PrepareHandler handler = db.prepareWriter("TestHandler");
    Index ix = db.openIndex("test");
    Transaction txn = db.newTransaction();
    byte[] key = "hello".getBytes();
    ix.store(txn, key, "world".getBytes());
    prepare(handler, txn, "message".getBytes());
    // Reopen without the handler.
    config.prepareHandlers(null);
    // Install a listener which is notified that recovery fails.
    var listener = new EventListener() {

        private boolean notified;

        @Override
        public void notify(EventType type, String message, Object... args) {
            if (type == EventType.RECOVERY_HANDLER_UNCAUGHT) {
                synchronized (this) {
                    notified = true;
                    notifyAll();
                }
            }
        }

        synchronized void waitForNotify() throws InterruptedException {
            while (!notified) {
                wait();
            }
        }
    };
    config.eventListener(listener);
    db = reopenTempDatabase(getClass(), db, config);
    // Still locked (unless prepareCommit)
    ix = db.openIndex("test");
    txn = db.newTransaction();
    if (isPrepareCommit()) {
        assertEquals(LockResult.ACQUIRED, ix.tryLockShared(txn, key, 0));
    } else {
        assertEquals(LockResult.TIMED_OUT_LOCK, ix.tryLockShared(txn, key, 0));
    }
    txn.reset();
    listener.waitForNotify();
    // Reopen with the handler installed.
    config.prepareHandlers(Map.of("TestHandler", recovery));
    config.eventListener(null);
    recovery.message = null;
    recovery.prepareCommit = false;
    db = reopenTempDatabase(getClass(), db, config);
    // Verify that the handler has committed the recovered transaction.
    ix = db.openIndex("test");
    fastAssertArrayEquals("world".getBytes(), ix.load(null, key));
    byte[] message = recovery.message;
    if (message == null && isPrepareCommit()) {
        // Wait for it since no lock was held.
        for (int i = 0; i < 10; i++) {
            Thread.sleep(1000);
            message = recovery.message;
            if (message != null) {
                break;
            }
        }
    }
    fastAssertArrayEquals("message".getBytes(), recovery.message);
    assertEquals(isPrepareCommit(), recovery.prepareCommit);
}
Also used : EventType(org.cojen.tupl.diag.EventType) PrepareHandler(org.cojen.tupl.ext.PrepareHandler) EventListener(org.cojen.tupl.diag.EventListener)

Aggregations

EventListener (org.cojen.tupl.diag.EventListener)1 EventType (org.cojen.tupl.diag.EventType)1 PrepareHandler (org.cojen.tupl.ext.PrepareHandler)1