Skip to content

Commit 0b54c3f

Browse files
committed
Merge branch 'main' into v145
2 parents d1e78b2 + 5cf6b3c commit 0b54c3f

2 files changed

Lines changed: 93 additions & 0 deletions

File tree

src/main-mainnet.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { startToastVacuumMonitor } from '@utils/toast-vacuum'
12
import { coingeckoProcessor } from 'mainnet/processors/coingecko'
23
import { notionProcessor } from 'mainnet/processors/notion'
34
import { ognBuybacks } from 'mainnet/processors/ogn-buybacks'
@@ -65,6 +66,7 @@ export const processor = defineSquidProcessor({
6566
export default processor
6667

6768
if (require.main === module) {
69+
startToastVacuumMonitor()
6870
run(processor).catch((error) => {
6971
throw error
7072
})

src/utils/toast-vacuum.ts

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { Pool } from 'pg'
2+
3+
const TOAST_THRESHOLD_BYTES = 1_000_000_000 // 1 GB
4+
const CHECK_INTERVAL_MS = 30 * 60 * 1000 // 30 minutes
5+
6+
interface ToastInfo {
7+
schema_name: string
8+
table_name: string
9+
toast_table: string
10+
toast_size: string
11+
toast_bytes: number
12+
}
13+
14+
async function checkAndVacuumToast(pool: Pool): Promise<void> {
15+
const client = await pool.connect()
16+
try {
17+
const result = await client.query<ToastInfo>(`
18+
SELECT
19+
n.nspname AS schema_name,
20+
c.relname AS table_name,
21+
t.relname AS toast_table,
22+
pg_size_pretty(pg_relation_size(t.oid)) AS toast_size,
23+
pg_relation_size(t.oid) AS toast_bytes
24+
FROM pg_class c
25+
JOIN pg_class t ON t.oid = c.reltoastrelid
26+
JOIN pg_namespace n ON n.oid = c.relnamespace
27+
WHERE t.relkind = 't'
28+
ORDER BY pg_relation_size(t.oid) DESC
29+
LIMIT 20
30+
`)
31+
32+
for (const row of result.rows) {
33+
console.log(`[toast-vacuum] ${row.schema_name}.${row.table_name}: TOAST ${row.toast_size}`)
34+
}
35+
36+
const bloated = result.rows.filter((r) => r.toast_bytes > TOAST_THRESHOLD_BYTES)
37+
for (const row of bloated) {
38+
console.log(
39+
`[toast-vacuum] TOAST bloat detected on ${row.schema_name}.${row.table_name} (${row.toast_size}), running VACUUM FULL...`,
40+
)
41+
const start = Date.now()
42+
await client.query(`VACUUM FULL VERBOSE "${row.schema_name}"."${row.table_name}"`)
43+
const elapsed = ((Date.now() - start) / 1000).toFixed(1)
44+
console.log(`[toast-vacuum] VACUUM FULL on ${row.schema_name}.${row.table_name} completed in ${elapsed}s`)
45+
}
46+
47+
if (bloated.length === 0) {
48+
console.log('[toast-vacuum] No TOAST bloat detected.')
49+
}
50+
} catch (err) {
51+
console.error('[toast-vacuum] Error during TOAST check:', err)
52+
} finally {
53+
client.release()
54+
}
55+
}
56+
57+
async function configureSessionTimeouts(pool: Pool): Promise<void> {
58+
const client = await pool.connect()
59+
try {
60+
const dbName = process.env.DB_NAME || 'squid'
61+
await client.query(`ALTER DATABASE "${dbName}" SET idle_in_transaction_session_timeout = '2min'`)
62+
await client.query(`ALTER DATABASE "${dbName}" SET idle_session_timeout = '5min'`)
63+
console.log('[toast-vacuum] Configured database session timeouts (idle_in_transaction=2min, idle_session=5min)')
64+
} catch (err) {
65+
console.error('[toast-vacuum] Error configuring session timeouts:', err)
66+
} finally {
67+
client.release()
68+
}
69+
}
70+
71+
export function startToastVacuumMonitor(): void {
72+
const pool = new Pool({
73+
host: process.env.DB_HOST,
74+
port: parseInt(process.env.DB_PORT!),
75+
database: process.env.DB_NAME,
76+
user: process.env.DB_USER,
77+
password: process.env.DB_PASS,
78+
connectionTimeoutMillis: 60000,
79+
idleTimeoutMillis: 600000,
80+
query_timeout: 3600000, // 1 hour — VACUUM FULL can take a while
81+
statement_timeout: 3600000,
82+
})
83+
84+
// Configure database-level session timeouts to prevent idle transactions from blocking VACUUM
85+
configureSessionTimeouts(pool).then(() => checkAndVacuumToast(pool)).then(() => {
86+
// Then repeat every 30 minutes
87+
setInterval(() => {
88+
checkAndVacuumToast(pool)
89+
}, CHECK_INTERVAL_MS)
90+
})
91+
}

0 commit comments

Comments
 (0)