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