Skip to content

Commit 0778657

Browse files
authored
libsql: Fix metadata handling (#2149)
Fix metadata handling to ensure that we don't have inconsistent metadata locally.
2 parents 8f7d444 + 4d9247b commit 0778657

File tree

2 files changed

+31
-14
lines changed

2 files changed

+31
-14
lines changed

libsql/src/sync.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ pub enum SyncError {
6868
InvalidLocalGeneration(u32, u32),
6969
#[error("invalid local state: {0}")]
7070
InvalidLocalState(String),
71+
#[error("invalid remote state: {0}")]
72+
InvalidRemoteState(String),
7173
#[error("server returned invalid length of frames: {0}")]
7274
InvalidPullFrameBytes(usize),
7375
}
@@ -169,14 +171,10 @@ impl SyncContext {
169171
initial_server_sync: false,
170172
remote_encryption,
171173
};
172-
173-
if let Err(e) = me.read_metadata().await {
174-
tracing::error!(
175-
"failed to read sync metadata file, resetting back to defaults: {}",
176-
e
177-
);
174+
me.read_metadata().await?;
175+
if me.durable_generation == 0 {
176+
return Err(SyncError::InvalidLocalState("generation is 0".to_string()).into());
178177
}
179-
180178
Ok(me)
181179
}
182180

@@ -529,6 +527,8 @@ impl SyncContext {
529527
pub(crate) async fn write_metadata(&mut self) -> Result<()> {
530528
let path = format!("{}-info", self.db_path);
531529

530+
assert!(self.durable_generation > 0);
531+
532532
let mut metadata = MetadataJson {
533533
hash: 0,
534534
version: METADATA_VERSION,
@@ -616,8 +616,10 @@ impl SyncContext {
616616
.await
617617
.map_err(SyncError::HttpBody)?;
618618

619-
let info = serde_json::from_slice(&body).map_err(SyncError::JsonDecode)?;
620-
619+
let info: InfoResult = serde_json::from_slice(&body).map_err(SyncError::JsonDecode)?;
620+
if info.current_generation == 0 {
621+
return Err(SyncError::InvalidRemoteState("generation is 0".to_string()).into());
622+
}
621623
Ok(info)
622624
}
623625

libsql/src/sync/test.rs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ async fn test_sync_context_push_frame() {
1414
let server = MockServer::start();
1515
let temp_dir = tempdir().unwrap();
1616
let db_path = temp_dir.path().join("test.db");
17+
gen_metadata_file(&db_path, 3278479626, 0, 0, 1);
1718

1819
let sync_ctx = SyncContext::new(
1920
server.connector(),
@@ -44,6 +45,7 @@ async fn test_sync_context_with_auth() {
4445
let server = MockServer::start();
4546
let temp_dir = tempdir().unwrap();
4647
let db_path = temp_dir.path().join("test.db");
48+
gen_metadata_file(&db_path, 3278479626, 0, 0, 1);
4749

4850
let sync_ctx = SyncContext::new(
4951
server.connector(),
@@ -69,6 +71,7 @@ async fn test_sync_context_multiple_frames() {
6971
let server = MockServer::start();
7072
let temp_dir = tempdir().unwrap();
7173
let db_path = temp_dir.path().join("test.db");
74+
gen_metadata_file(&db_path, 3278479626, 0, 0, 1);
7275

7376
let sync_ctx = SyncContext::new(
7477
server.connector(),
@@ -98,6 +101,7 @@ async fn test_sync_context_corrupted_metadata() {
98101
let server = MockServer::start();
99102
let temp_dir = tempdir().unwrap();
100103
let db_path = temp_dir.path().join("test.db");
104+
gen_metadata_file(&db_path, 3278479626, 0, 0, 1);
101105

102106
// Create initial sync context and push a frame
103107
let sync_ctx = SyncContext::new(
@@ -129,12 +133,9 @@ async fn test_sync_context_corrupted_metadata() {
129133
None,
130134
None,
131135
)
132-
.await
133-
.unwrap();
136+
.await;
134137

135-
// Verify that the context was reset to default values
136-
assert_eq!(sync_ctx.durable_frame_num(), 0);
137-
assert_eq!(sync_ctx.durable_generation(), 0);
138+
assert!(sync_ctx.is_err());
138139
}
139140

140141
#[tokio::test]
@@ -144,6 +145,7 @@ async fn test_sync_restarts_with_lower_max_frame_no() {
144145
let server = MockServer::start();
145146
let temp_dir = tempdir().unwrap();
146147
let db_path = temp_dir.path().join("test.db");
148+
gen_metadata_file(&db_path, 3278479626, 0, 0, 1);
147149

148150
// Create initial sync context and push a frame
149151
let sync_ctx = SyncContext::new(
@@ -211,6 +213,7 @@ async fn test_sync_context_retry_on_error() {
211213
let server = MockServer::start();
212214
let temp_dir = tempdir().unwrap();
213215
let db_path = temp_dir.path().join("test.db");
216+
gen_metadata_file(&db_path, 3278479626, 0, 0, 1);
214217

215218
let sync_ctx = SyncContext::new(
216219
server.connector(),
@@ -475,3 +478,15 @@ impl hyper::client::connect::Connection for MockConnection {
475478
hyper::client::connect::Connected::new()
476479
}
477480
}
481+
482+
fn gen_metadata_file(db_path: &Path, hash: u32, version: u32, durable_frame_num: u32, generation: u32) {
483+
let metadata_path = format!("{}-info", db_path.to_str().unwrap());
484+
std::fs::write(
485+
&metadata_path,
486+
format!(
487+
"{{\"hash\": {hash}, \"version\": {version}, \"durable_frame_num\": {durable_frame_num}, \"generation\": {generation}}}"
488+
)
489+
.as_bytes(),
490+
)
491+
.unwrap();
492+
}

0 commit comments

Comments
 (0)