From a85e7ed4c1914887ca6a07d1245f79700a399646 Mon Sep 17 00:00:00 2001 From: Otto V Date: Wed, 2 Sep 2026 23:05:25 +0200 Subject: [PATCH] perf: stop mint-breakdown scanning mod_to_acct_transfers; add transactions ordering index get_mint_breakdown_between_dates summed reimbursement from the raw mod_to_acct_transfers table (~2.9 B rows on mainnet). The only index on op_reason is single-column, so every call heap-fetched all ~18 M reimbursement rows (~21 GB of disk reads, ~30 s) regardless of the date window. The same escrow amount is already recorded as a synthetic opReason 10 mint on event_claim_settleds, so the raw-table branch was redundant and double counted in the reward_distribution_detailed era. reimbursement is now derived from event_claim_settleds.mints only. Add transactions_block_id_desc_live_idx, a covering partial btree on transactions (block_id DESC, _id) INCLUDE (_block_range) WHERE id IS NOT NULL, so the PostGraphile transactions connection (ORDER BY block_id DESC LIMIT n + count(*)) no longer parallel seq scans the 13 GB heap twice per call. Created CONCURRENTLY and IF NOT EXISTS via the existing performance-index mechanism; it already exists on mainnet. --- src/mappings/dbFunctions/domainRewards.ts | 9 +++++++ src/mappings/dbFunctions/supply.ts | 29 ++++++++++++----------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/mappings/dbFunctions/domainRewards.ts b/src/mappings/dbFunctions/domainRewards.ts index dd31d95..bba656d 100644 --- a/src/mappings/dbFunctions/domainRewards.ts +++ b/src/mappings/dbFunctions/domainRewards.ts @@ -122,5 +122,14 @@ export function getPerformanceIndexSqls(dbSchema: string): string[] { ON ${dbSchema}.domain_service_daily_rewards (domain, day)`, `CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_dsdr_day ON ${dbSchema}.domain_service_daily_rewards (day)`, + // Covering partial index for the PostGraphile `transactions` connection + // (`ORDER BY block_id DESC LIMIT n` + `count(*)`). SubQuery only creates GIST + // (col, _block_range) indexes, which cannot serve ordering, so both halves of + // that query were parallel seq scans of the whole heap. PostGraphile adds + // `id IS NOT NULL` to the WHERE clause, so the index is partial on that + // predicate to allow an index-only scan for the count. + `CREATE INDEX CONCURRENTLY IF NOT EXISTS transactions_block_id_desc_live_idx + ON ${dbSchema}.transactions (block_id DESC, _id) INCLUDE (_block_range) + WHERE id IS NOT NULL`, ]; } diff --git a/src/mappings/dbFunctions/supply.ts b/src/mappings/dbFunctions/supply.ts index e51545f..a3d9464 100644 --- a/src/mappings/dbFunctions/supply.ts +++ b/src/mappings/dbFunctions/supply.ts @@ -120,36 +120,37 @@ STABLE AS $$ DECLARE reimbursement_amount NUMERIC := 0; - reimbursement_v2_amount NUMERIC := 0; inflation_amount NUMERIC := 0; mint_burn_amount NUMERIC := 0; BEGIN - -- Reimbursement amount - SELECT COALESCE(SUM(t.amount), 0) INTO reimbursement_amount - FROM ${dbSchema}.mod_to_acct_transfers t - INNER JOIN ${dbSchema}.blocks b ON t.block_id = b.id - WHERE t.op_reason = 'TLM_GLOBAL_MINT_REIMBURSEMENT_REQUEST_ESCROW_DAO_TRANSFER' - AND b.timestamp BETWEEN start_date AND end_date; - - -- Mint by op_reason + -- Mint by op_reason, read from event_claim_settleds.mints only. -- Old protocol: opReason 1=SUPPLIER_STAKE_MINT (mint_burn), 3=INFLATION (inflation) -- New protocol: opReason 19=TOKENOMICS_CLAIM_DISTRIBUTION_MINT (mint_burn), 5=DAO_REWARD_DISTRIBUTION (inflation) - -- opReason 10=REIMBURSEMENT_REQUEST_ESCROW_DAO_TRANSFER is unchanged across both protocols + -- opReason 10=REIMBURSEMENT_REQUEST_ESCROW_DAO_TRANSFER is unchanged across both protocols. + -- + -- Reimbursement used to also be summed from mod_to_acct_transfers (op_reason + -- TLM_GLOBAL_MINT_REIMBURSEMENT_REQUEST_ESCROW_DAO_TRANSFER). That table is ~2.9 B rows on + -- mainnet and the only index on op_reason is single-column, so the planner heap-fetched + -- every reimbursement row (~21 GB of reads per call regardless of the date window). The + -- mapping already records the same escrow amount as a synthetic opReason 10 mint on the + -- event_claim_settleds row (see _buildSettlementFromDetailedDistribution in relays.ts), + -- so the raw-table branch was both redundant and double counting in the + -- reward_distribution_detailed era. SELECT SUM(CASE WHEN (elem->>'opReason')::int = 10 THEN REPLACE(elem->>'amount', 'n', '')::numeric ELSE 0 END), SUM(CASE WHEN (elem->>'opReason')::int IN (3, 5) THEN REPLACE(elem->>'amount', 'n', '')::numeric ELSE 0 END), SUM(CASE WHEN (elem->>'opReason')::int IN (1, 19) THEN REPLACE(elem->>'amount', 'n', '')::numeric ELSE 0 END) INTO - reimbursement_v2_amount, inflation_amount, mint_burn_amount + reimbursement_amount, inflation_amount, mint_burn_amount FROM ${dbSchema}.event_claim_settleds t INNER JOIN ${dbSchema}.blocks b ON t.block_id = b.id JOIN LATERAL jsonb_array_elements(t.mints) AS elem ON TRUE WHERE b.timestamp BETWEEN start_date AND end_date; RETURN json_build_object( - 'reimbursement', reimbursement_amount + reimbursement_v2_amount, - 'inflation', inflation_amount, - 'mint_burn', mint_burn_amount + 'reimbursement', COALESCE(reimbursement_amount, 0), + 'inflation', COALESCE(inflation_amount, 0), + 'mint_burn', COALESCE(mint_burn_amount, 0) ); END; $$;