velocious 1.0.691 → 1.0.692

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.
@@ -183,6 +183,12 @@ const SCHEMA_INVALIDATION_SCAN_LIMIT = 8192
183
183
  /** Maximum checkout-name characters inspected by retry diagnostics. */
184
184
  const OPERATION_NAME_SCAN_LIMIT = 1024
185
185
  const REDACTED_DIAGNOSTIC_LABEL = "[REDACTED]"
186
+ /** First backoff between retries of a schema-metadata read blocked by concurrent online DDL. */
187
+ const SCHEMA_METADATA_RETRY_INITIAL_DELAY_MS = 250
188
+ /** Largest backoff between retries of a schema-metadata read blocked by concurrent online DDL. */
189
+ const SCHEMA_METADATA_RETRY_MAX_DELAY_MS = 2000
190
+ /** Default bounded deadline for outlasting a concurrent online-DDL metadata skip. */
191
+ const SCHEMA_METADATA_RETRY_DEADLINE_MS = 60000
186
192
 
187
193
  /**
188
194
  * Builds a non-reversible, stable SQL fingerprint without retaining SQL text. Literal spelling is
@@ -777,6 +783,52 @@ export default class VelociousDatabaseDriversBase {
777
783
  return this.getArgs().schemaCache !== false
778
784
  }
779
785
 
786
+ /**
787
+ * Runs schema metadata retry deadline ms.
788
+ * @returns {number} - Bounded deadline for outlasting transient schema-metadata contention.
789
+ */
790
+ _schemaMetadataRetryDeadlineMs() {
791
+ const configured = this.getArgs().schemaMetadataRetryDeadlineMs
792
+
793
+ return typeof configured === "number" ? configured : SCHEMA_METADATA_RETRY_DEADLINE_MS
794
+ }
795
+
796
+ /**
797
+ * Whether an error raised by a schema-metadata read is transient contention that a bounded
798
+ * retry can outlast. Drivers override this for their own contention signatures; the default
799
+ * treats every failure as final so only drivers that prove the transient shape retry it.
800
+ * @param {unknown} _error - Error raised by a schema-metadata read.
801
+ * @returns {boolean} - Whether the read may be retried.
802
+ */
803
+ _isTransientSchemaMetadataError(_error) {
804
+ return false
805
+ }
806
+
807
+ /**
808
+ * Runs a schema-metadata read, retrying transient concurrent-DDL contention until the bounded
809
+ * deadline. Non-transient failures and exhausted deadlines propagate the original error.
810
+ * @template T
811
+ * @param {() => Promise<T>} callback - Cache-miss schema read.
812
+ * @returns {Promise<T>} - Resolves with the read result.
813
+ */
814
+ async _withSchemaMetadataDdlRetry(callback) {
815
+ const deadlineMs = this._schemaMetadataRetryDeadlineMs()
816
+ const startedAt = Date.now()
817
+ let delayMs = SCHEMA_METADATA_RETRY_INITIAL_DELAY_MS
818
+
819
+ for (;;) {
820
+ try {
821
+ return await callback()
822
+ } catch (error) {
823
+ if (!this._isTransientSchemaMetadataError(error)) throw error
824
+ if (Date.now() - startedAt + delayMs > deadlineMs) throw error
825
+
826
+ await wait(delayMs)
827
+ delayMs = Math.min(delayMs * 2, SCHEMA_METADATA_RETRY_MAX_DELAY_MS)
828
+ }
829
+ }
830
+ }
831
+
780
832
  /**
781
833
  * Runs cached schema metadata.
782
834
  * @template T
@@ -785,7 +837,7 @@ export default class VelociousDatabaseDriversBase {
785
837
  * @returns {Promise<T>} - Resolves with the cached metadata.
786
838
  */
787
839
  async _cachedSchemaMetadata(cacheKey, callback) {
788
- if (!this._schemaCacheEnabled()) return await callback()
840
+ if (!this._schemaCacheEnabled()) return await this._withSchemaMetadataDdlRetry(callback)
789
841
 
790
842
  const existingPromise = this._schemaCache.get(cacheKey)
791
843
 
@@ -793,7 +845,7 @@ export default class VelociousDatabaseDriversBase {
793
845
  return /** @type {T} */ (this._schemaCacheReturnValue(await existingPromise))
794
846
  }
795
847
 
796
- const promise = (async () => await callback())()
848
+ const promise = (async () => await this._withSchemaMetadataDdlRetry(callback))()
797
849
 
798
850
  this._schemaCache.set(cacheKey, promise)
799
851
 
@@ -723,6 +723,29 @@ export default class VelociousDatabaseDriversMysql extends Base{
723
723
  return insert.toSql()
724
724
  }
725
725
 
726
+ /**
727
+ * MariaDB skips information-schema/SHOW reads for a table whose definition is being modified
728
+ * by concurrent online DDL (ER_WARN_I_S_SKIPPED_TABLE). The build completes in seconds, so the
729
+ * skip is transient contention that the bounded schema-metadata retry can outlast. Query
730
+ * helpers wrap the driver error, so the code is searched through the cause chain.
731
+ * @param {unknown} error - Error raised by a schema-metadata read.
732
+ * @returns {boolean} - Whether the read hit concurrent-DDL metadata contention.
733
+ */
734
+ _isTransientSchemaMetadataError(error) {
735
+ let currentError = /** @type {{code?: string, cause?: unknown} | undefined} */ (
736
+ /** @type {unknown} */ (error)
737
+ )
738
+
739
+ for (let depth = 0; depth < 5 && currentError; depth++) {
740
+ if (currentError.code === "ER_WARN_I_S_SKIPPED_TABLE") return true
741
+ currentError = /** @type {{code?: string, cause?: unknown} | undefined} */ (
742
+ /** @type {unknown} */ (currentError.cause)
743
+ )
744
+ }
745
+
746
+ return false
747
+ }
748
+
726
749
  /**
727
750
  * Runs get tables.
728
751
  * @returns {Promise<Array<import("../base-table.js").default>>} - Resolves with the tables.
@@ -171,7 +171,7 @@ export default async function query(pool, sql, {signal} = {}) {
171
171
  connection.release()
172
172
  }
173
173
 
174
- reject(new Error(`Query failed because of ${error}: ${sql}`))
174
+ reject(new Error(`Query failed because of ${error}: ${sql}`, {cause: error}))
175
175
 
176
176
  return
177
177
  }