redis_queued_locks 0.0.22 → 0.0.23

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4360a9af038cc33ca36215d729558bab6810b9c5a4fec7f31df6ca2871b7dd80
4
- data.tar.gz: 10b80a0f389994fde9a4815c3a5031ae85cee3c61d9c2a02e85d80146208bb99
3
+ metadata.gz: 05a8fc14cae41bf9870bedf4eae9fa298c3704b57d25df428c2e2c8bd9bf0223
4
+ data.tar.gz: 14a941048edc7679df1d80805a96d3db4019d45acfc80ac5444d99e12cc3eadc
5
5
  SHA512:
6
- metadata.gz: 362e7708a37f8716217e08117f072ef34d14886677379d28bef08f80c1a1a82e1309509dfc5fe4f748a3a10268bf340087c83e797ebcd72cb8172594352fb23d
7
- data.tar.gz: 32a0943ee8c80b8ed745dff8eeea748c2289008d8fb00d3e20c7b464ef14a4d21158066e548bb86cfebc73c5d61a365289b49ae8061bc3ea0339ad7bb3c9894d
6
+ metadata.gz: 292f4ed74242a1a53b96e290e7759db0b1c918070f0875805bedbe78360fac71b6ac81ec09b63836002b2a17ab247ca06f9cf7ff828a4a07f924b2588e24fd3f
7
+ data.tar.gz: 270749b6a32418e2a57b3995b5ef3b826b6066a97a56efd08f9305f0ed314ed7da13f9f395f891ab619ceea3d4bde53c880278758fbf42fb1e84b98fa5b14690
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.0.23] - 2024-03-21
4
+ ### Changed
5
+ - Composed redis commands are invoked on the one conenction
6
+ (instead of mutiple connection fetchiong from redis connection pool on each redis command);
7
+
3
8
  ## [0.0.22] - 2024-03-21
4
9
  ### Added
5
10
  - Logging infrastructure. Initial implementation includes the only debugging features.
@@ -31,95 +31,101 @@ module RedisQueuedLocks::Acquier::AcquireLock::TryToLock
31
31
  timestamp = nil
32
32
 
33
33
  # Step 0: watch the lock key changes (and discard acquirement if lock is already acquired)
34
- result = redis.multi(watch: [lock_key]) do |transact|
35
- # Fast-Step X0: fail-fast check
36
- if fail_fast && redis.call('HGET', lock_key, 'acq_id')
37
- # Fast-Step X1: is lock already obtained. fail fast - no try.
38
- inter_result = :fail_fast_no_try
39
- else
40
- # Step 1: add an acquier to the lock acquirement queue
41
- res = redis.call('ZADD', lock_key_queue, 'NX', acquier_position, acquier_id)
42
-
43
- RedisQueuedLocks.debug(
44
- "Step №1: добавление в очередь (#{acquier_id}). [ZADD to the queue: #{res}]"
45
- )
46
-
47
- # Step 2.1: drop expired acquiers from the lock queue
48
- res = redis.call(
49
- 'ZREMRANGEBYSCORE',
50
- lock_key_queue,
51
- '-inf',
52
- RedisQueuedLocks::Resource.acquier_dead_score(queue_ttl)
53
- )
54
-
55
- RedisQueuedLocks.debug(
56
- "Step №2: дропаем из очереди просроченных ожидающих. [ZREMRANGE: #{res}]"
57
- )
58
-
59
- # Step 3: get the actual acquier waiting in the queue
60
- waiting_acquier = Array(redis.call('ZRANGE', lock_key_queue, '0', '0')).first
61
-
62
- RedisQueuedLocks.debug(
63
- "Step №3: какой процесс в очереди сейчас ждет. " \
64
- "[ZRANGE <следующий процесс>: #{waiting_acquier} :: <текущий процесс>: #{acquier_id}]"
65
- )
66
-
67
- # Step 4: check the actual acquier: is it ours? are we aready to lock?
68
- unless waiting_acquier == acquier_id
69
- # Step ROLLBACK 1.1: our time hasn't come yet. retry!
34
+ result = redis.with do |rconn|
35
+ rconn.multi(watch: [lock_key]) do |transact|
36
+ # Fast-Step X0: fail-fast check
37
+ if fail_fast && rconn.call('HGET', lock_key, 'acq_id')
38
+ # Fast-Step X1: is lock already obtained. fail fast - no try.
39
+ inter_result = :fail_fast_no_try
40
+ else
41
+ # Step 1: add an acquier to the lock acquirement queue
42
+ res = rconn.call('ZADD', lock_key_queue, 'NX', acquier_position, acquier_id)
70
43
 
