From b1fd259f108060ae21711d1ae3979b5d1614d7e5 Mon Sep 17 00:00:00 2001 From: Denozordec Date: Thu, 10 Sep 2026 13:36:35 +0700 Subject: [PATCH] =?UTF-8?q?fix(statistics):=20=D1=83=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D1=82=D1=8C=20=D0=BF=D0=B0=D1=80=D0=B0=D0=BC=D0=B5=D1=82=D1=80?= =?UTF-8?q?=D1=8B=20=D1=85=D1=80=D0=B0=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F=20?= =?UTF-8?q?=D1=81=20=D1=80=D0=BE=D0=B4=D0=B8=D1=82=D0=B5=D0=BB=D1=8C=D1=81?= =?UTF-8?q?=D0=BA=D0=B8=D1=85=20=D0=BF=D0=B0=D1=80=D1=82=D0=B8=D1=86=D0=B8?= =?UTF-8?q?=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PostgreSQL 42809: FILLFACTOR и autovacuum нельзя задавать на partitioned parent — только на листьях. Co-authored-by: Cursor --- backend/drizzle/0005_flow_facts.sql | 15 ++------------- backend/src/db/partitions.ts | 9 +++++++++ 2 files changed, 11 insertions(+), 13 deletions(-) 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 }