Don't schedule a move-later alarm if an interleaving commit setAlarm()#5589
Merged
MellowYarker merged 1 commit intomainfrom Nov 27, 2025
Merged
Don't schedule a move-later alarm if an interleaving commit setAlarm()#5589MellowYarker merged 1 commit intomainfrom
MellowYarker merged 1 commit intomainfrom
Conversation
kentonv
reviewed
Nov 26, 2025
825c1a8 to
362c2fc
Compare
kentonv
approved these changes
Nov 26, 2025
362c2fc to
73569aa
Compare
Prior to yielding to the event loop when we try to persist to SQLite, we save the current alarm time in `alarmStateForCommit`. When we come back after the operation completes, this value remains unchanged, but `alarmScheduledNoLaterThan` may have changed while we were async. Prior to this commit, that meant we might schedule a move-later alarm synchronization with the alarm manager if `alarmScheduledNoLaterThan` was now earlier than `alarmStateForCommit`, even if `alarmStateForCommit` was set to the currently durable (and synced!) alarm time. Consider the following: 1. SQLite and the alarm manager have an alarm time of 2 years from now. 2. The application does a large SQL insert, starting commit 1. We yield to the event loop in `commitImpl` when we try to persist the write to SQLite, but save `alarmStateForCommit` as the current alarm time (2 years from now) 3. The application does a setAlarm() to run the alarm in 5 minutes. This is a "move-earlier" operation, so we sync to the alarm manager eagerly. 4. The alarm manager now has an alarm time 5 minutes in the future, and we start trying to persist the setAlarm in 5 minutes to SQLite. 5. The first commit from (2) continues after persisting its large SQL insert. It sees `alarmStateForCommit` is 2 years from now, and `alarmScheduledNoLaterThan` is 5 minutes from now. We decide to do a background "move-later" sync with the alarm manager, then finish the commit. 6. The second commit from (3) finishes persisting the 5 minute alarm to SQLite and finishes its commit. 7. The background "move-later" alarm from (5) modifies the alarm manager's state to have an alarm time of 2 years in the future. The end state is we essentially miss the "move-earlier" update in the alarm manager, breaking our model of "move-early eagerly and move-later lazily". **The fix** We track `alarmVersion`, which is incremented every time the application calls setAlarm(). We capture this value before starting to persist to SQLite and compare it against the current `alarmVersion` after continuing commitImpl. If our captured `alarmVersion` differs from the current version, then the application called setAlarm() while our SQLite commit was in-flight, and so we can safely skip attempting a move-later sync with the alarm manager, since a subsequent commit will modify the alarm anyways.
73569aa to
5ab21e0
Compare
Contributor
Author
|
Latest is a rebase. The MacOS build was hanging at the linking step, not sure why. Figured it would be worth kicking the build from the top. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Prior to yielding to the event loop when we try to persist to SQLite, we save the current alarm time in
alarmStateForCommit. When we come back after the operation completes, this value remains unchanged, butalarmScheduledNoLaterThanmay have changed while we were async.Prior to this commit, that meant we might schedule a move-later alarm synchronization with the alarm manager if
alarmScheduledNoLaterThanwas now earlier thanalarmStateForCommit, even ifalarmStateForCommitwas set to the currently durable (and synced!) alarm time.Consider the following:
commitImplwhen we try to persist the write to SQLite, but savealarmStateForCommitas the current alarm time (2 years from now)alarmStateForCommitis 2 years from now, andalarmScheduledNoLaterThanis 5 minutes from now. We decide to do a background "move-later" sync with the alarm manager, then finish the commit.The end state is we essentially miss the "move-earlier" update in the alarm manager, breaking our model of "move-early eagerly and move-later lazily".
The fix
We track
alarmVersion, which is incremented every time the application calls setAlarm(). We capture this value before starting to persist to SQLite and compare it against the currentalarmVersionafter continuing commitImpl. If our capturedalarmVersiondiffers from the current version, then the application called setAlarm() while our SQLite commit was in-flight, and so we can safely skip attempting a move-later sync with the alarm manager, since a subsequent commit will modify the alarm anyways.