From a226a92c120cb2a74b7c3daaaa83915e26729096 Mon Sep 17 00:00:00 2001 From: tomdebone Date: Tue, 7 Jul 2026 21:50:47 +0200 Subject: [PATCH] =?UTF-8?q?fix(appview):=20skip=20did:key=20in=20handle-sy?= =?UTF-8?q?nc=20SQL=20=E2=80=94=20they=20blocked=20progress?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The handle-sync worker picks the next BATCH_SIZE=100 distinct DIDs with 'handle = \"\"' via 'ORDER BY did LIMIT 100'. But 'alphabetically' (which is what ORDER BY on a text column produces) puts 'did:key:' before 'did:plc:' before 'did:web:'. The 'dispatch' function returns 'Ok(None)' for 'did:key:' (no resolver exists), counts that as 'skipped', and exits the batch. Result: every pass processes the same ~3700 'did:key:' rows first and never reaches any resolvable 'did:plc:' DID. Fix at the SQL layer: 'WHERE did LIKE \"'did:plc:%\"' OR did LIKE \"'did:web:%\"' \"'\") so every batch is real work. After the first run on a fresh start, 'resolved=254 failed=0 skipped=0' instead of 'resolved=0 failed=0 skipped=100'. --- crates/appview/src/handle_sync.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/crates/appview/src/handle_sync.rs b/crates/appview/src/handle_sync.rs index b72ad0d..45e62c4 100644 --- a/crates/appview/src/handle_sync.rs +++ b/crates/appview/src/handle_sync.rs @@ -112,11 +112,22 @@ impl HandleSyncWorker { /// One bounded scan: find up to [`BATCH_SIZE`] distinct DIDs whose /// posts have an empty handle, resolve them, and update the rows /// where the handle is still empty (race-safe). + /// + /// **SQL-level filter**: we exclude `did:key:` entirely because + /// there's no resolver path for them — the PLC directory and the + /// `did:web:` HTTPS resolver both reject non-`did:plc:` / + /// non-`did:web:` DIDs with `Ok(None)`. Previously the worker + /// picked via `ORDER BY did LIMIT 100`, but lexicographically + /// `did:key:` sorts before `did:plc:` / `did:web:`, so the worker + /// would process the same 100 `did:key:` rows every 300 s and + /// never reach any resolvable DID. Filtering at SQL time makes + /// every batch contribute real work. pub async fn run_once(&self) -> Result { let dids: Vec<(String,)> = sqlx::query_as( r#"SELECT DISTINCT did FROM posts WHERE handle = '' + AND (did LIKE 'did:plc:%' OR did LIKE 'did:web:%') ORDER BY did LIMIT $1"#, )