Search in sources :

Example 1 with UnmodifiableReplicaException

use of org.cojen.tupl.UnmodifiableReplicaException in project Tupl by cojen.

the class ReplController method ready.

public void ready(long initialPosition, long initialTxnId) throws IOException {
    acquireExclusive();
    try {
        mLeaderNotifyCondition = new LatchCondition();
        // Init for the shouldCheckpoint method. Without this, an initial checkpoint is
        // performed even if it's not necessary.
        cCheckpointPosHandle.setOpaque(this, initialPosition | (1L << 63));
    } finally {
        releaseExclusive();
    }
    ReplDecoder decoder = mEngine.startReceiving(initialPosition, initialTxnId);
    if (decoder == null) {
        // Failed to start, and database has been closed with an exception.
        return;
    }
    CoreDatabase db = mEngine.mDatabase;
    // Can now send control messages.
    mRepl.controlMessageAcceptor(message -> {
        try {
            db.writeControlMessage(message);
        } catch (UnmodifiableReplicaException e) {
        // Drop it.
        } catch (Throwable e) {
            Utils.uncaught(e);
        }
    });
    // Can now accept snapshot requests.
    mRepl.snapshotRequestAcceptor(sender -> {
        try {
            ReplUtils.sendSnapshot(db, sender);
        } catch (Throwable e) {
            Utils.closeQuietly(sender);
            if (e instanceof DatabaseException || !(e instanceof IOException)) {
                Utils.uncaught(e);
            }
        }
    });
    // Update the local member role.
    mRepl.start();
    // Wait until replication has "caught up" before returning.
    boolean isLeader = decoder.catchup();
    // We're not truly caught up until all outstanding redo operations have been applied.
    // Suspend and resume does the trick.
    mEngine.suspend();
    mEngine.resume();
    // Wait for leaderNotify method to be called. The local member might be the leader now,
    // or the new leadership might have been immediately revoked. Either case is detected.
    acquireExclusive();
    try {
        if (isLeader && mLeaderNotifyCondition != null) {
            mLeaderNotifyCondition.await(this);
        }
    } finally {
        mLeaderNotifyCondition = null;
        releaseExclusive();
    }
}
Also used : UnmodifiableReplicaException(org.cojen.tupl.UnmodifiableReplicaException) LatchCondition(org.cojen.tupl.util.LatchCondition) IOException(java.io.IOException) DatabaseException(org.cojen.tupl.DatabaseException)

Example 2 with UnmodifiableReplicaException

use of org.cojen.tupl.UnmodifiableReplicaException in project Tupl by cojen.

the class DatabaseReplicatorTest method prepareTransfer.

