-
Notifications
You must be signed in to change notification settings - Fork 301
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sqlite-backed DOs: Allow calling deleteAll() inside an alarm handler #3018
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -346,7 +346,17 @@ void ActorSqlite::maybeDeleteDeferredAlarm() { | |
inAlarmHandler = false; | ||
|
||
if (haveDeferredDelete) { | ||
metadata.setAlarm(kj::none); | ||
// If we have reached this point, the client is destroying its DeferredAlarmDeleter at the end | ||
// of an alarm handler run, and deletion hasn't been cancelled, indicating that the handler | ||
// returned success. | ||
// | ||
// If the output gate has somehow broken in the interim, attempting to write the deletion here | ||
// will cause the DeferredAlarmDeleter destructor to throw, which the caller probably isn't | ||
// expecting. So we'll skip the deletion attempt, and let the caller detect the gate | ||
// brokenness through other means. | ||
if (broken == kj::none) { | ||
metadata.setAlarm(kj::none); | ||
} | ||
haveDeferredDelete = false; | ||
} | ||
} | ||
|
@@ -513,15 +523,22 @@ ActorCacheInterface::DeleteAllResults ActorSqlite::deleteAll(WriteOptions option | |
} | ||
} | ||
|
||
if (localAlarmState == kj::none && !deleteAllCommitScheduled) { | ||
// If we're not going to perform a write to restore alarm state, we'll want to make sure the | ||
// commit callback is called for the deleteAll(). | ||
if (!deleteAllCommitScheduled) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In case it isn't apparent: the main fix here is removal of the The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I think you're overestimating the harm of a "redundant commit". And Let's instead simplify this code to just always schedule (and apply) the deleteAll commit. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm... Thinking about it a bit more while responding to Justin's comment... Is there a potential problem if we queue a commit task for the Or is it perhaps OK to assume the alarm state reset isn't crucial, if Or, I suppose, is some small window of alarm state loss unavoidable, due to the nature of deleting and reinstating the sqlite database? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess what you're getting at is that the reset commit task doesn't actually do the same set of work as a regular write commit -- it doesn't actually commit a transaction and whatnot. Yeah, I agree it would be a problem if we committed the reset without actually committing the transaction to restore the alarm time. So perhaps the reset commit task needs to actually skip calling
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another, maybe cleaner approach would be to unify the two commit tasks into a single one which checks both There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was also curious... I assume it's OK/moot that we call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah nothing matters once broken. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried combining the One thing I wasn't quite sure of, though, is the case involving explicit transactions:
When the I assume the And in this case, we'd also be running a |
||
// Make sure a commit callback is queued for the deleteAll(). | ||
commitTasks.add(outputGate.lockWhile(kj::evalLater([this]() mutable -> kj::Promise<void> { | ||
// Don't commit if shutdown() has been called. | ||
requireNotBroken(); | ||
|
||
deleteAllCommitScheduled = false; | ||
return commitCallback(); | ||
if (currentTxn.is<ImplicitTxn*>()) { | ||
// An implicit transaction is already scheduled, so we'll count on it to perform a commit when it's | ||
// done. This is particularly important for the case where deleteAll() was called while an alarm | ||
// is outstanding; resetting the alarm state (below) starts an implicit transaction. | ||
// We don't want to commit the deletion without that transaction. | ||
return kj::READY_NOW; | ||
} else { | ||
return commitCallback(); | ||
} | ||
}))); | ||
deleteAllCommitScheduled = true; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For my own education, we only expect to see
scheduleRun(1ms)
when setAlarm is called, and not on subsequent resets? Presumably, that's because the scheduled run gets scheduled outside of this deletion?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pretty much, yeah. The
scheduleRun()
hook is called by ActorSqlite as part of the committing logic -- implemented instartPrecommitAlarmScheduling()
andcommitImpl()
-- to request a change to the alarm manager's scheduled alarm notification time. If ActorSqlite knows the currently scheduled alarm doesn't need to be updated, it doesn't callscheduleRun()
.But thinking about this a bit more, I guess the point of confusion might be why we don't see a
scheduleRun(kj::none)
in the test corresponding to thedb.reset()
and a subsequentscheduleRun(1ms)
corresponding to the alarm state reset? That's because we don't run the committing logic for thedb.reset()
, only for the state reset. And because we're restoring the original state, the committing logic sees no change, soscheduleRun()
is not called.I haven't yet implemented Kenton's suggestion of simplifying the deleteAll() logic by performing a redundant commit, but I'm pretty sure this won't change the situation, since the promise that deleteAll() queues calls
commitCallback()
directly, rather than through the alarm commit logic. And I'm pretty sure that's OK? But might need to think about it a bit more to be sure.