@koralabs/kora-labs-common 6.18.3 → 6.19.0
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.
|
@@ -80,7 +80,9 @@ const getImageDataFromDatum = async (apiHost, datum, options) => {
|
|
|
80
80
|
const image = (_b = (_a = decodedDatum === null || decodedDatum === void 0 ? void 0 : decodedDatum.constructor_0) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.image;
|
|
81
81
|
let data = { image };
|
|
82
82
|
const creatorDefaults = (_f = (_e = (_d = (_c = decodedDatum === null || decodedDatum === void 0 ? void 0 : decodedDatum.constructor_0) === null || _c === void 0 ? void 0 : _c[2]) === null || _d === void 0 ? void 0 : _d.constructor_0) === null || _e === void 0 ? void 0 : _e[0]) !== null && _f !== void 0 ? _f : (_g = decodedDatum.constructor_0) === null || _g === void 0 ? void 0 : _g[2];
|
|
83
|
-
|
|
83
|
+
// CIP-68 `extra` is arbitrary PlutusData; only a map is creator defaults (a bytestring like
|
|
84
|
+
// "@THERAF" is not, and used to throw in normalizeCreatorDefaultTextFields).
|
|
85
|
+
if (creatorDefaults && typeof creatorDefaults === 'object' && !Array.isArray(creatorDefaults)) {
|
|
84
86
|
(0, exports.normalizeCreatorDefaultTextFields)(creatorDefaults);
|
|
85
87
|
data = { image, creatorDefaults, metadata: (_h = decodedDatum === null || decodedDatum === void 0 ? void 0 : decodedDatum.constructor_0) === null || _h === void 0 ? void 0 : _h[0] };
|
|
86
88
|
}
|
|
@@ -48,6 +48,8 @@ export declare const isUnknownOutcomeError: (error: unknown) => boolean;
|
|
|
48
48
|
* concurrent ticks on one node cannot both treat the node identity as ownership.
|
|
49
49
|
*/
|
|
50
50
|
export declare const acquireWithExecutor: (executor: CqlExecutor, name: string, offline: string | null | undefined, topology: CronLockTopology, token?: string, lease?: number) => Promise<AcquireResult>;
|
|
51
|
+
/** One fresh process's acquisition: make sure the lock table exists, then take the lock. */
|
|
52
|
+
export declare const acquireOnExecutor: (executor: CqlExecutor, name: string, offline: string | null | undefined, topology: CronLockTopology, lease?: number) => Promise<AcquireResult>;
|
|
51
53
|
export declare const acquire: (name: string, offline?: string | null, options?: {
|
|
52
54
|
leaseMs?: number;
|
|
53
55
|
}) => Promise<AcquireResult>;
|
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.releaseWithExecutor = exports.acquire = exports.acquireWithExecutor = exports.isUnknownOutcomeError = exports.isWriteTimeoutError = exports.isPeerUnavailableError = exports.resolveMode = exports.parseOffline = exports.leaseMs = exports.resolveTopology = void 0;
|
|
3
|
+
exports.release = exports.releaseWithExecutor = exports.acquire = exports.acquireOnExecutor = exports.acquireWithExecutor = exports.isUnknownOutcomeError = 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
|
|
@@ -14,9 +14,9 @@ exports.release = exports.releaseWithExecutor = exports.acquire = exports.acquir
|
|
|
14
14
|
* - all boxes online: SERIAL / QUORUM;
|
|
15
15
|
* - this box offline: stand down;
|
|
16
16
|
* - this box is the only declared-online box: LOCAL_SERIAL / LOCAL_QUORUM;
|
|
17
|
-
* - an undeclared peer outage:
|
|
18
|
-
*
|
|
19
|
-
* CQL UNAVAILABLE response (never after a timeout or unknown failure).
|
|
17
|
+
* - an undeclared peer outage: 'peerUnavailable'. Callers whose job is safe on one
|
|
18
|
+
* box (on-chain/idempotent) may take a DC-local fallback, and only after this
|
|
19
|
+
* definite CQL UNAVAILABLE response (never after a timeout or unknown failure).
|
|
20
20
|
*
|
|
21
21
|
* The row TTL recovers crashed holders. Unique per-invocation owner tokens prevent
|
|
22
22
|
* overlapping ticks on one box from mistaking node identity for lock ownership.
|
|
@@ -70,7 +70,7 @@ const leaseSeconds = (override) => Math.ceil((0, exports.leaseMs)(override) / 10
|
|
|
70
70
|
const scopeName = (name, topology) => `${topology.network}:${name}`;
|
|
71
71
|
let client = null;
|
|
72
72
|
let clientTopologyKey = '';
|
|
73
|
-
|
|
73
|
+
const ensured = new WeakSet();
|
|
74
74
|
const getClient = (topology) => {
|
|
75
75
|
const topologyKey = JSON.stringify([topology.contactPoints, topology.localDataCenter]);
|
|
76
76
|
if (!client || clientTopologyKey !== topologyKey) {
|
|
@@ -80,17 +80,25 @@ const getClient = (topology) => {
|
|
|
80
80
|
socketOptions: { connectTimeout: 8000, readTimeout: 12000 }
|
|
81
81
|
});
|
|
82
82
|
clientTopologyKey = topologyKey;
|
|
83
|
-
ensured = false;
|
|
84
83
|
}
|
|
85
84
|
return client;
|
|
86
85
|
};
|
|
86
|
+
/**
|
|
87
|
+
* Schema changes need a majority of the whole cluster, so with a peer DC down a CREATE … IF NOT EXISTS
|
|
88
|
+
* hangs until the driver times out — masking the definite UNAVAILABLE the one-box solo path relies on
|
|
89
|
+
* (incident 2026-10-07). system_schema is replicated to every node (LocalStrategy), so reading it
|
|
90
|
+
* needs no peer; DDL is issued only on a cluster that really lacks the table.
|
|
91
|
+
*/
|
|
87
92
|
const ensureSchema = async (executor) => {
|
|
88
|
-
if (ensured)
|
|
93
|
+
if (ensured.has(executor))
|
|
89
94
|
return;
|
|
90
|
-
await executor.execute(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
const existing = await executor.execute('SELECT table_name FROM system_schema.tables WHERE keyspace_name = ? AND table_name = ?', [KEYSPACE, LOCK_TABLE], { prepare: true, consistency: cassandra_driver_1.types.consistencies.localOne });
|
|
96
|
+
if (existing.rows.length === 0) {
|
|
97
|
+
await executor.execute(`CREATE KEYSPACE IF NOT EXISTS ${KEYSPACE} WITH replication = ` +
|
|
98
|
+
`{'class':'NetworkTopologyStrategy','kora-dc1':1,'kora-dc2':1}`);
|
|
99
|
+
await executor.execute(`CREATE TABLE IF NOT EXISTS ${KEYSPACE}.${LOCK_TABLE} (name text PRIMARY KEY, owner text, acquired_at timestamp)`);
|
|
100
|
+
}
|
|
101
|
+
ensured.add(executor);
|
|
94
102
|
};
|
|
95
103
|
const parseOffline = (raw) => {
|
|
96
104
|
const value = (raw !== null && raw !== void 0 ? raw : '').trim().toLowerCase();
|
|
@@ -191,12 +199,16 @@ const deleteIfOwner = async (executor, key, token, serialConsistency, consistenc
|
|
|
191
199
|
return false;
|
|
192
200
|
}
|
|
193
201
|
};
|
|
202
|
+
/** One fresh process's acquisition: make sure the lock table exists, then take the lock. */
|
|
203
|
+
const acquireOnExecutor = async (executor, name, offline, topology, lease) => {
|
|
204
|
+
await ensureSchema(executor);
|
|
205
|
+
return (0, exports.acquireWithExecutor)(executor, name, offline, topology, undefined, lease);
|
|
206
|
+
};
|
|
207
|
+
exports.acquireOnExecutor = acquireOnExecutor;
|
|
194
208
|
const acquire = async (name, offline, options = {}) => {
|
|
195
209
|
try {
|
|
196
210
|
const topology = (0, exports.resolveTopology)();
|
|
197
|
-
const
|
|
198
|
-
await ensureSchema(executor);
|
|
199
|
-
const result = await (0, exports.acquireWithExecutor)(executor, name, offline, topology, undefined, options.leaseMs);
|
|
211
|
+
const result = await (0, exports.acquireOnExecutor)(getClient(topology), name, offline, topology, options.leaseMs);
|
|
200
212
|
if (result.status === 'peerUnavailable') {
|
|
201
213
|
logger_1.Logger.log({
|
|
202
214
|
message: `cron lock '${name}' SERIAL unavailable because the configured peer quorum is unavailable: ${result.message}`,
|