redis-unique-queue 1.0.3 → 2.0
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 +4 -4
- data/README.md +4 -4
- data/lib/redis_unique_queue.rb +179 -0
- data/redis-unique-queue.gemspec +2 -2
- data/spec/redis_unique_queue_spec.rb +150 -0
- data/spec/spec_helper.rb +2 -0
- metadata +9 -5
- data/lib/redis/unique/queue.rb +0 -155
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: de78d99306d017bd477d116439e987481571fcbc
|
4
|
+
data.tar.gz: 313940b8fe19ff797910d5b9fb0997d4e08879f7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1f918cd173c15ea8d164aa6f7ac277752701d2af2c6e90bfbd5876b12630445ee2453a8260e449ce8fc3c779514f8a279d5c543af16fb35da550eca34827402d
|
7
|
+
data.tar.gz: 4f48d23ef3510e98e76cc8270c4cda303ffa8749c185767a7055b56c74130cac10abd4a3df27cc8d7b1f7b2ab7e59e64ee816d7af706d230f11f84aba1a5a178
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# RedisUniqueQueue
|
2
2
|
|
3
3
|
A unique FIFO queue with atomic operations built on top of Redis. Useful if you want to enqueue data without worrying about it existing multiple times in the queue.
|
4
4
|
|
@@ -22,19 +22,19 @@ Or install it yourself as:
|
|
22
22
|
You can instantiate a named queue using your default Redis configuration.
|
23
23
|
|
24
24
|
```ruby
|
25
|
-
q =
|
25
|
+
q = RedisUniqueQueue.new 'jobs'
|
26
26
|
```
|
27
27
|
|
28
28
|
Or you can pass in your own instance of the Redis class.
|
29
29
|
|
30
30
|
```ruby
|
31
|
-
q =
|
31
|
+
q = RedisUniqueQueue.new 'jobs', Redis.new(:host => "10.0.1.1", :port => 6380, :db => 15)
|
32
32
|
```
|
33
33
|
|
34
34
|
A third option is to instead pass your Redis configurations.
|
35
35
|
|
36
36
|
```ruby
|
37
|
-
q =
|
37
|
+
q = RedisUniqueQueue.new 'jobs', :host => "10.0.1.1", :port => 6380, :db => 15
|
38
38
|
```
|
39
39
|
|
40
40
|
## Using the queue
|
@@ -0,0 +1,179 @@
|
|
1
|
+
require "redis"
|
2
|
+
|
3
|
+
class RedisUniqueQueue
|
4
|
+
attr_reader :name
|
5
|
+
|
6
|
+
VERSION = "2.0"
|
7
|
+
|
8
|
+
class InvalidNameException < StandardError; end;
|
9
|
+
class InvalidRedisConfigException < StandardError; end;
|
10
|
+
|
11
|
+
def initialize(name, redis_or_options = {}, more_options = {})
|
12
|
+
name = name.to_s if name.kind_of? Symbol
|
13
|
+
|
14
|
+
raise InvalidNameException.new unless name.kind_of?(String) && name.size > 0
|
15
|
+
@name = name
|
16
|
+
@redis = if redis_or_options.kind_of?(Redis)
|
17
|
+
redis_or_options
|
18
|
+
elsif redis_or_options.kind_of? Hash
|
19
|
+
::Redis.new redis_or_options
|
20
|
+
elsif defined?(ActiveSupport::Cache::RedisStore) && redis_or_options.kind_of?(ActiveSupport::Cache::RedisStore)
|
21
|
+
@pooled = redis_or_options.data.kind_of?(ConnectionPool)
|
22
|
+
redis_or_options.data
|
23
|
+
elsif defined?(ConnectionPool) && redis_or_options.kind_of?(ConnectionPool)
|
24
|
+
@pooled = true
|
25
|
+
redis_or_options
|
26
|
+
else
|
27
|
+
raise InvalidRedisConfigException.new
|
28
|
+
end
|
29
|
+
|
30
|
+
if more_options.kind_of?(Hash) && more_options[:expire]
|
31
|
+
expire more_options[:expire]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def push data
|
36
|
+
[block_on_atomic_attempt { attempt_atomic_push_multi(data) }].flatten.first
|
37
|
+
end
|
38
|
+
|
39
|
+
def push_multi *values
|
40
|
+
if values.size > 0
|
41
|
+
block_on_atomic_attempt { attempt_atomic_push_multi(*values) }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def pop
|
46
|
+
block_on_atomic_attempt { attempt_atomic_pop }
|
47
|
+
end
|
48
|
+
|
49
|
+
def pop_all
|
50
|
+
block_on_atomic_attempt { attempt_atomic_pop_all }
|
51
|
+
end
|
52
|
+
|
53
|
+
def pop_multi amount
|
54
|
+
block_on_atomic_attempt { attempt_atomic_pop_multi amount }
|
55
|
+
end
|
56
|
+
|
57
|
+
def front
|
58
|
+
with { |redis| redis.zrange(name, 0, 0).first }
|
59
|
+
end
|
60
|
+
|
61
|
+
def back
|
62
|
+
with { |redis| redis.zrevrange(name, 0, 0).first }
|
63
|
+
end
|
64
|
+
|
65
|
+
def remove data
|
66
|
+
with { |redis| redis.zrem name, data }
|
67
|
+
end
|
68
|
+
|
69
|
+
def remove_item_by_index index
|
70
|
+
with { |redis| redis.zremrangebyrank name, index, index }
|
71
|
+
end
|
72
|
+
|
73
|
+
def size
|
74
|
+
with { |redis| redis.zcard name }
|
75
|
+
end
|
76
|
+
|
77
|
+
def all
|
78
|
+
peek 0, size
|
79
|
+
end
|
80
|
+
|
81
|
+
def peek index, amount = 1
|
82
|
+
with { |redis| redis.zrange name, index, index + amount - 1 }
|
83
|
+
end
|
84
|
+
|
85
|
+
def include? data
|
86
|
+
!with { |redis| redis.zscore(name, data).nil? }
|
87
|
+
end
|
88
|
+
|
89
|
+
def clear
|
90
|
+
with { |redis| redis.del name }
|
91
|
+
[]
|
92
|
+
end
|
93
|
+
|
94
|
+
def expire seconds
|
95
|
+
with { |redis| redis.expire name, seconds }
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def max_score
|
101
|
+
with { |redis| redis.zscore name, back }
|
102
|
+
end
|
103
|
+
|
104
|
+
def attempt_atomic_push_multi *values
|
105
|
+
with do |redis|
|
106
|
+
success = redis.watch(name) do
|
107
|
+
score = [Time.now.to_f, max_score].compact.max
|
108
|
+
values = values.first if 1 == values.size && values.first.kind_of?(Array)
|
109
|
+
scored_values = []
|
110
|
+
values.each_with_index do |value, i|
|
111
|
+
scored_values << [score + i, value]
|
112
|
+
end
|
113
|
+
redis.multi do |multi|
|
114
|
+
multi.zadd name, scored_values
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
[success, values]
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def attempt_atomic_pop_multi amount
|
123
|
+
attempt_atomic_read_write lambda { peek 0, amount }, lambda { |multi, read_result| multi.zremrangebyrank name, 0, amount - 1 }
|
124
|
+
end
|
125
|
+
|
126
|
+
def attempt_atomic_pop_all
|
127
|
+
attempt_atomic_read_write lambda { all }, lambda { |multi, read_result| multi.del name }
|
128
|
+
end
|
129
|
+
|
130
|
+
def attempt_atomic_pop
|
131
|
+
read = lambda do
|
132
|
+
with{|redis| redis.zrangebyscore(name, 0, max_score, :with_scores => false, :limit => [0, 1]).first}
|
133
|
+
end
|
134
|
+
|
135
|
+
write = lambda do |multi, read_result|
|
136
|
+
multi.zrem name, read_result
|
137
|
+
end
|
138
|
+
|
139
|
+
attempt_atomic_read_write read, write
|
140
|
+
end
|
141
|
+
|
142
|
+
def block_on_atomic_attempt
|
143
|
+
begin
|
144
|
+
success, result = yield
|
145
|
+
end while !success && result
|
146
|
+
result
|
147
|
+
end
|
148
|
+
|
149
|
+
def attempt_atomic_read_write read_op, write_op
|
150
|
+
result = nil
|
151
|
+
success = with do |redis|
|
152
|
+
redis.watch(name) do
|
153
|
+
result = read_op.call
|
154
|
+
if result
|
155
|
+
redis.multi do |multi|
|
156
|
+
write_op.call multi, result
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
[success, result]
|
163
|
+
end
|
164
|
+
|
165
|
+
private
|
166
|
+
|
167
|
+
def with(&block)
|
168
|
+
if pooled?
|
169
|
+
@redis.with(&block)
|
170
|
+
else
|
171
|
+
block.call(@redis)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def pooled?
|
176
|
+
!!@pooled
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
data/redis-unique-queue.gemspec
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
lib = File.expand_path('../lib', __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require '
|
4
|
+
require 'redis_unique_queue'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "redis-unique-queue"
|
8
|
-
spec.version =
|
8
|
+
spec.version = RedisUniqueQueue::VERSION
|
9
9
|
spec.authors = ["Misha Conway"]
|
10
10
|
spec.email = ["mishaAconway@gmail.com"]
|
11
11
|
spec.summary = %q{A unique queue with atomic operations implemented in Redis}
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RedisUniqueQueue do
|
4
|
+
let(:redis) { Redis.new }
|
5
|
+
let(:name) { "some_queue" }
|
6
|
+
let(:queue) { described_class.new(name, redis) }
|
7
|
+
|
8
|
+
before do
|
9
|
+
redis.flushall
|
10
|
+
end
|
11
|
+
|
12
|
+
context "instance methods" do
|
13
|
+
describe '#push' do
|
14
|
+
it 'should push items to the queue' do
|
15
|
+
expect(queue.size).to eq(0)
|
16
|
+
queue.push('a')
|
17
|
+
queue.push('b')
|
18
|
+
queue.push('c')
|
19
|
+
expect(queue.size).to eq(3)
|
20
|
+
expect(queue.all).to eq(%w(a b c))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#push_multi' do
|
25
|
+
it 'should push multiple items to the queue' do
|
26
|
+
expect(queue.size).to eq(0)
|
27
|
+
queue.push_multi('a', 'b', 'c')
|
28
|
+
expect(queue.size).to eq(3)
|
29
|
+
expect(queue.all).to eq(%w(a b c))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context do
|
34
|
+
before do
|
35
|
+
expect(queue.size).to eq(0)
|
36
|
+
queue.push('a')
|
37
|
+
queue.push('b')
|
38
|
+
queue.push('c')
|
39
|
+
expect(queue.size).to eq(3)
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#pop' do
|
43
|
+
it 'should pop single items from the queue' do
|
44
|
+
expect(queue.pop).to eq('a')
|
45
|
+
expect(queue.size).to eq(2)
|
46
|
+
|
47
|
+
expect(queue.pop).to eq('b')
|
48
|
+
expect(queue.size).to eq(1)
|
49
|
+
|
50
|
+
expect(queue.pop).to eq('c')
|
51
|
+
expect(queue.size).to eq(0)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe '#pop_all' do
|
56
|
+
it 'should pop all items from the queue' do
|
57
|
+
expect(queue.pop_all).to eq(%w(a b c))
|
58
|
+
expect(queue.size).to eq(0)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#pop_multi' do
|
63
|
+
it 'should pop multiple items from the queue' do
|
64
|
+
expect(queue.pop_multi(2)).to eq(%w(a b))
|
65
|
+
expect(queue.size).to eq(1)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '#front' do
|
70
|
+
it 'shows the front of the queue' do
|
71
|
+
expect(queue.front).to eq('a')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#back' do
|
76
|
+
it 'shows the back of the queue' do
|
77
|
+
expect(queue.back).to eq('c')
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe '#remove' do
|
82
|
+
it 'removes single items from the queue' do
|
83
|
+
queue.remove('b')
|
84
|
+
expect(queue.size).to eq(2)
|
85
|
+
expect(queue.all).to eq(%w(a c))
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe '#remove_item_by_index' do
|
90
|
+
it 'removes single items by index' do
|
91
|
+
queue.remove_item_by_index(1)
|
92
|
+
expect(queue.size).to eq(2)
|
93
|
+
expect(queue.all).to eq(%w(a c))
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe '#size' do
|
98
|
+
it 'returns the size of the queue' do
|
99
|
+
expect(queue.size).to eq(3)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe '#all' do
|
104
|
+
it 'returns all of the items in the queue' do
|
105
|
+
expect(queue.all).to eq(%w(a b c))
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe '#peek' do
|
110
|
+
it 'allows you to peek at items in the queue without mutating the queue' do
|
111
|
+
expect(queue.peek(0,1)).to eq(%w(a))
|
112
|
+
expect(queue.peek(1,1)).to eq(%w(b))
|
113
|
+
expect(queue.peek(2,1)).to eq(%w(c))
|
114
|
+
expect(queue.peek(3,1)).to eq([])
|
115
|
+
|
116
|
+
expect(queue.peek(0,2)).to eq(%w(a b))
|
117
|
+
expect(queue.peek(0,3)).to eq(%w(a b c))
|
118
|
+
expect(queue.peek(0,4)).to eq(%w(a b c))
|
119
|
+
|
120
|
+
expect(queue.peek(1,2)).to eq(%w(b c))
|
121
|
+
expect(queue.peek(1,3)).to eq(%w(b c))
|
122
|
+
|
123
|
+
expect(queue.size).to eq(3)
|
124
|
+
expect(queue.all).to eq(%w(a b c))
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe '#include?' do
|
129
|
+
it 'indicates if an item is in the queue or not' do
|
130
|
+
expect(queue.include?('a')).to be true
|
131
|
+
expect(queue.include?('A')).to be false
|
132
|
+
|
133
|
+
expect(queue.include?('b')).to be true
|
134
|
+
expect(queue.include?('B')).to be false
|
135
|
+
|
136
|
+
expect(queue.include?('c')).to be true
|
137
|
+
expect(queue.include?('C')).to be false
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe '#clear' do
|
142
|
+
it 'empties the queue' do
|
143
|
+
queue.clear
|
144
|
+
expect(queue.size).to eq(0)
|
145
|
+
expect(queue.all).to eq([])
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis-unique-queue
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: '2.0'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Misha Conway
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-12-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -64,8 +64,10 @@ files:
|
|
64
64
|
- LICENSE.txt
|
65
65
|
- README.md
|
66
66
|
- Rakefile
|
67
|
-
- lib/
|
67
|
+
- lib/redis_unique_queue.rb
|
68
68
|
- redis-unique-queue.gemspec
|
69
|
+
- spec/redis_unique_queue_spec.rb
|
70
|
+
- spec/spec_helper.rb
|
69
71
|
homepage: https://github.com/MishaConway/ruby-redis-unique-queue
|
70
72
|
licenses:
|
71
73
|
- MIT
|
@@ -86,8 +88,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
86
88
|
version: '0'
|
87
89
|
requirements: []
|
88
90
|
rubyforge_project:
|
89
|
-
rubygems_version: 2.6.
|
91
|
+
rubygems_version: 2.6.14
|
90
92
|
signing_key:
|
91
93
|
specification_version: 4
|
92
94
|
summary: A unique queue with atomic operations implemented in Redis
|
93
|
-
test_files:
|
95
|
+
test_files:
|
96
|
+
- spec/redis_unique_queue_spec.rb
|
97
|
+
- spec/spec_helper.rb
|
data/lib/redis/unique/queue.rb
DELETED
@@ -1,155 +0,0 @@
|
|
1
|
-
require "redis"
|
2
|
-
|
3
|
-
class Redis
|
4
|
-
module Unique
|
5
|
-
class Queue
|
6
|
-
attr_reader :name
|
7
|
-
|
8
|
-
VERSION = "1.0.3"
|
9
|
-
|
10
|
-
class InvalidNameException < StandardError; end;
|
11
|
-
class InvalidRedisConfigException < StandardError; end;
|
12
|
-
|
13
|
-
def initialize(name, redis_or_options = {})
|
14
|
-
name = name.to_s if name.kind_of? Symbol
|
15
|
-
|
16
|
-
raise InvalidNameException.new unless name.kind_of?(String) && name.size > 0
|
17
|
-
@name = name
|
18
|
-
@redis = if redis_or_options.kind_of? Redis
|
19
|
-
redis_or_options
|
20
|
-
elsif redis_or_options.kind_of? Hash
|
21
|
-
Redis.new redis_or_options
|
22
|
-
else
|
23
|
-
raise InvalidRedisConfigException.new
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def push data
|
28
|
-
[block_on_atomic_attempt{ attempt_atomic_push_multi(data) }].flatten.first
|
29
|
-
end
|
30
|
-
|
31
|
-
def push_multi *values
|
32
|
-
if values.size > 0
|
33
|
-
block_on_atomic_attempt{ attempt_atomic_push_multi(*values) }
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def pop
|
38
|
-
block_on_atomic_attempt{ attempt_atomic_pop }
|
39
|
-
end
|
40
|
-
|
41
|
-
def pop_all
|
42
|
-
block_on_atomic_attempt{ attempt_atomic_pop_all }
|
43
|
-
end
|
44
|
-
|
45
|
-
def pop_multi amount
|
46
|
-
block_on_atomic_attempt{ attempt_atomic_pop_multi amount }
|
47
|
-
end
|
48
|
-
|
49
|
-
def front
|
50
|
-
@redis.zrange(name, 0, 0).first
|
51
|
-
end
|
52
|
-
|
53
|
-
def back
|
54
|
-
@redis.zrevrange(name, 0, 0).first
|
55
|
-
end
|
56
|
-
|
57
|
-
def remove data
|
58
|
-
@redis.zrem name, data
|
59
|
-
end
|
60
|
-
|
61
|
-
def remove_item_by_index index
|
62
|
-
@redis.zremrangebyrank name, index, index
|
63
|
-
end
|
64
|
-
|
65
|
-
def size
|
66
|
-
@redis.zcard name
|
67
|
-
end
|
68
|
-
|
69
|
-
def all
|
70
|
-
peek 0, size
|
71
|
-
end
|
72
|
-
|
73
|
-
def peek index, amount=1
|
74
|
-
@redis.zrange name, index, index + amount - 1
|
75
|
-
end
|
76
|
-
|
77
|
-
def include? data
|
78
|
-
!@redis.zscore(name, data).nil?
|
79
|
-
end
|
80
|
-
|
81
|
-
def clear
|
82
|
-
@redis.del name
|
83
|
-
[]
|
84
|
-
end
|
85
|
-
|
86
|
-
def expire seconds
|
87
|
-
@redis.expire name, seconds
|
88
|
-
end
|
89
|
-
|
90
|
-
private
|
91
|
-
|
92
|
-
def max_score
|
93
|
-
@redis.zscore name, back
|
94
|
-
end
|
95
|
-
|
96
|
-
def attempt_atomic_push_multi *values
|
97
|
-
success = @redis.watch(name) do
|
98
|
-
score = [Time.now.to_f, max_score].compact.max
|
99
|
-
values = values.first if 1 == values.size && values.first.kind_of?(Array)
|
100
|
-
scored_values = []
|
101
|
-
values.each_with_index do |value, i|
|
102
|
-
scored_values << [score + i, value]
|
103
|
-
end
|
104
|
-
@redis.multi do |multi|
|
105
|
-
multi.zadd name, scored_values
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
[success, values]
|
110
|
-
end
|
111
|
-
|
112
|
-
def attempt_atomic_pop_multi amount
|
113
|
-
attempt_atomic_read_write lambda{ peek 0, amount }, lambda{ |multi, read_result| multi.zremrangebyrank name, 0, amount - 1 }
|
114
|
-
end
|
115
|
-
|
116
|
-
def attempt_atomic_pop_all
|
117
|
-
attempt_atomic_read_write lambda{ all }, lambda{ |multi, read_result| multi.del name}
|
118
|
-
end
|
119
|
-
|
120
|
-
def attempt_atomic_pop
|
121
|
-
read = lambda do
|
122
|
-
@redis.zrangebyscore(name, 0, max_score, :with_scores => false, :limit => [0, 1]).first
|
123
|
-
end
|
124
|
-
|
125
|
-
write = lambda do |multi, read_result|
|
126
|
-
multi.zrem name, read_result
|
127
|
-
end
|
128
|
-
|
129
|
-
attempt_atomic_read_write read, write
|
130
|
-
end
|
131
|
-
|
132
|
-
def block_on_atomic_attempt
|
133
|
-
begin
|
134
|
-
success, result = yield
|
135
|
-
end while !success && result
|
136
|
-
result
|
137
|
-
end
|
138
|
-
|
139
|
-
def attempt_atomic_read_write read_op, write_op
|
140
|
-
|
141
|
-
result = nil
|
142
|
-
success = @redis.watch(name) do
|
143
|
-
result = read_op.call
|
144
|
-
if result
|
145
|
-
@redis.multi do |multi|
|
146
|
-
write_op.call multi, result
|
147
|
-
end
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
[success, result]
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|