Summary
The soroban_events table will grow without bound for as long as the indexer runs. On testnet this might be tolerable, but on mainnet with busy contracts it will consume disk at a rate that makes self-hosting prohibitively expensive. Operators need a configurable TTL that automatically prunes old events on a schedule.
This is a quality-of-life feature for Phase 2 self-hosted deployments. It does not affect the hosted Trident tier (which will manage retention separately at the infrastructure level), but it is essential for operators running their own instance against a contract with high event volume.
Context
The soroban_events table has a ledger_timestamp column of type TIMESTAMPTZ which is perfect for time-based pruning. The cleanup job should run in the background in the Go API process — it does not need its own service — and should be chunked to avoid holding a long lock that blocks the indexer's inserts.
What needs to be built
A background goroutine in the Go API that:
- Reads
EVENT_RETENTION_DAYS env var at startup (integer, default 0 = disabled)
- When
EVENT_RETENTION_DAYS > 0, starts a ticker that fires every 6 hours
- On each tick, deletes events in chunks of 1,000 rows at a time using:
DELETE FROM soroban_events WHERE id IN (SELECT id FROM soroban_events WHERE ledger_timestamp < NOW() - INTERVAL '$N days' LIMIT 1000)
- Loops the chunked delete until zero rows are returned (all old events pruned)
- Logs the total rows deleted and wall-clock duration for each cleanup run
- Handles context cancellation cleanly — stops mid-run if the process is shutting down
Acceptance Criteria
Summary
The
soroban_eventstable will grow without bound for as long as the indexer runs. On testnet this might be tolerable, but on mainnet with busy contracts it will consume disk at a rate that makes self-hosting prohibitively expensive. Operators need a configurable TTL that automatically prunes old events on a schedule.This is a quality-of-life feature for Phase 2 self-hosted deployments. It does not affect the hosted Trident tier (which will manage retention separately at the infrastructure level), but it is essential for operators running their own instance against a contract with high event volume.
Context
The
soroban_eventstable has aledger_timestampcolumn of typeTIMESTAMPTZwhich is perfect for time-based pruning. The cleanup job should run in the background in the Go API process — it does not need its own service — and should be chunked to avoid holding a long lock that blocks the indexer's inserts.What needs to be built
A background goroutine in the Go API that:
EVENT_RETENTION_DAYSenv var at startup (integer, default 0 = disabled)EVENT_RETENTION_DAYS > 0, starts a ticker that fires every 6 hoursDELETE FROM soroban_events WHERE id IN (SELECT id FROM soroban_events WHERE ledger_timestamp < NOW() - INTERVAL '$N days' LIMIT 1000)Acceptance Criteria
EVENT_RETENTION_DAYS=0(default), the background job never starts[retention] pruned N events older than X days in Ymsledger_timestampset to 10 days ago, trigger retention withEVENT_RETENTION_DAYS=7, assert all 50 deleted