Back to feed

sledtools/pika branch #75

pika-git-mirror-store-1

Extract mirror persistence into mirror_store

Target branch: master

Merge Commit: 28357f9e3fe2a621e1ce7116471f18bb10910978

branch: merged tutorial: ready ci: success
Open CI Details

Continuous Integration

CI: success

Compact status on the review page, with full logs on the CI page.

Open CI Details

Latest run #94 success

2 passed

head 2ad44e0dce443f047fbc219d85ad383ddd94486a · queued 2026-03-25 21:19:52 · 2 lane(s)

queued 1m 25s · ran 25s

check-notifications · success check-agent-contracts · success

Summary

This branch extracts all mirror-persistence related types, database methods, helper functions, and unit tests from the monolithic branch_store.rs into a dedicated mirror_store.rs module. The three data structures (MirrorSyncRunRecord, MirrorStatusRecord, MirrorSyncRunInput), the three Store impl methods (record_mirror_sync_run, get_mirror_status, list_recent_mirror_sync_runs), two private helpers (map_mirror_sync_row, classify_mirror_failure_kind), and the associated test are moved verbatim. Import paths across forge_runtime.rs, mirror.rs, web.rs, and main.rs are updated to reference the new module. One minor behavioral refinement is made to record_mirror_sync_run: the repo-metadata lookup is hoisted out of the with_connection closure via a new ensure_forge_repo_metadata public helper on Store, separating the metadata step from the insert transaction.

Tutorial Steps

Remove mirror types and methods from branch_store.rs

Intent: Eliminate the three mirror-related data structs (`MirrorSyncRunRecord`, `MirrorStatusRecord`, `MirrorSyncRunInput`), the three `Store` impl methods (`record_mirror_sync_run`, `get_mirror_status`, `list_recent_mirror_sync_runs`), the two private helpers (`map_mirror_sync_row`, `classify_mirror_failure_kind`), and the unit test from the branch store module so they can live in their own dedicated module.

Affected files: crates/pika-news/src/branch_store.rs

