diff --git a/backend/drizzle/0005_flow_facts.sql b/backend/drizzle/0005_flow_facts.sql index d352fe2..577a2f7 100644 --- a/backend/drizzle/0005_flow_facts.sql +++ b/backend/drizzle/0005_flow_facts.sql @@ -1,5 +1,6 @@ -- Statistics cube: hour + daily facts (server × iface × country × service × ASN). --- Compact types, fillfactor for HOT upserts, autovacuum tuned for ON CONFLICT. +-- Compact types. FILLFACTOR/autovacuum нельзя на partitioned parent (PG 42809) — +-- задаются на листовых партициях в ensurePartitionFor. -- Retention: DROP partitions only (see PARTITION_SPECS). CREATE TABLE IF NOT EXISTS flow_hour_facts ( @@ -14,12 +15,6 @@ CREATE TABLE IF NOT EXISTS flow_hour_facts ( PRIMARY KEY (server_id, bucket_at, iface, country, service, asn) ) PARTITION BY RANGE (bucket_at); -ALTER TABLE flow_hour_facts SET ( - fillfactor = 70, - autovacuum_vacuum_scale_factor = 0.05, - autovacuum_vacuum_cost_limit = 2000 -); - CREATE TABLE IF NOT EXISTS flow_daily_facts ( server_id BIGINT NOT NULL REFERENCES servers(id) ON DELETE CASCADE, day DATE NOT NULL, @@ -31,9 +26,3 @@ CREATE TABLE IF NOT EXISTS flow_daily_facts ( packets BIGINT NOT NULL DEFAULT 0, PRIMARY KEY (server_id, day, iface, country, service, asn) ) PARTITION BY RANGE (day); - -ALTER TABLE flow_daily_facts SET ( - fillfactor = 70, - autovacuum_vacuum_scale_factor = 0.05, - autovacuum_vacuum_cost_limit = 2000 -); diff --git a/backend/src/db/partitions.ts b/backend/src/db/partitions.ts index 0333661..d46d51b 100644 --- a/backend/src/db/partitions.ts +++ b/backend/src/db/partitions.ts @@ -8,6 +8,12 @@ export interface PartitionSpec { keepDays: number } +/** Leaf-only: PG forbids storage params on partitioned parents (SQLSTATE 42809). */ +const FACT_LEAF_STORAGE = + "fillfactor = 70, autovacuum_vacuum_scale_factor = 0.05, autovacuum_vacuum_cost_limit = 2000" + +const FACT_PARENTS = new Set(["flow_hour_facts", "flow_daily_facts"]) + export const PARTITION_SPECS: PartitionSpec[] = [ { parent: "flow_buckets", kind: "day", keepDays: 4 }, { parent: "flow_minute_stats", kind: "day", keepDays: 4 }, @@ -112,6 +118,9 @@ export async function ensurePartitionFor( await pool.query( `CREATE TABLE IF NOT EXISTS ${name} PARTITION OF ${parent} FOR VALUES FROM ('${from}') TO ('${to}')`, ) + if (FACT_PARENTS.has(parent)) { + await pool.query(`ALTER TABLE ${name} SET (${FACT_LEAF_STORAGE})`) + } return name }