Skip to content

Commit 21c7150

Browse files
committed
[sqlite-watcher] introduce wal growth decoder
1 parent 5188e44 commit 21c7150

File tree

3 files changed

+72
-31
lines changed

3 files changed

+72
-31
lines changed

sqlite-watcher/src/decoder.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
use crate::change::RowChange;
2+
use crate::queue::ChangeOperation;
3+
use crate::wal::WalEvent;
4+
use serde_json::json;
5+
use std::time::{SystemTime, UNIX_EPOCH};
6+
7+
/// Temporary decoder that turns WAL growth bytes into placeholder RowChange events.
8+
/// Placeholder until row-level decoding is implemented.
9+
#[derive(Debug, Default, Clone)]
10+
pub struct WalGrowthDecoder;
11+
12+
impl WalGrowthDecoder {
13+
pub fn decode(&self, event: &WalEvent) -> Vec<RowChange> {
14+
let now = SystemTime::now()
15+
.duration_since(UNIX_EPOCH)
16+
.expect("clock should be >= UNIX epoch");
17+
vec![RowChange {
18+
table_name: "__wal__".to_string(),
19+
operation: ChangeOperation::Insert,
20+
primary_key: now.as_nanos().to_string(),
21+
payload: Some(json!({
22+
"kind": "wal_growth",
23+
"bytes_added": event.bytes_added,
24+
"current_size": event.current_size,
25+
"recorded_at": now.as_secs_f64(),
26+
})),
27+
wal_frame: None,
28+
cursor: None,
29+
}]
30+
}
31+
}
32+
33+
#[cfg(test)]
34+
mod tests {
35+
use super::*;
36+
37+
#[test]
38+
fn produces_placeholder_row_change() {
39+
let decoder = WalGrowthDecoder::default();
40+
let rows = decoder.decode(&WalEvent {
41+
bytes_added: 1024,
42+
current_size: 2048,
43+
});
44+
assert_eq!(rows.len(), 1);
45+
assert_eq!(rows[0].table_name, "__wal__");
46+
assert_eq!(rows[0].operation, ChangeOperation::Insert);
47+
}
48+
}

sqlite-watcher/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
pub mod change;
2+
pub mod decoder;
23
pub mod queue;
34
pub mod wal;

sqlite-watcher/src/main.rs

Lines changed: 23 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@ use std::fmt;
22
use std::path::{Path, PathBuf};
33
use std::str::FromStr;
44
use std::sync::mpsc;
5-
use std::time::{Duration, SystemTime, UNIX_EPOCH};
5+
use std::time::Duration;
66

77
use anyhow::{anyhow, bail, Context, Result};
88
use clap::Parser;
9-
use serde_json::json;
10-
use sqlite_watcher::change::RowChange;
11-
use sqlite_watcher::queue::{ChangeOperation, ChangeQueue};
12-
use sqlite_watcher::wal::{start_wal_watcher, WalEvent, WalWatcherConfig as TailConfig};
9+
use sqlite_watcher::decoder::WalGrowthDecoder;
10+
use sqlite_watcher::queue::ChangeQueue;
11+
use sqlite_watcher::wal::{start_wal_watcher, WalWatcherConfig as TailConfig};
1312
use tracing_subscriber::EnvFilter;
1413

1514
#[cfg(unix)]
@@ -219,6 +218,7 @@ fn main() -> Result<()> {
219218
);
220219

221220
let queue = ChangeQueue::open(&config.queue_path)?;
221+
let decoder = WalGrowthDecoder::default();
222222
let (event_tx, event_rx) = mpsc::channel();
223223
let _wal_handle = start_wal_watcher(
224224
&config.database_path,
@@ -230,50 +230,42 @@ fn main() -> Result<()> {
230230
)?;
231231

232232
for event in event_rx {
233-
match persist_wal_event(&queue, &event) {
234-
Ok(change_id) => {
233+
match process_wal_event(&decoder, &queue, &event) {
234+
Ok(change_ids) if !change_ids.is_empty() => {
235235
tracing::info!(
236236
bytes_added = event.bytes_added,
237237
wal_size = event.current_size,
238-
change_id,
238+
change_count = change_ids.len(),
239239
"queued wal growth event"
240240
);
241241
}
242242
Err(err) => {
243243
tracing::warn!(error = %err, "failed to persist wal event to queue");
244244
}
245+
_ => {}
245246
}
246247
}
247248

248249
Ok(())
249250
}
250251

251-
fn persist_wal_event(queue: &ChangeQueue, event: &WalEvent) -> Result<i64> {
252-
let now = SystemTime::now()
253-
.duration_since(UNIX_EPOCH)
254-
.map_err(|err| anyhow!("system clock drift: {err}"))?;
255-
let row = RowChange {
256-
table_name: "__wal__".to_string(),
257-
operation: ChangeOperation::Insert,
258-
primary_key: now.as_nanos().to_string(),
259-
payload: Some(json!({
260-
"kind": "wal_growth",
261-
"bytes_added": event.bytes_added,
262-
"current_size": event.current_size,
263-
"recorded_at": now.as_secs_f64(),
264-
})),
265-
wal_frame: None,
266-
cursor: None,
267-
};
268-
queue.enqueue(&row.into_new_change())
252+
fn process_wal_event(
253+
decoder: &WalGrowthDecoder,
254+
queue: &ChangeQueue,
255+
event: &sqlite_watcher::wal::WalEvent,
256+
) -> Result<Vec<i64>> {
257+
let mut ids = Vec::new();
258+
for row in decoder.decode(event) {
259+
ids.push(queue.enqueue(&row.into_new_change())?);
260+
}
261+
Ok(ids)
269262
}
270263

271264
#[cfg(test)]
272265
mod tests {
273266
use super::*;
274267
use clap::Parser;
275268
use sqlite_watcher::queue::ChangeQueue;
276-
use sqlite_watcher::wal::WalEvent;
277269
use tempfile::{tempdir, NamedTempFile};
278270

279271
#[test]
@@ -316,15 +308,15 @@ mod tests {
316308
let dir = tempdir().unwrap();
317309
let queue_path = dir.path().join("queue.db");
318310
let queue = ChangeQueue::open(&queue_path).unwrap();
311+
let decoder = WalGrowthDecoder::default();
319312

320-
let event = WalEvent {
313+
let event = sqlite_watcher::wal::WalEvent {
321314
bytes_added: 2048,
322315
current_size: 4096,
323316
};
324-
let change_id = persist_wal_event(&queue, &event).unwrap();
317+
let change_ids = process_wal_event(&decoder, &queue, &event).unwrap();
325318
let batch = queue.fetch_batch(10).unwrap();
326-
assert_eq!(batch.len(), 1);
327-
assert_eq!(batch[0].change_id, change_id);
319+
assert_eq!(batch.len(), change_ids.len());
328320
assert_eq!(batch[0].table_name, "__wal__");
329321
}
330322
}

0 commit comments

Comments
 (0)