Skip to content

Commit 10bf018

Browse files
committed
Way more details on checksum mismatch
1 parent 9550cf6 commit 10bf018

File tree

3 files changed

+63
-44
lines changed

3 files changed

+63
-44
lines changed

crates/core/src/sync/checksum.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use core::{
2+
fmt::Display,
23
num::Wrapping,
34
ops::{Add, AddAssign},
45
};
@@ -65,6 +66,12 @@ impl From<u32> for Checksum {
6566
}
6667
}
6768

69+
impl Display for Checksum {
70+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
71+
write!(f, "{:#010x}", self.value())
72+
}
73+
}
74+
6875
impl<'de> Deserialize<'de> for Checksum {
6976
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
7077
where

crates/core/src/sync/storage_adapter.rs

Lines changed: 54 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use alloc::{
44
string::{String, ToString},
55
vec::Vec,
66
};
7+
use num_traits::Zero;
78
use serde::Serialize;
89
use sqlite_nostd::{self as sqlite, Connection, ManagedStmt, ResultCode};
910
use streaming_iterator::StreamingIterator;
@@ -145,56 +146,39 @@ impl StorageAdapter {
145146
) -> Result<CheckpointResult, SQLiteError> {
146147
// language=SQLite
147148
let statement = self.db.prepare_v2(
148-
"WITH
149-
bucket_list(bucket, checksum) AS (
149+
"
150150
SELECT
151-
json_extract(json_each.value, '$.bucket') as bucket,
152-
json_extract(json_each.value, '$.checksum') as checksum
153-
FROM json_each(?1)
154-
)
155-
SELECT
156-
bucket_list.bucket as bucket,
157-
IFNULL(buckets.add_checksum, 0) as add_checksum,
158-
IFNULL(buckets.op_checksum, 0) as oplog_checksum,
159-
bucket_list.checksum as expected_checksum
160-
FROM bucket_list
161-
LEFT OUTER JOIN ps_buckets AS buckets ON
162-
buckets.name = bucket_list.bucket
163-
GROUP BY bucket_list.bucket",
151+
ps_buckets.add_checksum as add_checksum,
152+
ps_buckets.op_checksum as oplog_checksum
153+
FROM ps_buckets WHERE name = ?;",
164154
)?;
165155

166-
#[derive(Serialize)]
167-
struct BucketInfo<'a> {
168-
bucket: &'a str,
169-
checksum: Checksum,
170-
}
171-
172-
let mut buckets = Vec::<BucketInfo>::new();
156+
let mut failures: Vec<ChecksumMismatch> = Vec::new();
173157
for bucket in checkpoint.buckets.values() {
174158
if bucket.is_in_priority(priority) {
175-
buckets.push(BucketInfo {
176-
bucket: &bucket.bucket,
177-
checksum: bucket.checksum,
178-
});
179-
}
180-
}
159+
statement.bind_text(1, &bucket.bucket, sqlite_nostd::Destructor::STATIC)?;
181160

182-
let bucket_desc = serde_json::to_string(&buckets)?;
183-
statement.bind_text(1, &bucket_desc, sqlite::Destructor::STATIC)?;
161+
let (add_checksum, oplog_checksum) = match statement.step()? {
162+
ResultCode::ROW => {
163+
let add_checksum = Checksum::from_i32(statement.column_int(0));
164+
let oplog_checksum = Checksum::from_i32(statement.column_int(1));
165+
(add_checksum, oplog_checksum)
166+
}
167+
_ => (Checksum::zero(), Checksum::zero()),
168+
};
184169

185-
let mut failures: Vec<String> = Vec::new();
186-
while statement.step()? == ResultCode::ROW {
187-
let name = statement.column_text(0)?;
188-
// checksums with column_int are wrapped to i32 by SQLite
189-
let add_checksum = statement.column_int(1);
190-
let oplog_checksum = statement.column_int(2);
191-
let expected_checksum = statement.column_int(3);
170+
let actual = add_checksum + oplog_checksum;
192171

193-
// wrapping add is like +, but safely overflows
194-
let checksum = oplog_checksum.wrapping_add(add_checksum);
172+
if actual != bucket.checksum {
173+
failures.push(ChecksumMismatch {
174+
bucket_name: bucket.bucket.clone(),
175+
expected_checksum: bucket.checksum,
176+
actual_add_checksum: add_checksum,
177+
actual_op_checksum: oplog_checksum,
178+
});
179+
}
195180

196-
if checksum != expected_checksum {
197-
failures.push(String::from(name));
181+
statement.reset()?;
198182
}
199183
}
200184

@@ -211,7 +195,12 @@ GROUP BY bucket_list.bucket",
211195
let checksums = self.validate_checkpoint(checkpoint, priority)?;
212196

213197
if !checksums.is_valid() {
214-
self.delete_buckets(checksums.failed_buckets.iter().map(|i| i.as_str()))?;
198+
self.delete_buckets(
199+
checksums
200+
.failed_buckets
201+
.iter()
202+
.map(|i| i.bucket_name.as_str()),
203+
)?;
215204
return Ok(SyncLocalResult::ChecksumFailure(checksums));
216205
}
217206

@@ -312,7 +301,14 @@ pub struct BucketInfo {
312301
}
313302

314303
pub struct CheckpointResult {
315-
failed_buckets: Vec<String>,
304+
failed_buckets: Vec<ChecksumMismatch>,
305+
}
306+
307+
pub struct ChecksumMismatch {
308+
bucket_name: String,
309+
expected_checksum: Checksum,
310+
actual_op_checksum: Checksum,
311+
actual_add_checksum: Checksum,
316312
}
317313

318314
impl CheckpointResult {
@@ -340,6 +336,21 @@ impl Display for CheckpointResult {
340336
}
341337
}
342338

339+
impl Display for ChecksumMismatch {
340+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
341+
let actual = self.actual_add_checksum + self.actual_op_checksum;
342+
write!(
343+
f,
344+
"{} (expected {}, got {} = {} (op) + {} (add))",
345+
self.bucket_name,
346+
self.expected_checksum,
347+
actual,
348+
self.actual_op_checksum,
349+
self.actual_add_checksum
350+
)
351+
}
352+
}
353+
343354
pub enum SyncLocalResult {
344355
/// Changes could not be applied due to a checksum mismatch.
345356
ChecksumFailure(CheckpointResult),

dart/test/sync_test.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,8 @@ void _syncTests<T>({
633633
{
634634
'LogLine': {
635635
'severity': 'WARNING',
636-
'line': contains("Checksums didn't match, failed for: a")
636+
'line': contains(
637+
"Checksums didn't match, failed for: a (expected 0x000004d2, got 0x000010e1 = 0x000010e1 (op) + 0x00000000 (add))")
637638
}
638639
},
639640
{'CloseSyncStream': {}},

0 commit comments

Comments
 (0)