| 91 | } |
| 92 | |
| 93 | export class SchedulesMongoDB extends SchedulesStorage { |
| 94 | #connector: MongoDBConnector; |
| 95 | #skipDefaultIndexes?: boolean; |
| 96 | #indexes?: MongoDBIndexConfig[]; |
| 97 | |
| 98 | static readonly MANAGED_COLLECTIONS = [TABLE_SCHEDULES, TABLE_SCHEDULE_TRIGGERS] as const; |
| 99 | |
| 100 | /** |
| 101 | * Only trigger (fire-history) rows are retention-eligible — schedule |
| 102 | * definitions are config, not growth. Anchor `actual_fire_at` is stored as a |
| 103 | * raw epoch-ms number, so `anchorType: 'epoch-ms'` keeps the cutoff numeric. |
| 104 | * The default composite index `(schedule_id, actual_fire_at)` can't serve a |
| 105 | * bare `actual_fire_at` range scan, hence the lazy single-field anchor index. |
| 106 | */ |
| 107 | static override readonly retentionTables: RetentionTablesDescriptor = { |
| 108 | triggers: { table: TABLE_SCHEDULE_TRIGGERS, column: 'actual_fire_at', anchorType: 'epoch-ms', indexed: true }, |
| 109 | }; |
| 110 | |
| 111 | constructor(config: MongoDBDomainConfig) { |
| 112 | super(); |
| 113 | this.#connector = resolveMongoDBConfig(config); |
| 114 | this.#skipDefaultIndexes = config.skipDefaultIndexes; |
| 115 | this.#indexes = config.indexes?.filter(idx => |
| 116 | (SchedulesMongoDB.MANAGED_COLLECTIONS as readonly string[]).includes(idx.collection), |
| 117 | ); |
| 118 | } |
| 119 | |
| 120 | private getSchedulesCollection() { |
| 121 | return this.#connector.getCollection(TABLE_SCHEDULES); |
| 122 | } |
| 123 | |
| 124 | private getTriggersCollection() { |
| 125 | return this.#connector.getCollection(TABLE_SCHEDULE_TRIGGERS); |
| 126 | } |
| 127 | |
| 128 | /** Delete trigger (fire-history) rows older than the `triggers` policy's `maxAge`, batched. */ |
| 129 | async prune(policies: Record<string, TableRetentionPolicy>, options?: PruneOptions): Promise<PruneResult[]> { |
| 130 | const targets = resolveTargets({ |
| 131 | policies, |
| 132 | descriptor: SchedulesMongoDB.retentionTables, |
| 133 | order: ['triggers'], |
| 134 | }); |
| 135 | return runPrune({ connector: this.#connector, domain: 'schedules', targets, options, logger: this.logger }); |
| 136 | } |
| 137 | |
| 138 | getDefaultIndexDefinitions(): MongoDBIndexConfig[] { |
| 139 | return [ |
| 140 | { collection: TABLE_SCHEDULES, keys: { id: 1 }, options: { unique: true } }, |
| 141 | { collection: TABLE_SCHEDULES, keys: { status: 1, next_fire_at: 1 } }, |
| 142 | { collection: TABLE_SCHEDULES, keys: { 'target.workflowId': 1 } }, |
| 143 | { collection: TABLE_SCHEDULES, keys: { owner_type: 1, owner_id: 1 } }, |
| 144 | { collection: TABLE_SCHEDULE_TRIGGERS, keys: { id: 1 }, options: { unique: true } }, |
| 145 | { collection: TABLE_SCHEDULE_TRIGGERS, keys: { schedule_id: 1, actual_fire_at: -1 } }, |
| 146 | { collection: TABLE_SCHEDULE_TRIGGERS, keys: { parent_trigger_id: 1 } }, |
| 147 | ]; |
| 148 | } |
| 149 | |
| 150 | async createDefaultIndexes(): Promise<void> { |
nothing calls this directly
no outgoing calls
no test coverage detected
searching dependent graphs…