Skip to content

Commit

Permalink
fixup! SRS actors: Don't attempt alarm deletion at end of handler if …
Browse files Browse the repository at this point in the history
…the output gate is already broken
  • Loading branch information
jclee committed Oct 30, 2024
1 parent 4fc3582 commit 36bd8c0
Showing 1 changed file with 36 additions and 46 deletions.
82 changes: 36 additions & 46 deletions src/workerd/io/actor-sqlite-test.c++
Original file line number Diff line number Diff line change
Expand Up @@ -631,60 +631,50 @@ KJ_TEST("canceling deferred alarm deletion is idempotent") {
KJ_ASSERT(expectSync(test.getAlarm()) == oneMs);
}

KJ_TEST("alarm handler cleanup succeeds when output gate is broken and handler fails") {
ActorSqliteTest test({.monitorOutputGate = false});
auto promise = test.gate.onBroken();

// Initialize alarm state to 1ms.
test.setAlarm(oneMs);
test.pollAndExpectCalls({"scheduleRun(1ms)"})[0]->fulfill();
test.pollAndExpectCalls({"commit"})[0]->fulfill();
test.pollAndExpectCalls({});
KJ_ASSERT(expectSync(test.getAlarm()) == oneMs);
KJ_TEST("alarm handler cleanup succeeds when output gate is broken") {
auto runWithSetup = [](auto testFunc) {
ActorSqliteTest test({.monitorOutputGate = false});
auto promise = test.gate.onBroken();

auto armResult = test.actor.armAlarmHandler(oneMs, false);
KJ_ASSERT(armResult.is<ActorSqlite::RunAlarmHandler>());

// Break gate
test.put("foo", "bar");
test.pollAndExpectCalls({"commit"})[0]->reject(KJ_EXCEPTION(FAILED, "a_rejected_commit"));
KJ_EXPECT_THROW_MESSAGE("a_rejected_commit", promise.wait(test.ws));
// Ensure taskFailed handler runs and notices brokenness:
test.ws.poll();
// Initialize alarm state to 1ms.
test.setAlarm(oneMs);
test.pollAndExpectCalls({"scheduleRun(1ms)"})[0]->fulfill();
test.pollAndExpectCalls({"commit"})[0]->fulfill();
test.pollAndExpectCalls({});
KJ_ASSERT(expectSync(test.getAlarm()) == oneMs);

// In the case where the handler fails, we assume the caller will explicitly cancel deferred
// alarm deletion:
test.actor.cancelDeferredAlarmDeletion();
auto armResult = test.actor.armAlarmHandler(oneMs, false);
KJ_ASSERT(armResult.is<ActorSqlite::RunAlarmHandler>());
auto deferredDelete = kj::mv(armResult.get<ActorSqlite::RunAlarmHandler>().deferredDelete);

// Dropping the DeferredAlarmDeleter should succeed.
{ auto drop = kj::mv(armResult); }
}
// Break gate
test.put("foo", "bar");
test.pollAndExpectCalls({"commit"})[0]->reject(KJ_EXCEPTION(FAILED, "a_rejected_commit"));
KJ_EXPECT_THROW_MESSAGE("a_rejected_commit", promise.wait(test.ws));
// Ensure taskFailed handler runs and notices brokenness:
test.ws.poll();

KJ_TEST("alarm handler cleanup succeeds when output gate is broken but handler succeeds") {
ActorSqliteTest test({.monitorOutputGate = false});
auto promise = test.gate.onBroken();
testFunc(test, kj::mv(deferredDelete));
};

// Initialize alarm state to 1ms.
test.setAlarm(oneMs);
test.pollAndExpectCalls({"scheduleRun(1ms)"})[0]->fulfill();
test.pollAndExpectCalls({"commit"})[0]->fulfill();
test.pollAndExpectCalls({});
KJ_ASSERT(expectSync(test.getAlarm()) == oneMs);
// Here, we test that the deferred deleter destructor doesn't throw, both in the case when the
// caller cancels deletion and when it does not cancel it:

auto armResult = test.actor.armAlarmHandler(oneMs, false);
KJ_ASSERT(armResult.is<ActorSqlite::RunAlarmHandler>());
runWithSetup([](ActorSqliteTest& test, kj::Own<void> deferredDelete) {
// In the case where the handler fails, we assume the caller will explicitly cancel deferred
// alarm deletion:
test.actor.cancelDeferredAlarmDeletion();

// Break gate
test.put("foo", "bar");
test.pollAndExpectCalls({"commit"})[0]->reject(KJ_EXCEPTION(FAILED, "a_rejected_commit"));
KJ_EXPECT_THROW_MESSAGE("a_rejected_commit", promise.wait(test.ws));
// Ensure taskFailed handler runs and notices brokenness:
test.ws.poll();
// Dropping the DeferredAlarmDeleter should succeed -- that is, it should not throw here:
{ auto drop = kj::mv(deferredDelete); }
});

// In the case where the handler succeeds, the caller will not cancel deferred deletion before
// dropping the DeferredAlarmDeleter. Dropping the DeferredAlarmDeleter should still succeed,
// even if the output gate happens to already be broken.
{ auto drop = kj::mv(armResult); }
runWithSetup([](ActorSqliteTest& test, kj::Own<void> deferredDelete) {
// In the case where the handler succeeds, the caller will not cancel deferred deletion before
// dropping the DeferredAlarmDeleter. Dropping the DeferredAlarmDeleter should still succeed,
// even if the output gate happens to already be broken:
{ auto drop = kj::mv(deferredDelete); }
});
}

KJ_TEST("handler alarm is not deleted when commit fails") {
Expand Down

0 comments on commit 36bd8c0

Please sign in to comment.