import { MigrationInterface, QueryRunner, TableColumn, TableIndex } from 'typeorm' /** * 두 계측을 위한 컬럼 둘. * * ① `twin_events.action` — EPCIS 행위(ADD/OBSERVE/DELETE) 승격. * 되풀어 읽는 스냅샷은 원리적으로 `DELETE` 를 못 낸다. 그래서 「이 트윈이 반출을 한 번이라도 * 계산했는가」가 진짜 delta 피드의 증거인데, 값이 `payload`(simple-json = TEXT) 안에 있는 동안에는 * 5개 드라이버 어디에서도 그 질문을 할 수 없었다(DB별 JSON 연산자 금지). * * ② `twin_references.last_warning_count` — 마지막 동기에서 못 옮긴 값의 수. * 세지 않은 구조 인제스트는 도크를 랙으로 바꿔 놓고도 성공이라 답하면서 아무 일도 하지 않는다. * * **둘 다 nullable, 기본값 없음.** 기본값 0 을 깔면 「없었다」와 「세지 않았다」가 같아지고, 그 * 구분이 단계 판정의 전부다(옛 행은 모르는 것이 맞다 — 채워 넣지 않는다). * * 인덱스 `ix_twin_event_6` 는 저널이 큰 환경에서 생성에 시간이 걸린다. 그래도 만드는 이유: * 없으면 「반출을 계산했나」가 그 인스턴스 저널 전량 주사가 되고, 반출이 **없는** 경우(초기 상태이자 * 가장 흔한 경우)가 항상 최악이다. * * 멱등: 이미 있으면 아무것도 하지 않는다. TypeORM API 만 쓴다(raw SQL 금지 — 드라이버 5종). */ const EVENTS = 'twin_events' const REFS = 'twin_references' const ACTION = 'action' const WARN = 'last_warning_count' const IX = 'ix_twin_event_6' export class PromoteEventActionAndSyncWarnings1786100000000 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { const events = await queryRunner.getTable(EVENTS) if (events && !events.columns.some(c => c.name === ACTION)) { await queryRunner.addColumn(events, new TableColumn({ name: ACTION, type: 'varchar', length: '16', isNullable: true })) } const withColumn = await queryRunner.getTable(EVENTS) if (withColumn && withColumn.columns.some(c => c.name === ACTION) && !withColumn.indices.some(i => i.name === IX)) { await queryRunner.createIndex(withColumn, new TableIndex({ name: IX, columnNames: ['domain_id', 'instance_id', ACTION] })) } const refs = await queryRunner.getTable(REFS) if (refs && !refs.columns.some(c => c.name === WARN)) { await queryRunner.addColumn(refs, new TableColumn({ name: WARN, type: 'int', isNullable: true })) } } public async down(queryRunner: QueryRunner): Promise { const events = await queryRunner.getTable(EVENTS) if (events?.indices.some(i => i.name === IX)) await queryRunner.dropIndex(events, IX) const events2 = await queryRunner.getTable(EVENTS) if (events2?.columns.some(c => c.name === ACTION)) await queryRunner.dropColumn(events2, ACTION) const refs = await queryRunner.getTable(REFS) if (refs?.columns.some(c => c.name === WARN)) await queryRunner.dropColumn(refs, WARN) } }