@cap-js-community/event-queue 0.1.50 → 0.1.52

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,9 +36,7 @@ class PerformanceTracer {
36
36
  //determine, if an options object was provided as first argument
37
37
  if (
38
38
  typeof args?.[0] === "object" &&
39
- (args[0].quantity >= 0 ||
40
- args[0].threshold > 0 ||
41
- args[0].additionalQuantityThreshold > 0)
39
+ (args[0].quantity >= 0 || args[0].threshold > 0 || args[0].additionalQuantityThreshold > 0)
42
40
  ) {
43
41
  options = args.shift();
44
42
  }
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+
3
+ const COMPONENT = "eventQueue/SetIntervalDriftSafe";
4
+
5
+ class SetIntervalDriftSafe {
6
+ #adjustedInterval;
7
+ #interval;
8
+ #expectedCycleTime = 0;
9
+ #nextTickScheduledFor;
10
+ #logger;
11
+
12
+ constructor(interval) {
13
+ this.#interval = interval;
14
+ this.#adjustedInterval = interval;
15
+ this.#logger = cds.log(COMPONENT);
16
+ }
17
+
18
+ run(fn) {
19
+ const now = Date.now();
20
+ if (this.#expectedCycleTime === 0) {
21
+ this.#expectedCycleTime = now + this.#interval;
22
+ } else if (now + this.#interval - this.#nextTickScheduledFor < this.#interval) {
23
+ this.#logger.info("overlapping ticks, skipping this run");
24
+ return;
25
+ } else {
26
+ this.#adjustedInterval = this.#interval - (now - this.#expectedCycleTime);
27
+ this.#expectedCycleTime += this.#interval;
28
+ }
29
+ this.#nextTickScheduledFor = now + this.#adjustedInterval;
30
+ setTimeout(() => {
31
+ this.run(fn);
32
+ fn();
33
+ }, this.#adjustedInterval);
34
+ }
35
+ }
36
+
37
+ module.exports = SetIntervalDriftSafe;
@@ -32,31 +32,20 @@ class WorkerQueue {
32
32
  this.__runningPromises.push(promise);
33
33
  promise
34
34
  .finally(() => {
35
- this.__runningPromises.splice(
36
- this.__runningPromises.indexOf(promise),
37
- 1
38
- );
35
+ this.__runningPromises.splice(this.__runningPromises.indexOf(promise), 1);
39
36
  this._checkForNext();
40
37
  })
41
38
  .then((...results) => {
42
39
  resolve(...results);
43
40
  })
44
41
  .catch((err) => {
45
- cds
46
- .log(COMPONENT_NAME)
47
- .error(
48
- "Error happened in WorkQueue. Errors should be caught before! Error:",
49
- err
50
- );
42
+ cds.log(COMPONENT_NAME).error("Error happened in WorkQueue. Errors should be caught before! Error:", err);
51
43
  reject(err);
52
44
  });
53
45
  }
54
46
 
55
47
  _checkForNext() {
56
- if (
57
- !this.__queue.length ||
58
- this.__runningPromises.length >= this.__concurrencyLimit
59
- ) {
48
+ if (!this.__queue.length || this.__runningPromises.length >= this.__concurrencyLimit) {
60
49
  return;
61
50
  }
62
51
  const [cb, resolve, reject] = this.__queue.shift();
@@ -20,13 +20,7 @@ const COMPONENT_NAME = "eventQueue/cdsHelper";
20
20
  * @param info {object} Additional information object attached to logging
21
21
  * @returns {Promise<boolean>} Promise resolving to true if everything worked fine / false if an error occurred
22
22
  */
23
- async function executeInNewTransaction(
24
- context = {},
25
- transactionTag,
26
- fn,
27
- args,
28
- { info = {} } = {}
29
- ) {
23
+ async function executeInNewTransaction(context = {}, transactionTag, fn, args, { info = {} } = {}) {
30
24
  const parameters = Array.isArray(args) ? args : [args];
31
25
  const logger = cds.log(COMPONENT_NAME);
32
26
  try {
@@ -48,10 +42,7 @@ async function executeInNewTransaction(
48
42
  } else {
49
43
  const contextTx = cds.tx(context);
50
44
  const contextTxState = contextTx.ready;
51
- if (
52
- !contextTxState ||
53
- ["committed", "rolled back"].includes(contextTxState)
54
- ) {
45
+ if (!contextTxState || ["committed", "rolled back"].includes(contextTxState)) {
55
46
  await cds.tx(
56
47
  {
57
48
  id: context.id,
@@ -56,10 +56,7 @@ class Funnel {
56
56
  }
57
57
 
58
58
  // map function call to promise
59
- const p =
60
- f.constructor.name === "AsyncFunction"
61
- ? f(...args)
62
- : Promise.resolve().then(() => f(...args));
59
+ const p = f.constructor.name === "AsyncFunction" ? f(...args) : Promise.resolve().then(() => f(...args));
63
60
 
64
61
  // create promise for book keeping
65
62
  const workload = p.finally(() => {
@@ -100,9 +97,7 @@ const limiter = async (limit, payloads, iterator) => {
100
97
  returnPromises.push(p);
101
98
 
102
99
  if (limit <= payloads.length) {
103
- const e = p
104
- .catch(() => {})
105
- .finally(() => runningPromises.splice(runningPromises.indexOf(e), 1));
100
+ const e = p.catch(() => {}).finally(() => runningPromises.splice(runningPromises.indexOf(e), 1));
106
101
  runningPromises.push(e);
107
102
  if (limit <= runningPromises.length) {
108
103
  await Promise.race(runningPromises);
@@ -8,10 +8,7 @@ const { getConfigInstance } = require("../config");
8
8
  const acquireLock = async (
9
9
  context,
10
10
  key,
11
- {
12
- tenantScoped = true,
13
- expiryTime = config.getConfigInstance().globalTxTimeout,
14
- } = {}
11
+ { tenantScoped = true, expiryTime = config.getConfigInstance().globalTxTimeout } = {}
15
12
  ) => {
16
13
  const fullKey = _generateKey(context, tenantScoped, key);
17
14
  if (config.getConfigInstance().redisEnabled) {
@@ -25,11 +22,7 @@ const setValueWithExpire = async (
25
22
  context,
26
23
  key,
27
24
  value,
28
- {
29
- tenantScoped = true,
30
- expiryTime = config.getConfigInstance().globalTxTimeout,
31
- overrideValue = false,
32
- } = {}
25
+ { tenantScoped = true, expiryTime = config.getConfigInstance().globalTxTimeout, overrideValue = false } = {}
33
26
  ) => {
34
27
  const fullKey = _generateKey(context, tenantScoped, key);
35
28
  if (config.getConfigInstance().redisEnabled) {
@@ -54,11 +47,7 @@ const releaseLock = async (context, key, { tenantScoped = true } = {}) => {
54
47
  }
55
48
  };
56
49
 
57
- const checkLockExistsAndReturnValue = async (
58
- context,
59
- key,
60
- { tenantScoped = true } = {}
61
- ) => {
50
+ const checkLockExistsAndReturnValue = async (context, key, { tenantScoped = true } = {}) => {
62
51
  const fullKey = _generateKey(context, tenantScoped, key);
63
52
  if (config.getConfigInstance().redisEnabled) {
64
53
  return await _checkLockExistsRedis(context, fullKey);
@@ -67,12 +56,7 @@ const checkLockExistsAndReturnValue = async (
67
56
  }
68
57
  };
69
58
 
70
- const _acquireLockRedis = async (
71
- context,
72
- fullKey,
73
- expiryTime,
74
- { value = "true", overrideValue = false } = {}
75
- ) => {
59
+ const _acquireLockRedis = async (context, fullKey, expiryTime, { value = "true", overrideValue = false } = {}) => {
76
60
  const client = await redis.createMainClientAndConnect();
77
61
  const result = await client.set(fullKey, value, {
78
62
  PX: expiryTime,
@@ -89,17 +73,9 @@ const _checkLockExistsRedis = async (context, fullKey) => {
89
73
  const _checkLockExistsDb = async (context, fullKey) => {
90
74
  let result;
91
75
  const configInstance = getConfigInstance();
92
- await cdsHelper.executeInNewTransaction(
93
- context,
94
- "distributedLock-checkExists",
95
- async (tx) => {
96
- result = await tx.run(
97
- SELECT.one
98
- .from(configInstance.tableNameEventLock)
99
- .where("code =", fullKey)
100
- );
101
- }
102
- );
76
+ await cdsHelper.executeInNewTransaction(context, "distributedLock-checkExists", async (tx) => {
77
+ result = await tx.run(SELECT.one.from(configInstance.tableNameEventLock).where("code =", fullKey));
78
+ });
103
79
  return result?.value;
104
80
  };
105
81
 
@@ -110,69 +86,49 @@ const _releaseLockRedis = async (context, fullKey) => {
110
86
 
111
87
  const _releaseLockDb = async (context, fullKey) => {
112
88
  const configInstance = getConfigInstance();
113
- await cdsHelper.executeInNewTransaction(
114
- context,
115
- "distributedLock-release",
116
- async (tx) => {
117
- await tx.run(
118
- DELETE.from(configInstance.tableNameEventLock).where("code =", fullKey)
119
- );
120
- }
121
- );
89
+ await cdsHelper.executeInNewTransaction(context, "distributedLock-release", async (tx) => {
90
+ await tx.run(DELETE.from(configInstance.tableNameEventLock).where("code =", fullKey));
91
+ });
122
92
  };
123
93
 
124
- const _acquireLockDB = async (
125
- context,
126
- fullKey,
127
- expiryTime,
128
- { value = "true", overrideValue = false } = {}
129
- ) => {
94
+ const _acquireLockDB = async (context, fullKey, expiryTime, { value = "true", overrideValue = false } = {}) => {
130
95
  let result;
131
96
  const configInstance = getConfigInstance();
132
- await cdsHelper.executeInNewTransaction(
133
- context,
134
- "distributedLock-acquire",
135
- async (tx) => {
136
- try {
97
+ await cdsHelper.executeInNewTransaction(context, "distributedLock-acquire", async (tx) => {
98
+ try {
99
+ await tx.run(
100
+ INSERT.into(configInstance.tableNameEventLock).entries({
101
+ code: fullKey,
102
+ value,
103
+ })
104
+ );
105
+ result = true;
106
+ } catch (err) {
107
+ let currentEntry;
108
+
109
+ if (!overrideValue) {
110
+ currentEntry = await tx.run(
111
+ SELECT.one
112
+ .from(configInstance.tableNameEventLock)
113
+ .forUpdate({ wait: config.getConfigInstance().forUpdateTimeout })
114
+ .where("code =", fullKey)
115
+ );
116
+ }
117
+ if (overrideValue || (currentEntry && new Date(currentEntry.createdAt).getTime() + expiryTime <= Date.now())) {
137
118
  await tx.run(
138
- INSERT.into(configInstance.tableNameEventLock).entries({
139
- code: fullKey,
140
- value,
141
- })
119
+ UPDATE.entity(configInstance.tableNameEventLock)
120
+ .set({
121
+ createdAt: new Date().toISOString(),
122
+ value,
123
+ })
124
+ .where("code =", currentEntry.code)
142
125
  );
143
126
  result = true;
144
- } catch (err) {
145
- let currentEntry;
146
-
147
- if (!overrideValue) {
148
- currentEntry = await tx.run(
149
- SELECT.one
150
- .from(configInstance.tableNameEventLock)
151
- .forUpdate({ wait: config.getConfigInstance().forUpdateTimeout })
152
- .where("code =", fullKey)
153
- );
154
- }
155
- if (
156
- overrideValue ||
157
- (currentEntry &&
158
- new Date(currentEntry.createdAt).getTime() + expiryTime <=
159
- Date.now())
160
- ) {
161
- await tx.run(
162
- UPDATE.entity(configInstance.tableNameEventLock)
163
- .set({
164
- createdAt: new Date().toISOString(),
165
- value,
166
- })
167
- .where("code =", currentEntry.code)
168
- );
169
- result = true;
170
- } else {
171
- result = false;
172
- }
127
+ } else {
128
+ result = false;
173
129
  }
174
130
  }
175
- );
131
+ });
176
132
  return result;
177
133
  };
178
134
 
@@ -15,9 +15,7 @@ const createMainClientAndConnect = () => {
15
15
  }
16
16
 
17
17
  const errorHandlerCreateClient = (err) => {
18
- cds
19
- .log(COMPONENT_NAME)
20
- .error("error from redis client for pub/sub failed", err);
18
+ cds.log(COMPONENT_NAME).error("error from redis client for pub/sub failed", err);
21
19
  subscriberClientPromise = null;
22
20
  setTimeout(createMainClientAndConnect, 5 * 1000).unref();
23
21
  };