71
44
  RedisQueuedLocks.debug(
72
- "Step ROLLBACK №1: не одинаковые ключи. выходим. " \
73
- "[Ждет: #{waiting_acquier}. А нужен: #{acquier_id}]"
45
+ "Step №1: добавление в очередь (#{acquier_id}). [ZADD to the queue: #{res}]"
74
46
  )
75
47
 
76
- inter_result = :acquier_is_not_first_in_queue
77
- else
78
- # NOTE: our time has come! let's try to acquire the lock!
48
+ # Step 2.1: drop expired acquiers from the lock queue
49
+ res = rconn.call(
50
+ 'ZREMRANGEBYSCORE',
51
+ lock_key_queue,
52
+ '-inf',
53
+ RedisQueuedLocks::Resource.acquier_dead_score(queue_ttl)
54
+ )
55
+
56
+ RedisQueuedLocks.debug(
57
+ "Step №2: дропаем из очереди просроченных ожидающих. [ZREMRANGE: #{res}]"
58
+ )
79
59
 
80
- # Step 5: check if the our lock is already acquired
81
- locked_by_acquier = redis.call('HGET', lock_key, 'acq_id')
60
+ # Step 3: get the actual acquier waiting in the queue
61
+ waiting_acquier = Array(rconn.call('ZRANGE', lock_key_queue, '0', '0')).first
82
62
 
83
63
  RedisQueuedLocks.debug(
84
- "Ste5: Ищем требуемый лок. " \
85
- "[HGET<#{lock_key}>: " \
86
- "#{(locked_by_acquier == nil) ? 'не занят' : "занят процессом <#{locked_by_acquier}>"}"
64
+ "Step3: какой процесс в очереди сейчас ждет. " \
65
+ "[ZRANGE <следующий процесс>: #{waiting_acquier} :: <текущий процесс>: #{acquier_id}]"
87
66
  )
88
67
 
89
- if locked_by_acquier
90
- # Step ROLLBACK 2: required lock is stil acquired. retry!
68
+ # Step 4: check the actual acquier: is it ours? are we aready to lock?
69
+ unless waiting_acquier == acquier_id
70
+ # Step ROLLBACK 1.1: our time hasn't come yet. retry!
91
71
 
92
72
  RedisQueuedLocks.debug(
93
- "Step ROLLBACK №2: Ключ уже занят. Ничего не делаем. " \
94
- "[Занят процессом: #{locked_by_acquier}]"
73
+ "Step ROLLBACK №1: не одинаковые ключи. выходим. " \
74
+ "[Ждет: #{waiting_acquier}. А нужен: #{acquier_id}]"
95
75
  )
96
76
 
97
- inter_result = :lock_is_still_acquired
77
+ inter_result = :acquier_is_not_first_in_queue
98
78
  else
99
- # NOTE: required lock is free and ready to be acquired! acquire!
79
+ # NOTE: our time has come! let's try to acquire the lock!
100
80
 
101
- # Step 6.1: remove our acquier from waiting queue
102
- transact.call('ZPOPMIN', lock_key_queue, '1')
81
+ # Step 5: check if the our lock is already acquired
82
+ locked_by_acquier = rconn.call('HGET', lock_key, 'acq_id')
103
83
 
84
+ # rubocop:disable Layout/LineLength
104
85
  RedisQueuedLocks.debug(
105
- 'Step4: Забираем наш текущий процесс из очереди. [ZPOPMIN]'
86
+ "Ste5: Ищем требуемый лок. " \
87
+ "[HGET<#{lock_key}>: " \
88
+ "#{(locked_by_acquier == nil) ? 'не занят' : "занят процессом <#{locked_by_acquier}>"}"
106
89
  )
