use of org.cojen.tupl.diag.EventListener in project Tupl by cojen.
the class IndexBackfill method run.
@Override
public void run() {
EventListener listener;
checkNotify: {
try {
if (mManager.mPrimaryIndex.isEmpty()) {
// No need to notify if index is empty because there isn't really a backfill.
listener = null;
break checkNotify;
}
} catch (IOException e) {
}
listener = mRowStore.mDatabase.eventListener();
}
if (listener != null) {
listener.notify(EventType.TABLE_INDEX_INFO, "Starting backfill for %1$s", mSecondaryStr);
}
boolean success = false;
try {
success = doRun();
if (!success && listener != null) {
listener.notify(EventType.TABLE_INDEX_INFO, "Stopped backfill for %1$s", mSecondaryStr);
}
} catch (Throwable e) {
error("Unable to backfill %1$s: %2$s", e);
}
try {
mRowStore.activateSecondaryIndex(this, success);
if (success && listener != null) {
listener.notify(EventType.TABLE_INDEX_INFO, "Finished backfill for %1$s", mSecondaryStr);
}
} catch (Throwable e) {
error("Unable to activate %1$s: %2$s", e);
}
}
use of org.cojen.tupl.diag.EventListener in project Tupl by cojen.
the class DatabaseReplicatorTest method startGroup.
/**
* @return first is the leader
*/
private Database[] startGroup(int members, Role replicaRole, Supplier<PrepareHandler> handlerSupplier) throws Exception {
if (members < 1) {
throw new IllegalArgumentException();
}
mSockets = new ServerSocket[members];
for (int i = 0; i < members; i++) {
mSockets[i] = TestUtils.newServerSocket();
}
mReplBaseFiles = new File[members];
mReplConfigs = new ReplicatorConfig[members];
mReplicators = new StreamReplicator[members];
mDbConfigs = new DatabaseConfig[members];
mDatabases = new Database[members];
for (int i = 0; i < members; i++) {
mReplBaseFiles[i] = TestUtils.newTempBaseFile(getClass());
EventListener listener = false ? EventListener.printTo(System.out) : null;
mReplConfigs[i] = new ReplicatorConfig().groupToken(1).localSocket(mSockets[i]).baseFile(mReplBaseFiles[i]).eventListener(listener);
if (i > 0) {
mReplConfigs[i].addSeed(mSockets[0].getLocalSocketAddress());
mReplConfigs[i].localRole(replicaRole);
}
mReplicators[i] = StreamReplicator.open(mReplConfigs[i]);
((Controller) mReplicators[i]).keepServerSocket();
mDbConfigs[i] = new DatabaseConfig().baseFile(mReplBaseFiles[i]).replicate(mReplicators[i]).eventListener(listener).lockTimeout(5, TimeUnit.SECONDS).directPageAccess(false);
if (handlerSupplier != null) {
mDbConfigs[i].prepareHandlers(Map.of("TestHandler", handlerSupplier.get()));
}
Database db = Database.open(mDbConfigs[i]);
mDatabases[i] = db;
readyCheck: {
for (int trial = 0; trial < 100; trial++) {
TestUtils.sleep(100);
if (i == 0) {
try {
db.openIndex("control");
// Ensure that replicas obtain the index in the snapshot.
db.checkpoint();
break readyCheck;
} catch (UnmodifiableReplicaException e) {
// Not leader yet.
}
} else {
assertNotNull(db.openIndex("control"));
break readyCheck;
}
}
throw new AssertionError(i == 0 ? "No leader" : "Not joined");
}
}
return mDatabases;
}
use of org.cojen.tupl.diag.EventListener 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);
}
Aggregations