From c3b0c0780bde8ad1e957effa712223acaa5b0c31 Mon Sep 17 00:00:00 2001 From: Joe Lee Date: Wed, 9 Oct 2024 23:13:04 -0700 Subject: [PATCH] SQLite-backed DOs: check actor brokenness before starting implicit transactions --- src/workerd/io/actor-sqlite-test.c++ | 21 +++++++++++++++++++++ src/workerd/io/actor-sqlite.c++ | 1 + 2 files changed, 22 insertions(+) diff --git a/src/workerd/io/actor-sqlite-test.c++ b/src/workerd/io/actor-sqlite-test.c++ index b01af7e17f9..575c4948d21 100644 --- a/src/workerd/io/actor-sqlite-test.c++ +++ b/src/workerd/io/actor-sqlite-test.c++ @@ -1136,5 +1136,26 @@ KJ_TEST("rolling back nested transaction leaves deferred alarm deletion in expec KJ_ASSERT(expectSync(test.getAlarm()) == kj::none); } +KJ_TEST("database write operations check for brokenness") { + ActorSqliteTest test({.monitorOutputGate = false}); + + auto promise = test.gate.onBroken(); + + // 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)); + + // We don't actually set ActorSqlite's brokenness until the taskFailed handler runs... + test.ws.poll(); + + // Try making a write operation to the database, expecting it to throw the broken message via + // the onWrite handler: + KJ_EXPECT_THROW_MESSAGE( + "a_rejected_commit", test.db.run("CREATE TABLE IF NOT EXISTS counter (count INTEGER)")); + test.pollAndExpectCalls({}); +} + } // namespace } // namespace workerd diff --git a/src/workerd/io/actor-sqlite.c++ b/src/workerd/io/actor-sqlite.c++ index b3bc1cfda90..7a604422a78 100644 --- a/src/workerd/io/actor-sqlite.c++ +++ b/src/workerd/io/actor-sqlite.c++ @@ -194,6 +194,7 @@ void ActorSqlite::ExplicitTxn::rollbackImpl() noexcept(false) { } void ActorSqlite::onWrite() { + requireNotBroken(); if (currentTxn.is()) { auto txn = kj::heap(*this);