private void prepareTransfer(boolean prepareCommit) throws Exception {
    // Prepared transaction should be transferred to replica and finish.
    var dbQueue = new LinkedBlockingQueue<Database>();
    var txnQueue = new LinkedBlockingQueue<Transaction>();
    Supplier<PrepareHandler> supplier = () -> new PrepareHandler() {

        private Database mDb;

        @Override
        public void init(Database db) {
            mDb = db;
        }

        @Override
        public void prepare(Transaction txn, byte[] message) throws IOException {
            dbQueue.add(mDb);
            txnQueue.add(txn);
        }

        @Override
        public void prepareCommit(Transaction txn, byte[] message) throws IOException {
            prepare(txn, message);
        }
    };
    Database[] dbs = startGroup(2, Role.NORMAL, supplier);
    Database leaderDb = dbs[0];
    Database replicaDb = dbs[1];
    Index leaderIx = leaderDb.openIndex("test");
    // Wait for replica to catch up.
    fence(leaderDb, replicaDb);
    Index replicaIx = replicaDb.openIndex("test");
    Transaction txn1 = leaderDb.newTransaction();
    PrepareHandler handler = leaderDb.prepareWriter("TestHandler");
    byte[] k1 = "k1".getBytes();
    byte[] v1 = "v1".getBytes();
    leaderIx.store(txn1, k1, v1);
    if (prepareCommit) {
        handler.prepareCommit(txn1, null);
        fastAssertArrayEquals(v1, leaderIx.load(null, k1));
    } else {
        handler.prepare(txn1, null);
        try {
            leaderIx.load(null, k1);
            fail();
        } catch (LockTimeoutException e) {
        }
    }
    leaderDb.failover();
    // Replica is now the leader and should have the transaction.
    assertEquals(replicaDb, dbQueue.take());
    Transaction txn2 = txnQueue.take();
    assertNotEquals(txn1, txn2);
    assertEquals(txn1.id(), txn2.id());
    fastAssertArrayEquals(v1, replicaIx.load(txn2, k1));
    byte[] k2 = "k2".getBytes();
    byte[] v2 = "v2".getBytes();
    replicaIx.store(txn2, k2, v2);
    if (prepareCommit) {
        fastAssertArrayEquals(v1, replicaIx.load(null, k1));
    } else {
        try {
            replicaIx.load(null, k1);
            fail();
        } catch (LockTimeoutException e) {
        }
    }
    txn2.commit();
    // Wait for old leader to catch up. This will fail at first because the old leader
    // transaction is stuck.
    boolean pass = true;
    try {
        fence(replicaDb, leaderDb, true);
        pass = false;
    } catch (AssertionError e) {
    }
    assertTrue(pass);
    try {
        txn1.commit();
        fail();
    } catch (UnmodifiableReplicaException e) {
    // This will unstick the transaction.
    }
    fence(replicaDb, leaderDb);
    // Verify that the old leader observes the committed changes.
    fastAssertArrayEquals(v1, leaderIx.load(null, k1));
    fastAssertArrayEquals(v2, leaderIx.load(null, k2));
}
Also used : Index(org.cojen.tupl.Index) LinkedBlockingQueue(java.util.concurrent.LinkedBlockingQueue) UnmodifiableReplicaException(org.cojen.tupl.UnmodifiableReplicaException) Transaction(org.cojen.tupl.Transaction) PrepareHandler(org.cojen.tupl.ext.PrepareHandler) Database(org.cojen.tupl.Database) LockTimeoutException(org.cojen.tupl.LockTimeoutException)

Example 3 with UnmodifiableReplicaException

use of org.cojen.tupl.UnmodifiableReplicaException in project Tupl by cojen.

the class DatabaseReplicatorTest method prepareBlank.

private void prepareBlank(boolean prepareCommit) throws Exception {
    // Test against a prepared transaction that has no changes. It should still ensure that
    // the transaction is created properly on the replica.
    var dbQueue = new LinkedBlockingQueue<Database>();
    var txnQueue = new LinkedBlockingQueue<Transaction>();
    var msgQueue = new LinkedBlockingQueue<byte[]>();
    var pcQueue = new LinkedBlockingQueue<Boolean>();
    Supplier<PrepareHandler> supplier = () -> new PrepareHandler() {

        private Database mDb;

        @Override
        public void init(Database db) {
            mDb = db;
        }

        @Override
        public void prepare(Transaction txn, byte[] message) throws IOException {
            doPrepare(txn, message, false);
        }

        @Override
        public void prepareCommit(Transaction txn, byte[] message) throws IOException {
            doPrepare(txn, message, true);
        }

        private void doPrepare(Transaction txn, byte[] message, boolean commit) throws IOException {
            dbQueue.add(mDb);
            txnQueue.add(txn);
            msgQueue.add(message);
            pcQueue.add(commit);
        }
    };
    Database[] dbs = startGroup(2, Role.NORMAL, supplier);
    Database leaderDb = dbs[0];
    Database replicaDb = dbs[1];
    Index leaderIx = leaderDb.openIndex("test");
    // Wait for replica to catch up.
    fence(leaderDb, replicaDb);
    Index replicaIx = replicaDb.openIndex("test");
    Transaction txn1 = leaderDb.newTransaction();
    PrepareHandler handler = leaderDb.prepareWriter("TestHandler");
    if (prepareCommit) {
        handler.prepareCommit(txn1, "message".getBytes());
    } else {
        handler.prepare(txn1, "message".getBytes());
    }
    leaderDb.failover();
    // Must capture the id before it gets replaced.
    long txnId = txn1.id();
    try {
        txn1.commit();
        fail();
    } catch (UnmodifiableReplicaException e) {
    // This will unstick the transaction.
    }
    // Replica is now the leader and should have the transaction.
    assertEquals(replicaDb, dbQueue.take());
    Transaction txn2 = txnQueue.take();
    assertNotEquals(txn1, txn2);
    assertEquals(txnId, txn2.id());
    fastAssertArrayEquals("message".getBytes(), msgQueue.take());
    assertEquals(prepareCommit, pcQueue.take());
    byte[] k1 = "k1".getBytes();
    byte[] v1 = "v1".getBytes();
    replicaIx.store(txn2, k1, v1);
    txn2.commit();
    fence(replicaDb, leaderDb);
    // Verify that the old leader observes the committed changes.
    fastAssertArrayEquals(v1, leaderIx.load(null, k1));
}
Also used : UnmodifiableReplicaException(org.cojen.tupl.UnmodifiableReplicaException) Transaction(org.cojen.tupl.Transaction) PrepareHandler(org.cojen.tupl.ext.PrepareHandler) Database(org.cojen.tupl.Database) Index(org.cojen.tupl.Index) LinkedBlockingQueue(java.util.concurrent.LinkedBlockingQueue)