107
-
108
- RedisQueuedLocks.debug(
109
- "===> <FINAL> Step №6: закрепляем лок за процессом [HSET<#{lock_key}>: #{acquier_id}]"
110
- )
111
-
112
- # Step 6.2: acquire a lock and store an info about the acquier
113
- transact.call(
114
- 'HSET',
115
- lock_key,
116
- 'acq_id', acquier_id,
117
- 'ts', (timestamp = Time.now.to_i),
118
- 'ini_ttl', ttl
119
- )
120
-
121
- # Step 6.3: set the lock expiration time in order to prevent "infinite locks"
122
- transact.call('PEXPIRE', lock_key, ttl) # NOTE: in milliseconds
90
+ # rubocop:enable Layout/LineLength
91
+
92
+ if locked_by_acquier
93
+ # Step ROLLBACK 2: required lock is stil acquired. retry!
94
+
95
+ RedisQueuedLocks.debug(
96
+ "Step ROLLBACK №2: Ключ уже занят. Ничего не делаем. " \
97
+ "[Занят процессом: #{locked_by_acquier}]"
98
+ )
99
+
100
+ inter_result = :lock_is_still_acquired
101
+ else
102
+ # NOTE: required lock is free and ready to be acquired! acquire!
103
+
104
+ # Step 6.1: remove our acquier from waiting queue
105
+ transact.call('ZPOPMIN', lock_key_queue, '1')
106
+
107
+ RedisQueuedLocks.debug(
108
+ 'Step №4: Забираем наш текущий процесс из очереди. [ZPOPMIN]'
109
+ )
110
+
111
+ # rubocop:disable Layout/LineLength
112
+ RedisQueuedLocks.debug(
113
+ "===> <FINAL> Step №6: закрепляем лок за процессом [HSET<#{lock_key}>: #{acquier_id}]"
114
+ )
115
+ # rubocop:enable Layout/LineLength
116
+
117
+ # Step 6.2: acquire a lock and store an info about the acquier
118
+ transact.call(
119
+ 'HSET',
120
+ lock_key,
121
+ 'acq_id', acquier_id,
122
+ 'ts', (timestamp = Time.now.to_i),
123
+ 'ini_ttl', ttl
124
+ )
125
+
126
+ # Step 6.3: set the lock expiration time in order to prevent "infinite locks"
127
+ transact.call('PEXPIRE', lock_key, ttl) # NOTE: in milliseconds
128
+ end
123
129
  end
124
130
  end
125
131
  end
@@ -57,26 +57,28 @@ module RedisQueuedLocks::Acquier::ReleaseAllLocks
57
57
  # @api private
58
58
  # @since 0.1.0
59
59
  def fully_release_all_locks(redis, batch_size)
60
- result = redis.pipelined do |pipeline|
61
- # Step A: release all queus and their related locks
62
- redis.scan(
63
- 'MATCH',
64
- RedisQueuedLocks::Resource::LOCK_QUEUE_PATTERN,
65
- count: batch_size
66
- ) do |lock_queue|
67
- # TODO: reduce unnecessary iterations
68
- pipeline.call('ZREMRANGEBYSCORE', lock_queue, '-inf', '+inf')
69
- pipeline.call('EXPIRE', RedisQueuedLocks::Resource.lock_key_from_queue(lock_queue), '0')
70
- end
60
+ result = redis.with do |rconn|
61
+ rconn.pipelined do |pipeline|
62
+ # Step A: release all queus and their related locks
63
+ rconn.scan(
64
+ 'MATCH',
65
+ RedisQueuedLocks::Resource::LOCK_QUEUE_PATTERN,
66
+ count: batch_size
67
+ ) do |lock_queue|
68
+ # TODO: reduce unnecessary iterations
69
+ pipeline.call('ZREMRANGEBYSCORE', lock_queue, '-inf', '+inf')
70
+ pipeline.call('EXPIRE', RedisQueuedLocks::Resource.lock_key_from_queue(lock_queue), '0')
71
+ end
71
72
 
72
- # Step B: release all locks
73
- redis.scan(
74
- 'MATCH',
75
- RedisQueuedLocks::Resource::LOCK_PATTERN,
76
- count: batch_size
77
- ) do |lock_key|
78
- # TODO: reduce unnecessary iterations
79
- pipeline.call('EXPIRE', lock_key, '0')
73
+ # Step B: release all locks
74
+ rconn.scan(
75
+ 'MATCH',
76
+ RedisQueuedLocks::Resource::LOCK_PATTERN,
77
+ count: batch_size
78
+ ) do |lock_key|
79
+ # TODO: reduce unnecessary iterations
80
+ pipeline.call('EXPIRE', lock_key, '0')
81
+ end
80
82
  end
81
83
  end
82
84
 
@@ -65,9 +65,11 @@ module RedisQueuedLocks::Acquier::ReleaseLock
65
65
  # @api private
66
66
  # @since 0.1.0
67
67
  def fully_release_lock(redis, lock_key, lock_key_queue)
68
- result = redis.multi do |transact|
69
- transact.call('ZREMRANGEBYSCORE', lock_key_queue, '-inf', '+inf')
70
- transact.call('EXPIRE', lock_key, '0')
68
+ result = redis.with do |rconn|
69
+ rconn.multi do |transact|
70
+ transact.call('ZREMRANGEBYSCORE', lock_key_queue, '-inf', '+inf')
71
+ transact.call('EXPIRE', lock_key, '0')
72
+ end
71
73
  end
72
74
 
73
75
  RedisQueuedLocks::Data[ok: true, result:]
@@ -5,6 +5,6 @@ module RedisQueuedLocks
5
5
  #
6
6
  # @api public
7
7
  # @since 0.0.1
8
- # @version 0.0.22
9
- VERSION = '0.0.22'
8
+ # @version 0.0.23
9
+ VERSION = '0.0.23'
10
10
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis_queued_locks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.22
4
+ version: 0.0.23
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rustam Ibragimov