@dudousxd/nestjs-durable-store-mikro-orm 0.33.1 → 0.33.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -25,6 +25,8 @@ __export(index_exports, {
25
25
  BufferedEventRepository: () => BufferedEventRepository,
26
26
  BufferedSignalEntity: () => BufferedSignalEntity,
27
27
  BufferedSignalRepository: () => BufferedSignalRepository,
28
+ DATETIME_PRECISION: () => DATETIME_PRECISION,
29
+ DEFAULT_BLOCKING_ALTER_ROW_LIMIT: () => DEFAULT_BLOCKING_ALTER_ROW_LIMIT,
28
30
  ENTITIES: () => ENTITIES,
29
31
  MikroOrmStateStore: () => MikroOrmStateStore,
30
32
  RunAttributeEntity: () => RunAttributeEntity,
@@ -40,6 +42,7 @@ __export(index_exports, {
40
42
  durableEntities: () => durableEntities,
41
43
  durableManagedTables: () => durableManagedTables,
42
44
  ensureMikroOrmDurableSchema: () => ensureMikroOrmDurableSchema,
45
+ mikroOrmDeferredAlterWarning: () => mikroOrmDeferredAlterWarning,
43
46
  mikroOrmSchemaRepairWarning: () => mikroOrmSchemaRepairWarning
44
47
  });
45
48
  module.exports = __toCommonJS(index_exports);
@@ -90,6 +93,7 @@ function snakeCase(property) {
90
93
  return property.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);
91
94
  }
92
95
  __name(snakeCase, "snakeCase");
96
+ var DATETIME_PRECISION = 6;
93
97
  function columnNamer(naming) {
94
98
  if (naming === "snake_case") return snakeCase;
95
99
  if (naming === "preserve") return (property) => property;
@@ -296,6 +300,7 @@ function durableEntities(options = {}) {
296
300
  },
297
301
  wakeAt: {
298
302
  type: "Date",
303
+ length: DATETIME_PRECISION,
299
304
  nullable: true,
300
305
  fieldName: col("wakeAt")
301
306
  },
@@ -306,6 +311,7 @@ function durableEntities(options = {}) {
306
311
  },
307
312
  lockedUntil: {
308
313
  type: "Date",
314
+ length: DATETIME_PRECISION,
309
315
  nullable: true,
310
316
  fieldName: col("lockedUntil")
311
317
  },
@@ -368,10 +374,12 @@ function durableEntities(options = {}) {
368
374
  },
369
375
  createdAt: {
370
376
  type: "Date",
377
+ length: DATETIME_PRECISION,
371
378
  fieldName: col("createdAt")
372
379
  },
373
380
  updatedAt: {
374
381
  type: "Date",
382
+ length: DATETIME_PRECISION,
375
383
  fieldName: col("updatedAt")
376
384
  }
377
385
  }
@@ -460,20 +468,24 @@ function durableEntities(options = {}) {
460
468
  },
461
469
  wakeAt: {
462
470
  type: "Date",
471
+ length: DATETIME_PRECISION,
463
472
  nullable: true,
464
473
  fieldName: col("wakeAt")
465
474
  },
466
475
  enqueuedAt: {
467
476
  type: "Date",
477
+ length: DATETIME_PRECISION,
468
478
  nullable: true,
469
479
  fieldName: col("enqueuedAt")
470
480
  },
471
481
  startedAt: {
472
482
  type: "Date",
483
+ length: DATETIME_PRECISION,
473
484
  fieldName: col("startedAt")
474
485
  },
475
486
  finishedAt: {
476
487
  type: "Date",
488
+ length: DATETIME_PRECISION,
477
489
  fieldName: col("finishedAt")
478
490
  }
479
491
  }
@@ -618,6 +630,7 @@ function durableEntities(options = {}) {
618
630
  },
619
631
  publishedAt: {
620
632
  type: "Date",
633
+ length: DATETIME_PRECISION,
621
634
  fieldName: col("publishedAt")
622
635
  }
623
636
  }
@@ -676,10 +689,12 @@ function durableEntities(options = {}) {
676
689
  },
677
690
  createdAt: {
678
691
  type: "Date",
692
+ length: DATETIME_PRECISION,
679
693
  fieldName: col("createdAt")
680
694
  },
