import env from '#start/env' /** * Retention for transient pipeline state in Redis. See CONCEPT.md (D11). */ export default { /** How often `gc:scheduler` sweeps. */ intervalMs: env.get('GC_INTERVAL_MS', 3 * 60 * 1000), jobs: { /** Completed jobs are only kept for inspection; keep them briefly. */ completedAgeSeconds: env.get('GC_COMPLETED_AGE_SECONDS', 60 * 60), completedMaxCount: 1000, /** Failed jobs are kept longer so they can be inspected and retried. */ failedAgeSeconds: env.get('GC_FAILED_AGE_SECONDS', 7 * 24 * 60 * 60), }, /** * Upper bound on waiting body jobs. Obfuscated traffic can exceed what the * line downloads for hours; beyond this, the oldest waiting jobs are * dropped so Redis stays bounded (~1 KB per job). */ bodyQueueMax: env.get('GC_BODY_QUEUE_MAX', 500_000), binaries: { /** * A binary that gets no new segments for this long is deleted: incomplete * ones are given up on, completed ones have been copied into `files`. */ staleAfterSeconds: env.get('GC_BINARY_STALE_SECONDS', 48 * 60 * 60), /** * Header-derived binaries that still have one article after this long: * uploads that give every article its own random name (and poster) never * complete from headers. A real file gets its next articles in minutes. */ singleArticleAfterSeconds: env.get('GC_SINGLE_ARTICLE_SECONDS', 2 * 60 * 60), /** * Completed header-derived binaries without a fingerprint (size, MD5 of * the first 16 KiB): their file exists and no par2 set can name them * later, so they go sooner than staleAfterSeconds. */ unfingerprintedAfterSeconds: env.get('GC_UNFINGERPRINTED_SECONDS', 6 * 60 * 60), }, /** * Staged lone segments (CONCEPT.md D35) are dropped after this long. A * real file's next segment comes in the same or the next fetch range, * seconds to minutes later; at backfill speed two hours of waiting * segments were 12M rows, 10 GB. */ stagingTtlSeconds: env.get('STAGING_TTL_SECONDS', 30 * 60), /** Releases without files or binaries (their binaries were collected) are deleted after this long. */ emptyReleaseAfterSeconds: env.get('GC_EMPTY_RELEASE_SECONDS', 2 * 60 * 60), /** * Bulk deletes run in batches of this size, each kind (copied parts, * single-article and unfingerprinted binaries, empty releases) for at most * this long per sweep, so one backlog can't starve the others. */ deleteBatch: 20_000, deleteBudgetMs: env.get('GC_DELETE_BUDGET_MS', 60 * 1000), /** Bloated indexes are rebuilt at most this often (IndexMaintenance). */ reindexIntervalMs: env.get('GC_REINDEX_INTERVAL_MS', 7 * 24 * 60 * 60 * 1000), /** * Deferred files (CONCEPT.md D28) are deleted this long after they were * first seen, processed or not. The articles stay on the server. */ deferredRetentionDays: env.get('GC_DEFERRED_RETENTION_DAYS', 30), /** Redis hashes from the old header worker (before D12); they only need to expire. */ legacyPendingFiles: { prefix: 'file:', ttlSeconds: 48 * 60 * 60, }, }