From 37d018a7a5d7ca1a58b4970b763e16505cf4b756 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Mar 2026 06:47:16 +0000 Subject: [PATCH 1/4] Initial plan From 998f0e48bc03e079eee83ac8e500956c19b41088 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Mar 2026 06:55:11 +0000 Subject: [PATCH 2/4] fix: make v9 migration explicitly additive and non-idempotent Co-authored-by: bansalayush247 <116891199+bansalayush247@users.noreply.github.com> Agent-Logs-Url: https://github.com/bansalayush247/fedimint-observer/sessions/7b630b56-bb78-4539-b6a4-7d0091421484 --- fmo_server/schema/v9.sql | 4 +++- fmo_server/src/federation/observer.rs | 10 ++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/fmo_server/schema/v9.sql b/fmo_server/schema/v9.sql index b262f74..7d4af1d 100644 --- a/fmo_server/schema/v9.sql +++ b/fmo_server/schema/v9.sql @@ -5,7 +5,9 @@ INSERT INTO VALUES (9); -CREATE TABLE IF NOT EXISTS gateways ( +-- V9 is intentionally additive: it creates a new table and indexes only. +-- It must not modify or drop any existing production tables. +CREATE TABLE gateways ( federation_id BYTEA NOT NULL REFERENCES federations (federation_id), gateway_id TEXT NOT NULL, node_pub_key TEXT NOT NULL, diff --git a/fmo_server/src/federation/observer.rs b/fmo_server/src/federation/observer.rs index 432177d..f0a5cef 100644 --- a/fmo_server/src/federation/observer.rs +++ b/fmo_server/src/federation/observer.rs @@ -1620,4 +1620,14 @@ mod tests { assert_eq!(last_7_days[6], now); assert_eq!(last_7_days[0], now - chrono::Duration::days(6)); } + + #[test] + fn v9_schema_is_additive_only() { + let migration = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/schema/v9.sql")); + + assert!(migration.contains("CREATE TABLE gateways")); + assert!(!migration.contains("ALTER TABLE")); + assert!(!migration.contains("DROP TABLE")); + assert!(!migration.contains("CREATE TABLE IF NOT EXISTS gateways")); + } } From a507e1000a333d661e73300ef52affb585984102 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Mar 2026 10:38:52 +0000 Subject: [PATCH 3/4] docs: explain what database migrations do Co-authored-by: bansalayush247 <116891199+bansalayush247@users.noreply.github.com> Agent-Logs-Url: https://github.com/bansalayush247/fedimint-observer/sessions/bc082a67-d8ae-4d0f-bc30-17fdcc417538 --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 1127c01..a396d1c 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,20 @@ When it comes to stability guarantees the Postgres database should always be mig don't lose historic data. The API under `/federations` isn't stable at this point and I'd recommend to subscribing to changes in Fedimint Observer if building against it. +### What database migrations actually do +On startup the backend runs schema setup/migrations in order (`v0.sql`, `v1.sql`, …). + +1. It checks the current DB version via `schema_version`. +2. It runs every migration newer than the current version inside a SQL transaction. +3. For migrations that need data reshaping, it runs a Rust backfill procedure before commit. + +There are three migration kinds in code: +* `schema_setup`: only for fresh DB setup, not allowed when upgrading an already populated DB. +* `migration`: pure SQL migration that can run for setup and upgrades. +* `migration_backfill`: SQL + Rust backfill for safe upgrades. + +Recent migrations (`v7+`) are applied for normal upgrades. `v9` is additive and creates a new `gateways` table plus indexes; it does not alter/drop existing tables. + ## Federation Inspector The lesser-known component is an API under the `/config` path it can be used to get a JSON-encoded version of the federation config if you have an invite code. The first time it fetches the config from the federation using the invite From c60d04fd970067f2f59cf92286432ec24185fa7b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Mar 2026 10:43:42 +0000 Subject: [PATCH 4/4] test/docs: clarify migration behavior and harden v9 assertions Co-authored-by: bansalayush247 <116891199+bansalayush247@users.noreply.github.com> Agent-Logs-Url: https://github.com/bansalayush247/fedimint-observer/sessions/bc082a67-d8ae-4d0f-bc30-17fdcc417538 --- README.md | 2 +- fmo_server/src/federation/observer.rs | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index a396d1c..eecc3c2 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ There are three migration kinds in code: * `migration`: pure SQL migration that can run for setup and upgrades. * `migration_backfill`: SQL + Rust backfill for safe upgrades. -Recent migrations (`v7+`) are applied for normal upgrades. `v9` is additive and creates a new `gateways` table plus indexes; it does not alter/drop existing tables. +In the current code path, `v0`-`v6` are marked as `schema_setup` (fresh setup only), while `v7`, `v8`, and `v9` are upgrade-capable migrations for existing databases. `v9` is additive and creates a new `gateways` table plus indexes; it does not alter/drop existing tables. ## Federation Inspector The lesser-known component is an API under the `/config` path it can be used to get a JSON-encoded version of the diff --git a/fmo_server/src/federation/observer.rs b/fmo_server/src/federation/observer.rs index f0a5cef..a5e22f9 100644 --- a/fmo_server/src/federation/observer.rs +++ b/fmo_server/src/federation/observer.rs @@ -1623,11 +1623,13 @@ mod tests { #[test] fn v9_schema_is_additive_only() { - let migration = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/schema/v9.sql")); - - assert!(migration.contains("CREATE TABLE gateways")); - assert!(!migration.contains("ALTER TABLE")); - assert!(!migration.contains("DROP TABLE")); - assert!(!migration.contains("CREATE TABLE IF NOT EXISTS gateways")); + let migration = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/schema/v9.sql")).to_lowercase(); + + assert!(migration.contains("create table gateways")); + assert!(migration.contains("create index if not exists gateways_federation_id")); + assert!(migration.contains("create index if not exists gateways_node_pub_key")); + assert!(!migration.contains("alter table")); + assert!(!migration.contains("drop table")); + assert!(!migration.contains("create table if not exists gateways")); } }