Skip to content

sync: persist Catalog and banlists to Postgres #2

Description

@jeff-arn

Context

The sync layer in src/sync/fab_cube.rs currently produces an in-memory
Catalog plus Vec<BannedEntry> and Vec<LivingLegendEntry>. Every
process restart re-fetches/re-parses the upstream JSON. There is no DB row
type, no migration, no sqlx queries for cards/printings/banlists.

Why deferred

Substantial slice (estimated 2-4 hours) — schema design, 5+ migrations,
db/cards.rs row types, sqlx-checked queries, the .sqlx cache committed
to repo, integration tests against a real Postgres. Today the in-memory
catalog is plenty fast (HashMap lookups over ~14k printings) and we have no
API endpoints reading it under concurrent load.

When to do this

When at least one of the following is true:

  • Validation API endpoint exists and serves traffic (need persistent shared
    state, fast lookups under concurrency)
  • Multi-instance deployment where each instance should not independently
    hold 4k+ cards in memory
  • We need historical card data for queries the in-memory catalog cannot
    answer efficiently

Scope

  • New tables: cards, printings, card_banned_entries (per format),
    card_living_legend_entries (per format), sets
  • Migrations following .claude/rules/database.md conventions (snake_case
    plural tables, id uuid PKs, timestamptz timestamps, FK indexes)
  • Row structs in src/db/cards.rs mirroring schema, with From<Row> for
    domain conversions at the db/ boundary
  • Idempotent upsert logic — the sync runs in a transaction and is no-op
    when run twice with no upstream changes
  • sqlx queries via query!/query_as! macros, .sqlx cache committed

Pointers

  • Sync producer: src/sync/fab_cube.rs::build_from_json returns
    SyncOutput { catalog, cc_banned, cc_living_legend, ... }
  • Conventions: .claude/rules/database.md
  • Architectural memory: project_architectural_constraints.md — printing-level identity is load-bearing, do not collapse

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions