diff --git a/lib/apps/fabro-server/src/serve.rs b/lib/apps/fabro-server/src/serve.rs index bc90e604e..3e0752c82 100644 --- a/lib/apps/fabro-server/src/serve.rs +++ b/lib/apps/fabro-server/src/serve.rs @@ -783,24 +783,13 @@ where ) .await .context("activating SQLite blob storage")?; - // Refresh tokens and authorization codes now live in SQLite. Nothing reads - // the old records and no reaper collects them any more, so clear them out - // once rather than leaving them in the object store forever. Retiring the - // authorization-code keyspace is fatal on failure; refresh-token cleanup - // stays best-effort. - let (retired_authorization_codes, retired_refresh_tokens) = tokio::join!( - store.retire_authorization_code_keyspace(), - store.retire_refresh_token_keyspace(), - ); - let retired_authorization_codes = - retired_authorization_codes.context("retiring SlateDB authorization code records")?; - if retired_authorization_codes > 0 { - info!( - removed = retired_authorization_codes, - "Removed retired SlateDB authorization code records" - ); - } - match retired_refresh_tokens { + // Refresh tokens now live in SQLite. Nothing reads the old records and no + // reaper collects them any more, so clear them out once rather than + // leaving them in the object store forever. Pending authorization codes + // also moved to SQLite, but their old records are left in place: at most a + // handful exist at cutover, every binary rejects them within 60 seconds of + // issue, and nothing reads their keyspace again. + match store.retire_refresh_token_keyspace().await { Ok(0) => {} Ok(removed) => info!(removed, "Removed retired SlateDB refresh token records"), Err(err) => warn!(error = %err, "Failed to remove retired SlateDB refresh token records"), diff --git a/lib/components/fabro-store/src/slate/mod.rs b/lib/components/fabro-store/src/slate/mod.rs index aac69c659..0e7d48d90 100644 --- a/lib/components/fabro-store/src/slate/mod.rs +++ b/lib/components/fabro-store/src/slate/mod.rs @@ -436,24 +436,10 @@ impl Database { /// nothing else would ever remove them. Returns the number of records /// deleted; a later boot finds the prefix empty and does nothing. pub async fn retire_refresh_token_keyspace(&self) -> Result { - self.retire_keyspace(keys::SlateKey::new("auth").with("refresh")) - .await - } - - /// Delete every record under the retired `auth/code` prefix. - /// - /// Authorization codes move to SQLite without an import. Their short - /// lifetime makes them safe to discard, while deletion prevents an older - /// binary from accepting a code issued before the storage cutover. - /// Returns the number of records deleted; later boots are no-ops. - pub async fn retire_authorization_code_keyspace(&self) -> Result { - self.retire_keyspace(keys::SlateKey::new("auth").with("code")) - .await - } - - async fn retire_keyspace(&self, keyspace: keys::SlateKey) -> Result { let db = self.open_db().await?; - let mut iter = db.scan_prefix(keyspace.into_prefix()).await?; + let mut iter = db + .scan_prefix(keys::SlateKey::new("auth").with("refresh").into_prefix()) + .await?; let mut batch = slatedb::WriteBatch::new(); let mut deletes = 0_u64; while let Some(entry) = iter.next().await? { @@ -584,8 +570,8 @@ mod tests { .as_ref() .to_vec() }); - // "auth/code" sorts adjacent to "auth/refresh" and is still live, so - // it is the neighbour a too-wide prefix delete would take with it. + // "auth/code" sorts adjacent to "auth/refresh", so it is the + // neighbour a too-wide prefix delete would take with it. let auth_code_key = keys::SlateKey::new("auth") .with("code") .with("keep") @@ -610,42 +596,6 @@ mod tests { ); } - #[tokio::test] - async fn retire_authorization_code_keyspace_clears_only_its_prefix_and_is_idempotent() { - let (_object_store, store) = make_store(); - let db = store.open_db().await.unwrap(); - - let authorization_code_keys = ["aaa", "bbb"].map(|id| { - keys::SlateKey::new("auth") - .with("code") - .with(id) - .as_ref() - .to_vec() - }); - let neighboring_key = keys::SlateKey::new("auth") - .with("refresh") - .with("keep") - .as_ref() - .to_vec(); - - let mut batch = slatedb::WriteBatch::new(); - for key in &authorization_code_keys { - batch.put(key.as_slice(), b"{}".as_slice()); - } - batch.put(neighboring_key.as_slice(), b"{}".as_slice()); - db.write(batch).await.unwrap(); - - assert_eq!(store.retire_authorization_code_keyspace().await.unwrap(), 2); - assert_eq!(store.retire_authorization_code_keyspace().await.unwrap(), 0); - for key in &authorization_code_keys { - assert!(db.get(key.as_slice()).await.unwrap().is_none()); - } - assert!( - db.get(neighboring_key.as_slice()).await.unwrap().is_some(), - "retiring authorization codes must not touch neighboring auth prefixes" - ); - } - async fn make_summary_store() -> (tempfile::TempDir, Arc) { let (directory, store) = store_test_support::sqlite_summary_store().await; (directory, Arc::new(store))