import { MigrationInterface, QueryRunner, TableIndex } from 'typeorm' /** * 「이 트윈의 오래된 기록」 — 보관 기간 정리가 쓰는 축에 인덱스가 없었다. * * ── 무엇이 났나 (2026-08-27 실측 · 개발 파일 51GB · 3,128만 행) ─────────────── * 정리는 `created_at` 으로 자른다(보관은 저장 나이의 문제이고 저장 나이는 실제 시계다). 그런데 그 축에 * 인덱스가 없어서, 매 배치마다 그 트윈의 저널을 전량 주사했다. * * hatio-mx2 의 7일 밖 행을 세기 208초 ← 1,029만 행을 전부 읽는다(7일 밖은 2,446건뿐이다) * revision 으로 한 행 짚기 0.004초 * * 정리는 한 창에 2초만 쓰도록 예산이 걸려 있다. 그 2초를 주사에 다 쓰고 44건만 지우고 멈췄다. 지금은 * 지울 것이 적어서 크게 드러나지 않지만, 보관 기간을 줄이거나 쓰는 양이 늘면 따라가지 못한다. * * ── 큰 기록에서는 만드는 데 시간이 걸린다 ─────────────────────────────────── * 3,128만 행에 인덱스를 만드는 동안 쓰기가 막힌다(sqlite 는 쓰기가 하나다). 서버를 내린 뒤 돌린다. * 멱등이므로 다시 돌려도 안전하고, TypeORM API 만 쓴다(드라이버 다섯을 다 지나가야 하므로 raw SQL 금지). */ const EVENTS = 'twin_events' const IX = 'ix_twin_event_8' const COLUMNS = ['domain_id', 'instance_id', 'created_at'] export class IndexEventCreatedAt1786400000000 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { const events = await queryRunner.getTable(EVENTS) if (!events) return if (events.indices.some(i => i.name === IX)) return await queryRunner.createIndex(events, new TableIndex({ name: IX, columnNames: COLUMNS })) } public async down(queryRunner: QueryRunner): Promise { const events = await queryRunner.getTable(EVENTS) if (!events) return if (!events.indices.some(i => i.name === IX)) return await queryRunner.dropIndex(events, IX) } }