Evidence
@@ -209,48 +209,6 @@ pub struct PendingNightlyLaneJob {
@@ -1571,174 +1529,6 @@ impl Store {
@@ -3236,62 +3026,6 @@ fn update_target_health_after_lane_finish(
@@ -3436,7 +3170,7 @@ fn update_nightly_run_status(conn: &Connection, nightly_run_id: i64) -> anyhow::
@@ -3530,38 +3264,6 @@ mod tests {

The bulk of this branch is a deletion pass across branch_store.rs. Three struct definitions totalling ~42 lines are removed from the top of the file (lines 212–253 in the old file). Three impl Store methods spanning ~174 lines are cut from the middle (record_mirror_sync_run, get_mirror_status, list_recent_mirror_sync_runs). Two private free functions (map_mirror_sync_row and classify_mirror_failure_kind, ~62 lines) are removed from near the bottom. Finally, the classifies_mirror_failure_kinds_for_runtime_safety_cases test and its classify_mirror_failure_kind import are deleted from the mod tests block.

Nothing else in branch_store.rs is modified beyond these removals and adjusting the test-module import line:

// Before
use super::{classify_mirror_failure_kind, BranchUpsertInput, CI_LANE_LEASE_LOST};
// After
use super::{BranchUpsertInput, CI_LANE_LEASE_LOST};

Create the new mirror_store.rs module

Intent: Provide a focused, self-contained module that owns all mirror-persistence concerns: data types, database queries, row-mapping helpers, failure classification logic, and their unit tests.

Affected files: crates/pika-news/src/mirror_store.rs

Evidence
@@ -0,0 +1,313 @@

A new file mirror_store.rs (313 lines) is created containing everything removed from branch_store.rs:

  1. Importsanyhow::Context, rusqlite::{params, OptionalExtension}, and crate::storage::Store.
  2. Three structsMirrorSyncRunRecord, MirrorStatusRecord, and MirrorSyncRunInput, copied verbatim.
  3. impl Store block with the three public methods:
    • record_mirror_sync_run — records a new sync run row. One notable change: the repo-metadata lookup now calls self.ensure_forge_repo_metadata(...) before entering with_connection, rather than calling the module-private ensure_repo_metadata inside the connection closure. This decouples the metadata upsert from the insert transaction.
    • get_mirror_status — aggregates the latest attempt, last success/failure timestamps, and consecutive-failure streak for a given remote.
    • list_recent_mirror_sync_runs — returns the N most recent sync runs for a remote.
  4. map_mirror_sync_row — maps a rusqlite::Row into MirrorSyncRunRecord, calling classify_mirror_failure_kind to populate failure_kind.
  5. classify_mirror_failure_kind — pattern-matches error text into categories: obsolete, stale, busy, timeout, config, or transient.
  6. #[cfg(test)] mod tests — the classifies_mirror_failure_kinds_for_runtime_safety_cases test verifying the classifier against four representative error strings.
// New standalone impl block in mirror_store.rs
impl Store {
    pub fn record_mirror_sync_run(&self, input: &MirrorSyncRunInput) -> anyhow::Result<i64> {
        let repo_id = self.ensure_forge_repo_metadata(
            &input.repo,
            &input.canonical_git_dir,
            &input.default_branch,
            "ci/forge-lanes.toml",
        )?;
        self.with_connection(|conn| { /* INSERT ... */ })
    }
    // ... get_mirror_status, list_recent_mirror_sync_runs
}

Register the new module in main.rs

Intent: Make the Rust compiler aware of `mirror_store` so other modules can import from it.

Affected files: crates/pika-news/src/main.rs

Evidence
@@ -12,6 +12,7 @@ mod github;
 mod live;
 mod local;
 mod mirror;
+mod mirror_store;
 mod model;

A single line mod mirror_store; is added to main.rs in alphabetical order between mod mirror and mod model. This is the only change needed to wire the new module into the crate's module tree.

Update imports in forge_runtime.rs

Intent: Point the forge runtime's mirror-status import at the new module instead of branch_store.

Affected files: crates/pika-news/src/forge_runtime.rs

Evidence
@@ -6,12 +6,12 @@ use std::time::Duration;
 use chrono::{SecondsFormat, Utc};
 use tokio::sync::Notify;
 
-use crate::branch_store::MirrorStatusRecord;
 use crate::ci;
 use crate::config::{Config, ForgeRepoConfig};
 use crate::forge;
 use crate::live::CiLiveUpdates;
 use crate::mirror;
+use crate::mirror_store::MirrorStatusRecord;
 use crate::poller;

In forge_runtime.rs, the import of MirrorStatusRecord switches from crate::branch_store to crate::mirror_store. The import is also repositioned alphabetically among the use statements.

Update imports in mirror.rs

Intent: Redirect the mirror module's type imports to mirror_store, since it consumes all three mirror data types.

Affected files: crates/pika-news/src/mirror.rs

Evidence
@@ -3,9 +3,9 @@ use std::env;
 use anyhow::Context;
 use chrono::{DateTime, NaiveDateTime, Utc};
 
-use crate::branch_store::{MirrorStatusRecord, MirrorSyncRunInput, MirrorSyncRunRecord};
 use crate::config::Config;
 use crate::forge;
+use crate::mirror_store::{MirrorStatusRecord, MirrorSyncRunInput, MirrorSyncRunRecord};

mirror.rs is the primary consumer of all three mirror types. The single grouped import line is moved from crate::branch_store to crate::mirror_store, keeping MirrorStatusRecord, MirrorSyncRunInput, and MirrorSyncRunRecord in the same import.

Update imports in web.rs tests

Intent: Fix the test module's imports so mirror types resolve from the new module.

Affected files: crates/pika-news/src/web.rs

Evidence
@@ -4254,7 +4254,7 @@ mod tests {
         ForgePikaciLogsQuery, InboxListParams, PageNoticeView, ReviewModeQuery,
     };
     use crate::auth::AuthState;
-    use crate::branch_store::{BranchUpsertInput, MirrorStatusRecord, MirrorSyncRunRecord};
+    use crate::branch_store::BranchUpsertInput;
@@ -4264,6 +4264,7 @@ mod tests {
     use crate::forge_service::ForgeService;
     use crate::mirror::MirrorRuntimeStatus;
+    use crate::mirror_store::{MirrorStatusRecord, MirrorSyncRunRecord};

In the web.rs test module, the combined import from branch_store is split: BranchUpsertInput stays with crate::branch_store, while MirrorStatusRecord and MirrorSyncRunRecord move to a new use crate::mirror_store line. This is the last consumer that needed updating.

Diff