redis_queued_locks 0.0.22 → 0.0.23

Sign up to get free protection for your applications and to get access to all the features.
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