Example 4 with UnmodifiableReplicaException

use of org.cojen.tupl.UnmodifiableReplicaException in project Tupl by cojen.

the class DatabaseReplicatorTest method basicTest.

private void basicTest(int memberCount) throws Exception {
    Database[] dbs = startGroup(memberCount);
    Index ix0 = dbs[0].openIndex("test");
    for (int t = 0; t < 10; t++) {
        byte[] key = ("hello-" + t).getBytes();
        byte[] value = ("world-" + t).getBytes();
        ix0.store(null, key, value);
        fastAssertArrayEquals(value, ix0.load(null, key));
        for (int i = 0; i < dbs.length; i++) {
            for (int q = 100; --q >= 0; ) {
                byte[] actual = null;
                try {
                    Index ix = dbs[i].openIndex("test");
                    actual = ix.load(null, key);
                    fastAssertArrayEquals(value, actual);
                    break;
                } catch (UnmodifiableReplicaException e) {
                    // Index doesn't exist yet due to replication delay.
                    if (q == 0) {
                        throw e;
                    }
                } catch (AssertionError e) {
                    // Value doesn't exist yet due to replication delay.
                    if (q == 0 || actual != null) {
                        throw e;
                    }
                }
                TestUtils.sleep(100);
            }
        }
    }
}
Also used : UnmodifiableReplicaException(org.cojen.tupl.UnmodifiableReplicaException) Database(org.cojen.tupl.Database) Index(org.cojen.tupl.Index)

Example 5 with UnmodifiableReplicaException

use of org.cojen.tupl.UnmodifiableReplicaException in project Tupl by cojen.

the class DatabaseReplicatorTest method explicitFailover.

@Test
public void explicitFailover() throws Exception {
    Database[] dbs = startGroup(2);
    Database leaderDb = dbs[0];
    Database replicaDb = dbs[1];
    Index leaderIx = leaderDb.openIndex("test");
    // Wait for replica to catch up.
    fence(leaderDb, replicaDb);
    Index replicaIx = replicaDb.openIndex("test");
    byte[] key = "hello".getBytes();
    byte[] value = "world".getBytes();
    leaderDb.failover();
    try {
        leaderIx.store(null, key, value);
        fail();
    } catch (UnmodifiableReplicaException e) {
    }
    boolean success = false;
    for (int i = 0; i < 10; i++) {
        try {
            replicaIx.store(null, key, value);
            success = true;
            break;
        } catch (UnmodifiableReplicaException e) {
        }
        TestUtils.sleep(1000);
    }
    assertTrue(success);
    // Wait for old leader to catch up.
    fence(replicaDb, leaderDb);
    fastAssertArrayEquals(value, leaderIx.load(null, key));
}
Also used : UnmodifiableReplicaException(org.cojen.tupl.UnmodifiableReplicaException) Database(org.cojen.tupl.Database) Index(org.cojen.tupl.Index)

Aggregations

UnmodifiableReplicaException (org.cojen.tupl.UnmodifiableReplicaException)8 Database (org.cojen.tupl.Database)7 Index (org.cojen.tupl.Index)6 LinkedBlockingQueue (java.util.concurrent.LinkedBlockingQueue)3 Transaction (org.cojen.tupl.Transaction)3 PrepareHandler (org.cojen.tupl.ext.PrepareHandler)3 IOException (java.io.IOException)1 DatabaseConfig (org.cojen.tupl.DatabaseConfig)1 DatabaseException (org.cojen.tupl.DatabaseException)1 LockTimeoutException (org.cojen.tupl.LockTimeoutException)1 EventListener (org.cojen.tupl.diag.EventListener)1 LatchCondition (org.cojen.tupl.util.LatchCondition)1