-
-
Notifications
You must be signed in to change notification settings - Fork 75
offload data loss fixes #235
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
Merged
Merged
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
3ccc263
fix(db): stop raw_archive silently dropping distinct frames on a reus…
abdulsaheel d2ccec4
feat(sync): log-only burst-completeness shortfall diagnostic (correct…
abdulsaheel 04a887e
fix(db): fsync the ACK-gating sync commit (synchronous=FULL bracket)
abdulsaheel a974918
docs(sync): don't overclaim shortfall as confirmed frame loss
abdulsaheel a434800
fix(db): re-key decoded ledger off volatile counter onto rec_ts
abdulsaheel 4928f28
docs(db): correct the isolate rationale in the sync=FULL bracket comment
abdulsaheel 52ee804
test(db): cover the v31→v32 raw_archive re-key on a populated counter…
abdulsaheel 161ded7
test(db): cover the v31 counter-keyed decoded store rekeying to rec_ts
abdulsaheel fe5510b
merge: decoded rec_ts-PK re-key (#234)
abdulsaheel ed45455
merge: num_packets shortfall diagnostic (#232)
abdulsaheel 72774a9
merge: raw_archive hex re-key (#231)
abdulsaheel 25a75f4
merge: synchronous=FULL ACK-commit durability bracket (#233)
abdulsaheel 90f9588
fix(sync): defer history offload under an untrustworthy phone clock (…
abdulsaheel 8573d7e
fix(sync): put the connect path behind the clock gate too
abdulsaheel 997e149
dont defer history forever if the strap clock is the fast one
abdulsaheel 2820403
clock gate fixes from cr
abdulsaheel 8ad8b8a
more cr fixes
abdulsaheel c5745ae
cr round 3
abdulsaheel 696afe1
cr round 4
abdulsaheel 9708326
drop the protocol dep — keep this PR whoop-4 only
abdulsaheel a54033f
cr round 5
abdulsaheel a1a2097
only _failConnect if we are still the live session
abdulsaheel File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
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
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
Large diffs are not rendered by default.
Oops, something went wrong.
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| // POWER-LOSS DURABILITY of the ACK-gating commit. `commitSyncBatch` is the one | ||
| // commit the safe-trim invariant hangs on: it must be durable (fsynced) BEFORE | ||
| // the caller writes the BLE batch-ACK that lets the band trim its flash. The DB | ||
| // otherwise runs WAL + synchronous=NORMAL (durable only at a checkpoint), so | ||
| // this path raises synchronous=FULL for its single transaction and restores | ||
| // NORMAL afterward — leaving every other (recomputable) path fast. This test | ||
| // pins that bracket: FULL is set around the commit, NORMAL is restored after, | ||
| // AND the restore still happens when the commit THROWS (a leaked FULL would | ||
| // fsync every subsequent write on the connection forever). | ||
| // | ||
| // We spy the real SQL stream via SqfliteDatabaseFactoryLogger (synchronous is | ||
| // per-connection and invisible from a second connection, so the log is the only | ||
| // honest observation point) and also read `PRAGMA synchronous` on LocalDb's own | ||
| // connection — the same one commitSyncBatch uses — to confirm the resting value. | ||
|
|
||
| import 'package:flutter_test/flutter_test.dart'; | ||
| import 'package:path/path.dart' as p; | ||
| import 'package:sqflite_common/sqflite_logger.dart'; | ||
| import 'package:sqflite_common_ffi/sqflite_ffi.dart'; | ||
| import 'package:openstrap_edge/data/db.dart'; | ||
| import 'package:openstrap_edge/data/models.dart'; | ||
|
|
||
| void main() { | ||
| // Every `synchronous=…` statement executed on any connection, in order. | ||
| final syncStmts = <String>[]; | ||
|
|
||
| setUpAll(() async { | ||
| sqfliteFfiInit(); | ||
| // ignore: experimental_member_use — stable enough to spy SQL in a test. | ||
| databaseFactory = SqfliteDatabaseFactoryLogger( | ||
| databaseFactoryFfi, | ||
| options: SqfliteLoggerOptions( | ||
| log: (event) { | ||
| if (event is SqfliteLoggerSqlEvent) { | ||
| final sql = event.sql.toLowerCase(); | ||
| if (sql.contains('pragma synchronous=')) syncStmts.add(sql); | ||
| } | ||
| }, | ||
| ), | ||
| ); | ||
| LocalDb.dbName = 'openstrap_ack_sync_full_test.db'; | ||
| final dir = await databaseFactory.getDatabasesPath(); | ||
| await databaseFactory.deleteDatabase(p.join(dir, LocalDb.dbName)); | ||
| // Force the open now so its onConfigure PRAGMAs aren't counted in per-test | ||
| // windows — each test clears syncStmts against an already-open connection. | ||
| await LocalDb.instance; | ||
| }); | ||
|
|
||
| tearDownAll(() async { | ||
| await LocalDb.close(); | ||
| final dir = await databaseFactory.getDatabasesPath(); | ||
| await databaseFactory.deleteDatabase(p.join(dir, LocalDb.dbName)); | ||
| }); | ||
|
|
||
| Future<int> restingSynchronous() async { | ||
| final db = await LocalDb.instance; | ||
| final rows = await db.rawQuery('PRAGMA synchronous'); | ||
| return rows.first.values.first as int; // FULL=2, NORMAL=1 | ||
| } | ||
|
|
||
| RawRecord recAt(int counter) => RawRecord( | ||
| counter: counter, | ||
| packetType: 0x2F, | ||
| hex: '2f18aabbccdd', | ||
| capturedAt: 1750000000000 + counter, | ||
| recTs: 1750000000 + counter, | ||
| ); | ||
|
|
||
| test('commitSyncBatch brackets synchronous=FULL and restores NORMAL', () async { | ||
| expect(await restingSynchronous(), 1, reason: 'connection opens at NORMAL'); | ||
|
|
||
| syncStmts.clear(); | ||
| await LocalDb.commitSyncBatch( | ||
| [recAt(5001)], | ||
| <Sample?>[Sample(tsEpoch: 1750005001, counter: 5001, hr: 60)], | ||
| trimToken: 'deadbeef', | ||
| ); | ||
|
|
||
| expect(syncStmts, ['pragma synchronous=full', 'pragma synchronous=normal'], | ||
| reason: 'FULL is set before the commit and NORMAL restored right after'); | ||
| expect(await restingSynchronous(), 1, reason: 'connection left at NORMAL'); | ||
| }); | ||
|
|
||
| test('synchronous is restored to NORMAL even when the commit throws', () async { | ||
| syncStmts.clear(); | ||
| // raws non-empty but samples empty → samples[i] throws RangeError INSIDE the | ||
| // db.transaction, after FULL is set. The finally must still restore NORMAL. | ||
| await expectLater( | ||
| LocalDb.commitSyncBatch([recAt(6001)], const <Sample?>[]), | ||
| throwsA(isA<RangeError>()), | ||
| ); | ||
|
|
||
| expect(syncStmts, ['pragma synchronous=full', 'pragma synchronous=normal'], | ||
| reason: 'a thrown commit must not leak FULL'); | ||
| expect(await restingSynchronous(), 1, | ||
| reason: 'FULL did not leak past the throwing commit'); | ||
| }); | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.