@koralabs/kora-labs-common 6.14.0 → 6.14.1
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/cronLock/cqlCronLock.d.ts +2 -0
- package/cronLock/cqlCronLock.js +26 -1
- package/package.json +1 -1
|
@@ -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.
|
package/cronLock/cqlCronLock.js
CHANGED
|
@@ -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)();
|