@koralabs/kora-labs-common 6.14.0 → 6.14.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.
@@ -36,6 +36,8 @@ type Mode = 'serial' | 'localSerial' | 'standDown';
36
36
  export declare const resolveMode: (offline: string[], topology: CronLockTopology) => Mode;
37
37
  /** Only an UNAVAILABLE response proves that no SERIAL proposal was accepted. */
38
38
  export declare const isPeerUnavailableError: (error: unknown) => boolean;
39
+ /** A write timeout leaves an LWT's outcome unknown: the proposal may or may not have been applied. */
40
+ export declare const isWriteTimeoutError: (error: unknown) => boolean;
39
41
  /**
40
42
  * The directly testable LWT operation. Each invocation owns a unique token, so
41
43
  * concurrent ticks on one node cannot both treat the node identity as ownership.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.release = exports.acquire = exports.acquireWithExecutor = exports.isPeerUnavailableError = exports.resolveMode = exports.parseOffline = exports.leaseMs = exports.resolveTopology = void 0;
3
+ exports.release = exports.acquire = exports.acquireWithExecutor = exports.isWriteTimeoutError = exports.isPeerUnavailableError = exports.resolveMode = exports.parseOffline = exports.leaseMs = exports.resolveTopology = void 0;
4
4
  /**
5
5
  * Cross-datacenter cron mutex for the self-hosted, multi-DC Scylla topology.
6
6
  * (Moved from minting.handle.me src/helpers/cronLock/cqlCronLock.ts @ 6ecb9c3 so every box cron
@@ -114,6 +114,9 @@ const createToken = (topology) => `${topology.nodeCode}:${(0, node_crypto_1.rand
114
114
  /** Only an UNAVAILABLE response proves that no SERIAL proposal was accepted. */
115
115
  const isPeerUnavailableError = (error) => error instanceof cassandra_driver_1.errors.ResponseError && error.code === cassandra_driver_1.types.responseErrorCodes.unavailableException;
116
116
  exports.isPeerUnavailableError = isPeerUnavailableError;
117
+ /** A write timeout leaves an LWT's outcome unknown: the proposal may or may not have been applied. */
118
+ const isWriteTimeoutError = (error) => error instanceof cassandra_driver_1.errors.ResponseError && error.code === cassandra_driver_1.types.responseErrorCodes.writeTimeout;
119
+ exports.isWriteTimeoutError = isWriteTimeoutError;
117
120
  /**
118
121
  * The directly testable LWT operation. Each invocation owns a unique token, so
119
122
  * concurrent ticks on one node cannot both treat the node identity as ownership.
@@ -141,10 +144,32 @@ const acquireWithExecutor = async (executor, name, offline, topology, token = cr
141
144
  if (mode === 'serial' && (0, exports.isPeerUnavailableError)(error)) {
142
145
  return { status: 'peerUnavailable', token, message };
143
146
  }
147
+ if ((0, exports.isWriteTimeoutError)(error)) {
148
+ // The LWT timed out: it may have been applied WITH OUR TOKEN, in which case nobody (not even
149
+ // us) could run until the lease expired. A read at the same serial consistency completes any
150
+ // in-flight Paxos round, so it tells us the real owner.
151
+ const owner = await readOwner(executor, key, serialConsistency);
152
+ if (owner === token)
153
+ return { status: 'acquired', token };
154
+ if (owner)
155
+ return { status: 'held' };
156
+ }
144
157
  return { status: 'unavailable', message };
145
158
  }
146
159
  };
147
160
  exports.acquireWithExecutor = acquireWithExecutor;
161
+ /** The lock's owner as a linearizable (serial) read sees it; undefined when absent or unreadable. */
162
+ const readOwner = async (executor, key, serialConsistency) => {
163
+ var _a;
164
+ try {
165
+ const result = await executor.execute(`SELECT owner FROM ${KEYSPACE}.${LOCK_TABLE} WHERE name = ?`, [key], { prepare: true, consistency: serialConsistency });
166
+ const owner = (_a = result.rows[0]) === null || _a === void 0 ? void 0 : _a.owner;
167
+ return typeof owner === 'string' ? owner : undefined;
168
+ }
169
+ catch (_b) {
170
+ return undefined;
171
+ }
172
+ };
148
173
  const acquire = async (name, offline, options = {}) => {
149
174
  try {
150
175
  const topology = (0, exports.resolveTopology)();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@koralabs/kora-labs-common",
3
- "version": "6.14.0",
3
+ "version": "6.14.2",
4
4
  "description": "Kora Labs Common Utilities",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",