@nsshunt/stsfhirpg 1.1.6 → 1.2.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/dist/stsfhirpg.cjs +0 -220
- package/dist/stsfhirpg.cjs.map +1 -1
- package/dist/stsfhirpg.mjs +2 -222
- package/dist/stsfhirpg.mjs.map +1 -1
- package/package.json +2 -3
- package/types/index.d.ts +0 -2
- package/types/index.d.ts.map +1 -1
- package/types/redisDistributedLock.d.ts +0 -36
- package/types/redisDistributedLock.d.ts.map +0 -1
- package/types/searchParameterManager.d.ts +0 -13
- package/types/searchParameterManager.d.ts.map +0 -1
package/dist/stsfhirpg.cjs
CHANGED
|
@@ -15,7 +15,6 @@ const require$$1$2 = require("tls");
|
|
|
15
15
|
const require$$0$7 = require("path");
|
|
16
16
|
const require$$0$6 = require("stream");
|
|
17
17
|
const require$$0$5 = require("buffer");
|
|
18
|
-
const redis = require("redis");
|
|
19
18
|
class LuxonError extends Error {
|
|
20
19
|
}
|
|
21
20
|
class InvalidDateTimeError extends LuxonError {
|
|
@@ -18945,229 +18944,10 @@ class PGFhirAccessLayer extends tinyEmitterExports.TinyEmitter {
|
|
|
18945
18944
|
}
|
|
18946
18945
|
};
|
|
18947
18946
|
}
|
|
18948
|
-
const RELEASE_SCRIPT = `
|
|
18949
|
-
if redis.call("get", KEYS[1]) == ARGV[1] then
|
|
18950
|
-
return redis.call("del", KEYS[1])
|
|
18951
|
-
else
|
|
18952
|
-
return 0
|
|
18953
|
-
end
|
|
18954
|
-
`;
|
|
18955
|
-
const RENEW_SCRIPT = `
|
|
18956
|
-
if redis.call("get", KEYS[1]) == ARGV[1] then
|
|
18957
|
-
return redis.call("pexpire", KEYS[1], ARGV[2])
|
|
18958
|
-
else
|
|
18959
|
-
return 0
|
|
18960
|
-
end
|
|
18961
|
-
`;
|
|
18962
|
-
class RedisDistributedLock {
|
|
18963
|
-
client;
|
|
18964
|
-
ttlMs;
|
|
18965
|
-
heartbeatMs;
|
|
18966
|
-
keyPrefix;
|
|
18967
|
-
constructor(client2, options) {
|
|
18968
|
-
this.client = client2;
|
|
18969
|
-
this.ttlMs = options?.ttlMs ?? 3e4;
|
|
18970
|
-
this.heartbeatMs = options?.heartbeatMs ?? Math.floor(this.ttlMs / 3);
|
|
18971
|
-
this.keyPrefix = options?.keyPrefix ?? "lock:";
|
|
18972
|
-
if (this.ttlMs <= 0) {
|
|
18973
|
-
throw new Error("ttlMs must be > 0");
|
|
18974
|
-
}
|
|
18975
|
-
if (this.heartbeatMs <= 0) {
|
|
18976
|
-
throw new Error("heartbeatMs must be > 0");
|
|
18977
|
-
}
|
|
18978
|
-
if (this.heartbeatMs >= this.ttlMs) {
|
|
18979
|
-
throw new Error("heartbeatMs should be less than ttlMs");
|
|
18980
|
-
}
|
|
18981
|
-
}
|
|
18982
|
-
buildKey(name) {
|
|
18983
|
-
return `${this.keyPrefix}${name}`;
|
|
18984
|
-
}
|
|
18985
|
-
async acquire(name, ttlMs) {
|
|
18986
|
-
const key = this.buildKey(name);
|
|
18987
|
-
const token = require$$0.randomUUID();
|
|
18988
|
-
const effectiveTtlMs = ttlMs ?? this.ttlMs;
|
|
18989
|
-
const result2 = await this.client.set(key, token, {
|
|
18990
|
-
NX: true,
|
|
18991
|
-
PX: effectiveTtlMs
|
|
18992
|
-
});
|
|
18993
|
-
if (result2 !== "OK") {
|
|
18994
|
-
return null;
|
|
18995
|
-
}
|
|
18996
|
-
return {
|
|
18997
|
-
key,
|
|
18998
|
-
token,
|
|
18999
|
-
ttlMs: effectiveTtlMs
|
|
19000
|
-
};
|
|
19001
|
-
}
|
|
19002
|
-
async release(lock) {
|
|
19003
|
-
const result2 = await this.client.eval(RELEASE_SCRIPT, {
|
|
19004
|
-
keys: [lock.key],
|
|
19005
|
-
arguments: [lock.token]
|
|
19006
|
-
});
|
|
19007
|
-
return Number(result2) === 1;
|
|
19008
|
-
}
|
|
19009
|
-
async renew(lock, ttlMs) {
|
|
19010
|
-
const effectiveTtlMs = ttlMs ?? lock.ttlMs;
|
|
19011
|
-
const result2 = await this.client.eval(RENEW_SCRIPT, {
|
|
19012
|
-
keys: [lock.key],
|
|
19013
|
-
arguments: [lock.token, String(effectiveTtlMs)]
|
|
19014
|
-
});
|
|
19015
|
-
return Number(result2) === 1;
|
|
19016
|
-
}
|
|
19017
|
-
async isOwner(lock) {
|
|
19018
|
-
const currentValue = await this.client.get(lock.key);
|
|
19019
|
-
return currentValue === lock.token;
|
|
19020
|
-
}
|
|
19021
|
-
async runExclusive(name, task, options) {
|
|
19022
|
-
const lock = await this.acquire(name, options?.ttlMs);
|
|
19023
|
-
if (!lock) {
|
|
19024
|
-
if (options?.onLockNotAcquired) {
|
|
19025
|
-
await options.onLockNotAcquired();
|
|
19026
|
-
}
|
|
19027
|
-
return { acquired: false };
|
|
19028
|
-
}
|
|
19029
|
-
let timer = null;
|
|
19030
|
-
let stopped = false;
|
|
19031
|
-
let renewalError = null;
|
|
19032
|
-
const autoRenew = options?.autoRenew ?? false;
|
|
19033
|
-
const heartbeatMs = options?.heartbeatMs ?? this.heartbeatMs;
|
|
19034
|
-
const stopHeartbeat = () => {
|
|
19035
|
-
stopped = true;
|
|
19036
|
-
if (timer) {
|
|
19037
|
-
clearInterval(timer);
|
|
19038
|
-
timer = null;
|
|
19039
|
-
}
|
|
19040
|
-
};
|
|
19041
|
-
if (autoRenew) {
|
|
19042
|
-
timer = setInterval(async () => {
|
|
19043
|
-
if (stopped) {
|
|
19044
|
-
return;
|
|
19045
|
-
}
|
|
19046
|
-
try {
|
|
19047
|
-
const ok = await this.renew(lock, lock.ttlMs);
|
|
19048
|
-
if (!ok) {
|
|
19049
|
-
renewalError = new Error(
|
|
19050
|
-
`Lost lock ownership while renewing "${lock.key}"`
|
|
19051
|
-
);
|
|
19052
|
-
stopHeartbeat();
|
|
19053
|
-
}
|
|
19054
|
-
} catch (err) {
|
|
19055
|
-
renewalError = err instanceof Error ? err : new Error(String(err));
|
|
19056
|
-
stopHeartbeat();
|
|
19057
|
-
}
|
|
19058
|
-
}, heartbeatMs).unref();
|
|
19059
|
-
}
|
|
19060
|
-
try {
|
|
19061
|
-
const result2 = await task();
|
|
19062
|
-
if (renewalError) {
|
|
19063
|
-
throw renewalError;
|
|
19064
|
-
}
|
|
19065
|
-
return { acquired: true, result: result2 };
|
|
19066
|
-
} finally {
|
|
19067
|
-
stopHeartbeat();
|
|
19068
|
-
try {
|
|
19069
|
-
await this.release(lock);
|
|
19070
|
-
} catch {
|
|
19071
|
-
}
|
|
19072
|
-
}
|
|
19073
|
-
}
|
|
19074
|
-
}
|
|
19075
|
-
class SearchParameterManager {
|
|
19076
|
-
redis;
|
|
19077
|
-
EnsureSearchParameterDataLoaded = async (name, loadFn, options) => {
|
|
19078
|
-
if (!this.redis) {
|
|
19079
|
-
const redisUrl = stsconfig.goptions.imRedisMessageProcessorUrl;
|
|
19080
|
-
console.log(`SearchParameterManager(): redis trying to connect ...`);
|
|
19081
|
-
this.redis = redis.createClient({
|
|
19082
|
-
url: redisUrl
|
|
19083
|
-
});
|
|
19084
|
-
await this.redis.connect();
|
|
19085
|
-
console.log(`SearchParameterManager(): redis connected`);
|
|
19086
|
-
}
|
|
19087
|
-
try {
|
|
19088
|
-
const pollIntervalMs = options?.pollIntervalMs ?? 250;
|
|
19089
|
-
const lockTtlMs = options?.lockTtlMs ?? 3e4;
|
|
19090
|
-
const autoRenew = options?.autoRenew ?? true;
|
|
19091
|
-
const heartbeatMs = options?.heartbeatMs ?? 1e4;
|
|
19092
|
-
const completeTtlSeconds = options?.completeTtlSeconds;
|
|
19093
|
-
const timeoutMs = options?.timeoutMs;
|
|
19094
|
-
const completeKey = `fhir:searchparam:${name}:complete`;
|
|
19095
|
-
const startedAt = Date.now();
|
|
19096
|
-
const lockManager = new RedisDistributedLock(this.redis, {
|
|
19097
|
-
ttlMs: 3e4,
|
|
19098
|
-
heartbeatMs: 1e4,
|
|
19099
|
-
keyPrefix: "lock:"
|
|
19100
|
-
});
|
|
19101
|
-
while (true) {
|
|
19102
|
-
if (typeof timeoutMs === "number" && timeoutMs > 0) {
|
|
19103
|
-
const elapsedMs = Date.now() - startedAt;
|
|
19104
|
-
if (elapsedMs >= timeoutMs) {
|
|
19105
|
-
throw new Error(
|
|
19106
|
-
`Timed out waiting for search param "${name}" to be loaded`
|
|
19107
|
-
);
|
|
19108
|
-
}
|
|
19109
|
-
}
|
|
19110
|
-
try {
|
|
19111
|
-
const alreadyComplete = await this.redis.get(completeKey);
|
|
19112
|
-
if (alreadyComplete) {
|
|
19113
|
-
console.log(`ensureSearchParamLoaded():alreadyComplete: PID: [${process.pid}] data loaded: [${alreadyComplete}]`);
|
|
19114
|
-
return;
|
|
19115
|
-
}
|
|
19116
|
-
} catch (error) {
|
|
19117
|
-
console.error(error);
|
|
19118
|
-
throw error;
|
|
19119
|
-
}
|
|
19120
|
-
const runResult = await lockManager.runExclusive(
|
|
19121
|
-
`fhir:searchparam:${name}`,
|
|
19122
|
-
async () => {
|
|
19123
|
-
console.log(`ensureSearchParamLoaded(): PID: [${process.pid}] lock acquired`);
|
|
19124
|
-
const completeAfterLock = await this.redis.get(completeKey);
|
|
19125
|
-
if (completeAfterLock) {
|
|
19126
|
-
console.log(`ensureSearchParamLoaded():completeAfterLock: PID: [${process.pid}] data loaded: [${completeAfterLock}]`);
|
|
19127
|
-
return;
|
|
19128
|
-
}
|
|
19129
|
-
await loadFn();
|
|
19130
|
-
const completeValue = JSON.stringify({
|
|
19131
|
-
loaded: true,
|
|
19132
|
-
loadedAt: (/* @__PURE__ */ new Date()).toLocaleString(),
|
|
19133
|
-
name,
|
|
19134
|
-
workerPid: process.pid
|
|
19135
|
-
});
|
|
19136
|
-
console.log(`ensureSearchParamLoaded(): PID: [${process.pid}] data loaded: [${completeValue}]`);
|
|
19137
|
-
if (typeof completeTtlSeconds === "number" && completeTtlSeconds > 0) {
|
|
19138
|
-
await this.redis.set(completeKey, completeValue, {
|
|
19139
|
-
EX: completeTtlSeconds
|
|
19140
|
-
});
|
|
19141
|
-
} else {
|
|
19142
|
-
await this.redis.set(completeKey, completeValue);
|
|
19143
|
-
}
|
|
19144
|
-
},
|
|
19145
|
-
{
|
|
19146
|
-
ttlMs: lockTtlMs,
|
|
19147
|
-
autoRenew,
|
|
19148
|
-
heartbeatMs
|
|
19149
|
-
}
|
|
19150
|
-
);
|
|
19151
|
-
if (runResult.acquired) {
|
|
19152
|
-
return;
|
|
19153
|
-
}
|
|
19154
|
-
console.log(`ensureSearchParamLoaded(): PID: [${process.pid}] Did not get lock`);
|
|
19155
|
-
await stsutils.Sleep(pollIntervalMs);
|
|
19156
|
-
}
|
|
19157
|
-
} finally {
|
|
19158
|
-
if (this.redis) {
|
|
19159
|
-
this.redis.close();
|
|
19160
|
-
console.log(`SearchParameterManager(): redis closed`);
|
|
19161
|
-
}
|
|
19162
|
-
}
|
|
19163
|
-
};
|
|
19164
|
-
}
|
|
19165
18947
|
exports.DBSearchIndex = DBSearchIndex;
|
|
19166
18948
|
exports.FHIRDateUtils = FHIRDateUtils;
|
|
19167
18949
|
exports.PGFhirAccessLayer = PGFhirAccessLayer;
|
|
19168
|
-
exports.RedisDistributedLock = RedisDistributedLock;
|
|
19169
18950
|
exports.ResourceHelper = ResourceHelper;
|
|
19170
|
-
exports.SearchParameterManager = SearchParameterManager;
|
|
19171
18951
|
exports.hashReferenceParam = hashReferenceParam;
|
|
19172
18952
|
exports.hashStringParam = hashStringParam;
|
|
19173
18953
|
exports.hashTokenParam = hashTokenParam;
|