fix: periodic star recount every 5 minutes During backfill, stars arrive before repos so increment_repo_star_count misses. The recount task recomputes star_count from the stars table for any repo where the count is wrong.

Author: Aaron Steven White
Commit ef94763fe68c0c3e76e05205a256c5de8ef91e32
Parent: 71c9b0e8dc
Structural diff unavailable

These commits were pushed via plain git push, so no pre-parsed schemas are available. Install git-remote-cospan and re-push via panproto:// to see scope-level changes, breaking change detection, and semantic diffs.

brew install panproto/tap/git-remote-cospan
2 files changed +38 -0
@@ -40,6 +40,27 @@ pub async fn decrement_repo_star_count(
4040     Ok(())
4141 }
4242 
43+/// Recompute all star counts from the stars table.
44+/// Handles backfill ordering where stars arrive before repos.
45+pub async fn recount_all_stars(pool: &PgPool) -> Result<u64, sqlx::Error> {
46+    let result = sqlx::query(
47+        "UPDATE repos SET star_count = COALESCE(s.cnt, 0) \
48+         FROM ( \
49+             SELECT \
50+                 split_part(replace(subject, 'at://', ''), '/', 1) AS repo_did, \
51+                 split_part(replace(subject, 'at://', ''), '/', 3) AS repo_rkey, \
52+                 COUNT(*) AS cnt \
53+             FROM stars \
54+             GROUP BY 1, 2 \
55+         ) s \
56+         WHERE repos.did = s.repo_did AND repos.rkey = s.repo_rkey \
57+         AND repos.star_count <> s.cnt",
58+    )
59+    .execute(pool)
60+    .await?;
61+    Ok(result.rows_affected())
62+}
63+
4364 pub async fn list_by_user(
4465     pool: &PgPool,
4566     did: &str,
@@ -77,6 +77,23 @@ async fn main() -> anyhow::Result<()> {
7777         }
7878     });
7979 
80+    // Periodic star recount (handles backfill ordering)
81+    let recount_pool = state.db.clone();
82+    tokio::spawn(async move {
83+        loop {
84+            tokio::time::sleep(std::time::Duration::from_secs(300)).await;
85+            match cospan_appview::db::star::recount_all_stars(&recount_pool).await {
86+                Ok(updated) if updated > 0 => {
87+                    tracing::info!(updated, "star recount complete");
88+                }
89+                Ok(_) => {}
90+                Err(e) => {
91+                    tracing::warn!(error = %e, "star recount failed");
92+                }
93+            }
94+        }
95+    });
96+
8097     // Build and start the HTTP server
8198     let app = cospan_appview::xrpc::router(state);
8299     let listener = tokio::net::TcpListener::bind(&config.listen).await?;
cospan · schematic version control on atproto built on AT Protocol