create-dacosta-proj 1.0.25 → 1.0.27

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-dacosta-proj",
3
- "version": "1.0.25",
3
+ "version": "1.0.27",
4
4
  "bin": {
5
5
  "create-dacosta-proj": "./index.js"
6
6
  }
@@ -6,6 +6,7 @@ const { SimpleSupabase } = require('simple-supabase');
6
6
  const { createClient: createSupabaseClient } = require('@supabase/supabase-js');
7
7
  const { createClient: createRedisClient } = require('redis');
8
8
  const redisLock = require('@/helpers/redisLock');
9
+ const wait = require('./wait');
9
10
 
10
11
  module.exports = async () => {
11
12
 
@@ -27,7 +28,17 @@ module.exports = async () => {
27
28
  // Redis
28
29
 
29
30
  const redis = createRedisClient();
30
- await redis.connect();
31
+ try {
32
+ await redis.connect();
33
+ while (true) {
34
+ try { await redis.ping(); break; }
35
+ catch (err) {
36
+ if (!err?.message?.includes('LOADING')) throw err;
37
+ await wait(500);
38
+ }
39
+ }
40
+ }
41
+ catch (err) { console.log('Redis Failed', err); return; }
31
42
 
32
43
  global.redis = redis;
33
44
  global.redisLock = redisLock(redis);
@@ -1,23 +1,39 @@
1
1
  'use strict';
2
2
 
3
- const DEFAULT_TIMEOUT = 5000;
3
+ const crypto = require('crypto');
4
+
5
+ const DEFAULT_TIMEOUT = 5_000;
4
6
  const DEFAULT_RETRY_DELAY = 50;
7
+ const DEFAULT_WAIT_TIMEOUT = 30_000;
8
+
9
+ // Atomic compare-and-delete so we only release a lock we still hold.
10
+ // Without this, an expired lock that another caller already re-acquired
11
+ // would be deleted by our release(), letting a third caller acquire it
12
+ // while the second is still inside its critical section.
13
+ const RELEASE_SCRIPT = 'if redis.call("GET", KEYS[1]) == ARGV[1] then return redis.call("DEL", KEYS[1]) else return 0 end';
5
14
 
6
15
  function redisLock(client, retryDelay = DEFAULT_RETRY_DELAY) {
7
- async function lock(lockName, timeout = DEFAULT_TIMEOUT) {
16
+ async function lock(lockName, timeout = DEFAULT_TIMEOUT, waitTimeout = DEFAULT_WAIT_TIMEOUT) {
8
17
  const lockKey = `lock.${lockName}`;
9
- const lockExpiry = Date.now() + timeout + 1;
18
+ const token = crypto.randomBytes(16).toString('hex');
19
+ const deadline = Date.now() + waitTimeout;
20
+ let released = false;
21
+
10
22
  while (true) {
11
- const result = await client.set(lockKey, lockExpiry, { PX: timeout, NX: true });
23
+ const result = await client.set(lockKey, token, { PX: timeout, NX: true });
12
24
  if (result !== null) {
13
25
  return async () => {
14
- if (lockExpiry > Date.now()) await client.del(lockKey);
26
+ if (released) return;
27
+ released = true;
28
+ try { await client.sendCommand(['EVAL', RELEASE_SCRIPT, '1', lockKey, token]); }
29
+ catch {}
15
30
  };
16
31
  }
32
+ if (Date.now() >= deadline) throw new Error(`redisLock: timed out waiting for "${lockName}" after ${waitTimeout}ms`);
17
33
  await new Promise(r => setTimeout(r, retryDelay));
18
34
  }
19
35
  }
20
36
  return lock;
21
37
  }
22
38
 
23
- module.exports = redisLock;
39
+ module.exports = redisLock;
File without changes