max / goingson
4 files changed,
+19 insertions,
-18 deletions
| @@ -15,7 +15,7 @@ use chrono::Utc; | |||
| 15 | 15 | use goingson_core::CoreError; | |
| 16 | 16 | use sqlx::SqlitePool; | |
| 17 | 17 | use std::collections::{HashMap, HashSet}; | |
| 18 | - | use synckit_client::Hlc; | |
| 18 | + | use synckit_client::{DeviceId, Hlc}; | |
| 19 | 19 | use uuid::Uuid; | |
| 20 | 20 | ||
| 21 | 21 | /// Load the persistent clock, reattaching `node` (which is not stored). | |
| @@ -25,7 +25,7 @@ async fn load_clock(pool: &SqlitePool, node: Uuid) -> Result<Hlc, CoreError> { | |||
| 25 | 25 | .fetch_one(pool) | |
| 26 | 26 | .await | |
| 27 | 27 | .map_err(CoreError::database)?; | |
| 28 | - | Ok(Hlc { wall_ms, counter: counter as u32, node }) | |
| 28 | + | Ok(Hlc { wall_ms, counter: counter as u32, node: DeviceId::new(node) }) | |
| 29 | 29 | } | |
| 30 | 30 | ||
| 31 | 31 | /// Stamp every unpushed changelog row that lacks an HLC, in id order, advancing the | |
| @@ -53,10 +53,11 @@ pub async fn assign_pending_hlcs(pool: &SqlitePool, device_id: Uuid) -> Result<( | |||
| 53 | 53 | .fetch_one(&mut *tx) | |
| 54 | 54 | .await | |
| 55 | 55 | .map_err(CoreError::database)?; | |
| 56 | - | let mut clock = Hlc { wall_ms, counter: counter as u32, node: device_id }; | |
| 56 | + | let node = DeviceId::new(device_id); | |
| 57 | + | let mut clock = Hlc { wall_ms, counter: counter as u32, node }; | |
| 57 | 58 | ||
| 58 | 59 | for (id, table_name, row_id) in ids { | |
| 59 | - | clock = Hlc::tick(clock, Utc::now().timestamp_millis(), device_id); | |
| 60 | + | clock = Hlc::tick(clock, Utc::now().timestamp_millis(), node); | |
| 60 | 61 | sqlx::query("UPDATE sync_changelog SET hlc_wall = ?, hlc_counter = ? WHERE id = ?") | |
| 61 | 62 | .bind(clock.wall_ms) | |
| 62 | 63 | .bind(clock.counter as i64) | |
| @@ -86,7 +87,7 @@ pub async fn assign_pending_hlcs(pool: &SqlitePool, device_id: Uuid) -> Result<( | |||
| 86 | 87 | #[tracing::instrument(skip_all)] | |
| 87 | 88 | pub async fn observe_remote(pool: &SqlitePool, remote: Hlc, device_id: Uuid) -> Result<(), CoreError> { | |
| 88 | 89 | let clock = load_clock(pool, device_id).await?; | |
| 89 | - | let advanced = Hlc::observe(clock, remote, Utc::now().timestamp_millis(), device_id); | |
| 90 | + | let advanced = Hlc::observe(clock, remote, Utc::now().timestamp_millis(), DeviceId::new(device_id)); | |
| 90 | 91 | sqlx::query("UPDATE hlc_state SET wall_ms = ?, counter = ? WHERE id = 1") | |
| 91 | 92 | .bind(advanced.wall_ms) | |
| 92 | 93 | .bind(advanced.counter as i64) | |
| @@ -168,7 +169,7 @@ pub(crate) async fn load_committed_hlcs( | |||
| 168 | 169 | { | |
| 169 | 170 | map.insert( | |
| 170 | 171 | (table, row_id), | |
| 171 | - | Hlc { wall_ms: wall, counter: counter as u32, node }, | |
| 172 | + | Hlc { wall_ms: wall, counter: counter as u32, node: DeviceId::new(node) }, | |
| 172 | 173 | ); | |
| 173 | 174 | } | |
| 174 | 175 | } |
| @@ -167,8 +167,8 @@ async fn read_local_pending(pool: &SqlitePool, device_id: Uuid) -> Result<Vec<Ch | |||
| 167 | 167 | let json_data = data.and_then(|d| serde_json::from_str(&d).ok()); | |
| 168 | 168 | ||
| 169 | 169 | let hlc = match (hlc_wall, hlc_counter) { | |
| 170 | - | (Some(wall), Some(counter)) => Hlc { wall_ms: wall, counter: counter as u32, node: device_id }, | |
| 171 | - | _ => Hlc::from_legacy(ts.timestamp_millis(), device_id), | |
| 170 | + | (Some(wall), Some(counter)) => Hlc { wall_ms: wall, counter: counter as u32, node: DeviceId::new(device_id) }, | |
| 171 | + | _ => Hlc::from_legacy(ts.timestamp_millis(), DeviceId::new(device_id)), | |
| 172 | 172 | }; | |
| 173 | 173 | ||
| 174 | 174 | Some(ChangeEntry { |
| @@ -64,8 +64,8 @@ pub async fn push_changes( | |||
| 64 | 64 | // Stamped by assign_pending_hlcs above; fall back to a | |
| 65 | 65 | // timestamp-derived HLC if a row was somehow left unstamped. | |
| 66 | 66 | let hlc = match (hlc_wall, hlc_counter) { | |
| 67 | - | (Some(wall), Some(counter)) => Hlc { wall_ms: wall, counter: counter as u32, node: device_id }, | |
| 68 | - | _ => Hlc::from_legacy(ts.timestamp_millis(), device_id), | |
| 67 | + | (Some(wall), Some(counter)) => Hlc { wall_ms: wall, counter: counter as u32, node: DeviceId::new(device_id) }, | |
| 68 | + | _ => Hlc::from_legacy(ts.timestamp_millis(), DeviceId::new(device_id)), | |
| 69 | 69 | }; | |
| 70 | 70 | ||
| 71 | 71 | Some(ChangeEntry { |
| @@ -45,7 +45,7 @@ use sqlx::SqlitePool; | |||
| 45 | 45 | op, | |
| 46 | 46 | row_id: row_id.to_string(), | |
| 47 | 47 | timestamp: chrono::Utc::now(), | |
| 48 | - | hlc: Default::default(), | |
| 48 | + | hlc: synckit_client::Hlc::zero(synckit_client::DeviceId::nil()), | |
| 49 | 49 | data, | |
| 50 | 50 | extra: Default::default(), | |
| 51 | 51 | } | |
| @@ -177,8 +177,8 @@ use sqlx::SqlitePool; | |||
| 177 | 177 | ||
| 178 | 178 | let pool = setup_test_db().await; | |
| 179 | 179 | let node = uuid::Uuid::new_v4(); | |
| 180 | - | let lo = Hlc { wall_ms: 100, counter: 0, node }; | |
| 181 | - | let hi = Hlc { wall_ms: 200, counter: 0, node }; | |
| 180 | + | let lo = Hlc { wall_ms: 100, counter: 0, node: synckit_client::DeviceId::new(node) }; | |
| 181 | + | let hi = Hlc { wall_ms: 200, counter: 0, node: synckit_client::DeviceId::new(node) }; | |
| 182 | 182 | let key = ("tasks".to_string(), "r1".to_string()); | |
| 183 | 183 | ||
| 184 | 184 | record_committed_hlc(&pool, "tasks", "r1", lo).await.unwrap(); | |
| @@ -1918,7 +1918,7 @@ use sqlx::SqlitePool; | |||
| 1918 | 1918 | .unwrap_or_else(|| panic!("no committed HLC for {row_id}")); | |
| 1919 | 1919 | assert_eq!(hlc.wall_ms, *wall); | |
| 1920 | 1920 | assert_eq!(hlc.counter as i64, *ctr); | |
| 1921 | - | assert_eq!(hlc.node, device_id, "committed HLC carries this device's node"); | |
| 1921 | + | assert_eq!(hlc.node.as_uuid(), device_id, "committed HLC carries this device's node"); | |
| 1922 | 1922 | } | |
| 1923 | 1923 | ||
| 1924 | 1924 | // The persistent clock advanced exactly to the last stamp. | |
| @@ -1952,7 +1952,7 @@ use sqlx::SqlitePool; | |||
| 1952 | 1952 | let annotation_id = uuid::Uuid::new_v4().to_string(); | |
| 1953 | 1953 | let now = now_sql(); | |
| 1954 | 1954 | let node = uuid::Uuid::new_v4(); | |
| 1955 | - | let hlc = Hlc { wall_ms: 5000, counter: 2, node }; | |
| 1955 | + | let hlc = Hlc { wall_ms: 5000, counter: 2, node: synckit_client::DeviceId::new(node) }; | |
| 1956 | 1956 | ||
| 1957 | 1957 | let task_data = json!({ | |
| 1958 | 1958 | "id": task_id, "project_id": null, "description": "Idempotent", | |
| @@ -2031,17 +2031,17 @@ use sqlx::SqlitePool; | |||
| 2031 | 2031 | let node = uuid::Uuid::new_v4(); | |
| 2032 | 2032 | ||
| 2033 | 2033 | // An honest local write at ~now is never flagged. | |
| 2034 | - | let local = Hlc { wall_ms: now_ms, counter: 0, node }; | |
| 2034 | + | let local = Hlc { wall_ms: now_ms, counter: 0, node: synckit_client::DeviceId::new(node) }; | |
| 2035 | 2035 | assert!(!is_clock_poisoned(&local, now), "an at-now HLC must not be poisoned"); | |
| 2036 | 2036 | ||
| 2037 | 2037 | // Just inside the drift window is still honest (inter-device skew tolerated). | |
| 2038 | - | let within = Hlc { wall_ms: now_ms + MAX_HLC_DRIFT_MS - 1_000, counter: 0, node }; | |
| 2038 | + | let within = Hlc { wall_ms: now_ms + MAX_HLC_DRIFT_MS - 1_000, counter: 0, node: synckit_client::DeviceId::new(node) }; | |
| 2039 | 2039 | assert!(!is_clock_poisoned(&within, now), "skew within the drift cap is allowed"); | |
| 2040 | 2040 | ||
| 2041 | 2041 | // A remote HLC far beyond the drift cap is poisoned. It sorts ABOVE the local | |
| 2042 | 2042 | // write, so it would win raw LWW ordering -- but the poison guard makes it lose, | |
| 2043 | 2043 | // so the local value survives. | |
| 2044 | - | let poisoned = Hlc { wall_ms: now_ms + MAX_HLC_DRIFT_MS + 60_000, counter: 0, node }; | |
| 2044 | + | let poisoned = Hlc { wall_ms: now_ms + MAX_HLC_DRIFT_MS + 60_000, counter: 0, node: synckit_client::DeviceId::new(node) }; | |
| 2045 | 2045 | assert!(is_clock_poisoned(&poisoned, now), "a far-future remote HLC must be flagged"); | |
| 2046 | 2046 | assert!(poisoned > local, "the poisoned HLC would otherwise win raw LWW ordering"); | |
| 2047 | 2047 | } |