681
695
  updatedAt: {
682
696
  type: "Date",
697
+ length: DATETIME_PRECISION,
683
698
  fieldName: col("updatedAt")
684
699
  }
685
700
  }
@@ -722,6 +737,21 @@ var MARKER_TABLE = "durable_schema_meta";
722
737
  var MARKER_ROW_ID = "durable";
723
738
  var SCHEMA_LOCK_NAME = "durable_schema";
724
739
  var SCHEMA_REVISION = 1;
740
+ var DEFAULT_BLOCKING_ALTER_ROW_LIMIT = 1e5;
741
+ function deferredAlterWarning(store, deferred, limit) {
742
+ return [
743
+ `[nestjs-durable] ${store} did NOT apply ${deferred.length} durable-schema column type change(s) at boot.`,
744
+ `MySQL rebuilds a table to change a column type and blocks writes to it until the copy ends, and these tables hold more than blockingAlterRowLimit (${limit}) rows:`,
745
+ ...deferred.map((d) => ` -- ${d.table}, ~${d.rows} rows
746
+ ${d.statement};`),
747
+ "Run them in a maintenance window or with an online schema change tool (gh-ost, pt-online-schema-change), or raise blockingAlterRowLimit to let a boot run them. Until then the store keeps working on the old column types, and this warning repeats on every boot."
748
+ ].join("\n");
749
+ }
750
+ __name(deferredAlterWarning, "deferredAlterWarning");
751
+ function mikroOrmDeferredAlterWarning(deferred, limit) {
752
+ return deferredAlterWarning("store-mikro-orm", deferred, limit);
753
+ }
754
+ __name(mikroOrmDeferredAlterWarning, "mikroOrmDeferredAlterWarning");
725
755
  function mikroOrmSchemaRepairWarning(repairs) {
726
756
  return [
727
757
  `[nestjs-durable] store-mikro-orm repaired the durable schema at boot: ${repairs.join("; ")}.`,
@@ -926,29 +956,43 @@ async function ensureMikroOrmDurableSchema(orm, options = {}) {
926
956
  if (await readStoredFingerprint(orm) === expected) {
927
957
  return;
928
958
  }
929
- const { repairs, freshInstall } = await healDurableSchema(orm);
930
- await writeFingerprint(orm, dialect, expected);
959
+ const limit = options.blockingAlterRowLimit ?? DEFAULT_BLOCKING_ALTER_ROW_LIMIT;
960
+ const { repairs, freshInstall, deferred } = await healDurableSchema(orm, dialect, limit);
961
+ if (deferred.length === 0) await writeFingerprint(orm, dialect, expected);
931
962
  if (repairs.length > 0 && !freshInstall) logger.warn(mikroOrmSchemaRepairWarning(repairs));
963
+ if (deferred.length > 0) logger.warn(mikroOrmDeferredAlterWarning(deferred, limit));
932
964
  } finally {
933
965
  await releaseSchemaLock(orm, dialect);
934
966
  }
935
967
  }
936
968
  __name(ensureMikroOrmDurableSchema, "ensureMikroOrmDurableSchema");
937
- async function healDurableSchema(orm) {
969
+ async function healDurableSchema(orm, dialect, blockingAlterRowLimit) {
938
970
  const sql = await orm.schema.getUpdateSchemaSQL({
939
971
  safe: true
940
972
  });
941
- const ours = sql.split(";").map((statement) => statement.trim()).filter((statement) => {
973
+ const ours = mergeColumnTypeChanges(sql.split(";").map((statement) => statement.trim()).filter((statement) => {
942
974
  const table = targetTable(statement);
943
975
  return table !== void 0 && DURABLE_TABLE_NAMES.has(table);
944
- });
976
+ }));
945
977
  const connection = orm.em.getConnection();
946
978
  const created = new Set(ours.filter((statement) => /^create\s+table\b/i.test(statement)).map((statement) => targetTable(statement)));
947
979
  const repairs = [];
980
+ const deferred = [];
948
981
  for (const statement of ours) {
982
+ const table = targetTable(statement);
983
+ if (dialect === "mysql" && !created.has(table) && isColumnTypeChange(statement)) {
984
+ const rows = await estimatedRowCount(orm, table);
985
+ if (rows > blockingAlterRowLimit) {
986
+ deferred.push({
987
+ table,
988
+ rows,
989
+ statement
990
+ });
991
+ continue;
992
+ }
993
+ }
949
994
  try {
950
995
  await connection.execute(statement);
951
- const table = targetTable(statement);
952
996
  const isCreateTable = /^create\s+table\b/i.test(statement);
953
997
  if (isCreateTable || !created.has(table)) repairs.push(...describeDurableStatement(statement));
954
998
  } catch (err) {
@@ -968,10 +1012,45 @@ async function healDurableSchema(orm) {
968
1012
  ].every((table) => created.has(table));
969
1013
  return {
970
1014
  repairs,
971
- freshInstall
1015
+ freshInstall,
1016
+ deferred
972
1017
  };
973
1018
  }
974
1019
  __name(healDurableSchema, "healDurableSchema");
1020
+ function mergeColumnTypeChanges(statements) {
1021
+ const merged = [];
1022
+ const at = /* @__PURE__ */ new Map();
1023
+ for (const statement of statements) {
1024
+ const table = targetTable(statement);
1025
+ if (!table || !isColumnTypeChange(statement)) {
1026
+ merged.push(statement);
1027
+ continue;
1028
+ }
1029
+ const actions = statement.replace(/^alter\s+table\s+[`"']?[\w$]+[`"']?\s*/i, "");
1030
+ const index = at.get(table);
1031
+ if (index === void 0) {
1032
+ at.set(table, merged.length);
1033
+ merged.push(statement);
1034
+ } else {
1035
+ merged[index] = `${merged[index]}, ${actions}`;
1036
+ }
1037
+ }
1038
+ return merged;
1039
+ }
1040
+ __name(mergeColumnTypeChanges, "mergeColumnTypeChanges");
1041
+ function isColumnTypeChange(statement) {
1042
+ if (!/^alter\s+table\b/i.test(statement)) return false;
1043
+ const actions = splitTopLevel(statement.replace(/^alter\s+table\s+[`"']?[\w$]+[`"']?\s*/i, ""));
1044
+ return actions.length > 0 && actions.every((action) => /^modify\b/i.test(action));
1045
+ }
1046
+ __name(isColumnTypeChange, "isColumnTypeChange");
1047
+ async function estimatedRowCount(orm, table) {
1048
+ const rows = await orm.em.getConnection().execute("select table_rows as table_rows from information_schema.tables where table_schema = database() and table_name = ? limit 1", [
1049
+ table
1050
+ ]);
1051
+ return Number(rows[0]?.table_rows ?? 0);
1052
+ }
1053
+ __name(estimatedRowCount, "estimatedRowCount");
975
1054
  function isSafeSqlIdentifier(value) {
976
1055
  return /^[A-Za-z0-9_]+$/.test(value);
977
1056
  }
@@ -1055,7 +1134,8 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
1055
1134
  }
1056
1135
  async ensureSchema() {
1057
1136
  await ensureMikroOrmDurableSchema(this.orm, {
1058
- logger: this.opts.schemaLogger
1137
+ logger: this.opts.schemaLogger,
1138
+ blockingAlterRowLimit: this.opts.schemaBlockingAlterRowLimit
1059
1139
  });
1060
1140
  }
1061
1141
  async tryAdmitSingleton(runId, tag, workflow, limit, retryWakeAt) {
@@ -2286,6 +2366,8 @@ __name(fromScheduleEntity, "fromScheduleEntity");
2286
2366
  BufferedEventRepository,
2287
2367
  BufferedSignalEntity,
2288
2368
  BufferedSignalRepository,
2369
+ DATETIME_PRECISION,
2370
+ DEFAULT_BLOCKING_ALTER_ROW_LIMIT,
2289
2371
  ENTITIES,
2290
2372
  MikroOrmStateStore,
2291
2373
  RunAttributeEntity,
@@ -2301,6 +2383,7 @@ __name(fromScheduleEntity, "fromScheduleEntity");
2301
2383
  durableEntities,
2302
2384
  durableManagedTables,
2303
2385
  ensureMikroOrmDurableSchema,
2386
+ mikroOrmDeferredAlterWarning,
2304
2387
  mikroOrmSchemaRepairWarning
2305
2388
  });
2306
2389
  //# sourceMappingURL=index.cjs.map