redis-deque 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1cdd48df50df7ac6187df87833dea87bc081d62fef36043681b57de55a720440
4
+ data.tar.gz: c91128c60fed13783d74d73c2a59c60974074d23403a2f4af150c4f1e2dbf3ad
5
+ SHA512:
6
+ metadata.gz: b3443c68eed981dd345298f79f001eab472a129dca821a5a48cbd2f678a3f62f66b697c2a14a8fc254a1b79ecb644e0a9525626c112313d5a31a9e2f421cd851
7
+ data.tar.gz: 6117efd7dfd74d2c639813eca093dca92c2d0ae08a9dc67f9c7f8d04a6e75e7db577f9ab12d45436f7caea8d0c4c4612e7d05d14f144497db6b6654035fcff57
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'http://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in redis-queue.gemspec
6
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2013 Francesco Laurita
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,74 @@
1
+ redis-deque
2
+ =============
3
+ Requires the redis gem.
4
+
5
+ Adds Redis::Deque class which can be used as Distributed-Deque based on Redis.
6
+ Redis is often used as a messaging server to implement processing of background jobs or other kinds of messaging tasks.
7
+ It implements Reliable-queue pattern decribed here: http://redis.io/commands/rpoplpush.
8
+
9
+ Installation
10
+ ----------------
11
+ $ gem install redis-deque
12
+
13
+ Testing
14
+ ----------------
15
+ $ bundle install
16
+ $ rake
17
+
18
+ Simple usage
19
+ ----------------
20
+
21
+ ```ruby
22
+ require "redis-deque"
23
+ redis = Redis.new
24
+ queue = Redis::Deque.new('q_test', redis: redis)
25
+
26
+ #Adding some elements
27
+ queue.push "b"
28
+ queue << "a" # << is an alias of push
29
+
30
+ # Process messages
31
+
32
+ # By default, calling pop method is a blocking operation
33
+ # Your code will wait here for a new
34
+ while message=queue.pop
35
+ #Remove message from the backup queue if the message has been processed without errors
36
+ queue.commit if YourTask.new(message).perform.succeed?
37
+ end
38
+
39
+ #Process messages using block
40
+
41
+ queue.process do |message|
42
+ #queue.commit is called if last statement of the block returns true
43
+ YourTask.new(message).perform.succeed?
44
+ end
45
+
46
+ # Process messages with timeout (starting from version 0.0.3)
47
+ # Wait for 15 seconds for new messages, then exit
48
+ queue.process(false, 15) do |message|
49
+ puts "'#{message}'"
50
+ end
51
+
52
+ # Process messages in a non blocking-way
53
+ # A soon as the queue is empty, the block will exit
54
+ queue.process(true) do |message|
55
+ puts "'#{message}'"
56
+ end
57
+ ```
58
+ Contributing to redis-deque
59
+ ----------------
60
+
61
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
62
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
63
+ * Fork the project.
64
+ * Start a feature/bugfix branch.
65
+ * Commit and push until you are happy with your contribution.
66
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
67
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
68
+
69
+ Copyright
70
+ ----------------
71
+
72
+ Copyright (c) 2013 Francesco Laurita. See LICENSE.txt for
73
+ further details.
74
+
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec) do |spec|
7
+ spec.pattern = 'spec/*_spec.rb'
8
+ end
9
+
10
+ task default: :spec
11
+ task test: :spec
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'redis-deque'
4
+ redis = Redis.new
5
+ # Create a queue that will listen for a new element for 10 seconds
6
+ queue = Redis::Deque.new('__test', redis: redis, timeout: 10)
7
+ queue.clear true
8
+
9
+ 100.times { queue << rand(100) }
10
+
11
+ # Simulate a delayed insert
12
+ t = Thread.new do
13
+ sleep 3
14
+ # We should use a second connection here since the first one is busy
15
+ # on a blocking call
16
+ other_redis = Redis.new
17
+ other_queue = Redis::Deque.new('__test', redis: other_redis)
18
+ 100.times { other_queue << "e_#{rand(100)}" }
19
+ end
20
+
21
+ # When all elements are de-queued, process method will wait for 10 secods before exit
22
+ queue.process do |message|
23
+ puts "'#{message}'"
24
+ end
25
+ t.join
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'redis-deque'
4
+
5
+ redis = Redis.new
6
+
7
+ queue = Redis::Deque.new('__test', redis: redis)
8
+ queue.clear true
9
+
10
+ 100.times { queue << rand(100) }
11
+
12
+ queue.process(true) { |m| puts m }
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'redis'
4
+ require 'redis/deque'
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ class Deque
5
+ VERSION = '0.1.0'
6
+
7
+ def self.version
8
+ "redis-deque version #{VERSION}"
9
+ end
10
+
11
+ def initialize(queue_name, options = {})
12
+ raise ArgumentError, 'queue_name must be a non-empty string' if !queue_name.is_a?(String) || queue_name.empty?
13
+ raise ArgumentError, 'process_queue_name must be a non-empty string' if options.key?(:process_queue_name) && (!options[:process_queue_name].is_a?(String) || options[:process_queue_name].empty?)
14
+ raise ArgumentError, 'queue_name and process_queue_name must be different' if options[:process_queue_name] == queue_name
15
+
16
+ @redis = options[:redis] || Redis.current
17
+ @queue_name = queue_name
18
+ @process_queue_name = options[:process_queue_name] || "#{queue_name}_process"
19
+ @last_message = nil
20
+ @timeout = options[:timeout] ||= 0
21
+ end
22
+
23
+ def length
24
+ @redis.llen @queue_name
25
+ end
26
+
27
+ def clear(clear_process_queue = false)
28
+ @redis.del @queue_name
29
+ @redis.del @process_queue_name if clear_process_queue
30
+ end
31
+
32
+ def empty?
33
+ length <= 0
34
+ end
35
+
36
+ def push(obj)
37
+ @redis.lpush(@queue_name, obj)
38
+ end
39
+
40
+ def unshift(obj)
41
+ @redis.rpush(@queue_name, obj)
42
+ end
43
+
44
+ def pop(non_block = false)
45
+ @last_message = if non_block
46
+ @redis.rpoplpush(@queue_name, @process_queue_name)
47
+ else
48
+ @redis.brpoplpush(@queue_name, @process_queue_name, @timeout)
49
+ end
50
+ @last_message
51
+ end
52
+
53
+ def commit
54
+ @redis.lrem(@process_queue_name, 0, @last_message)
55
+ end
56
+
57
+ def process(non_block = false, timeout = nil)
58
+ @timeout = timeout unless timeout.nil?
59
+ loop do
60
+ message = pop(non_block)
61
+ ret = yield message if block_given?
62
+ commit if ret
63
+ break if message.nil? || (non_block && empty?)
64
+ end
65
+ end
66
+
67
+ def refill
68
+ while (message = @redis.lpop(@process_queue_name))
69
+ unshift(message)
70
+ end
71
+ true
72
+ end
73
+
74
+ alias size length
75
+ alias dec pop
76
+ alias shift pop
77
+ alias enc push
78
+ alias << push
79
+ end
80
+ end
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ require File.expand_path('lib/redis/deque', __dir__)
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = 'redis-deque'
8
+ s.version = Redis::Deque::VERSION
9
+ s.authors = ['Jaakko Rinta-Filppula', 'Francesco Laurita']
10
+ s.email = ['jaakko@r-f.fi', 'francesco.laurita@gmail.com']
11
+ s.homepage = 'https://github.com/tophattom/redis-deque'
12
+ s.summary = 'A distributed deque based on Redis'
13
+ s.description = '
14
+ Adds Redis::Deque class which can be used as Distributed-Deque based on Redis.
15
+ Redis is often used as a messaging server to implement processing of background jobs or other kinds of messaging tasks.
16
+ It implements Reliable-queue pattern decribed here: http://redis.io/commands/rpoplpush
17
+ '
18
+
19
+ s.licenses = ['MIT']
20
+
21
+ s.rubyforge_project = 'redis-deque'
22
+
23
+ s.files = `git ls-files`.split("\n")
24
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
25
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
26
+ s.require_paths = ['lib']
27
+
28
+ s.add_runtime_dependency 'redis', '>= 3.3.5', '< 5'
29
+
30
+ s.add_development_dependency 'rspec', '~> 2.13', '>= 2.13.0'
31
+ end
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'timeout'
5
+
6
+ describe Redis::Deque do
7
+ before(:all) do
8
+ @redis = Redis.new
9
+ @queue = Redis::Deque.new('__test', process_queue_name: 'bp__test')
10
+ @queue.clear true
11
+ end
12
+
13
+ before(:each) do
14
+ @queue.clear true
15
+ end
16
+
17
+ after(:all) do
18
+ @queue.clear true
19
+ end
20
+
21
+ after(:each) do
22
+ @queue.clear true
23
+ end
24
+
25
+ it 'should return correct version string' do
26
+ Redis::Deque.version.should == "redis-deque version #{Redis::Deque::VERSION}"
27
+ end
28
+
29
+ it 'should create a new redis-queue object' do
30
+ queue = Redis::Deque.new('__test', process_queue_name: 'bp__test')
31
+ queue.class.should == Redis::Deque
32
+ end
33
+
34
+ it 'should create default process_queue_name if one is not given' do
35
+ queue = Redis::Deque.new '__test_queue'
36
+ queue.instance_variable_get(:@process_queue_name).should be == '__test_queue_process'
37
+ end
38
+
39
+ it 'should not allow same name for queue_name and process_queue_name' do
40
+ expect {
41
+ Redis::Deque.new '__test_queue', process_queue_name: '__test_queue'
42
+ }.to raise_error(ArgumentError)
43
+ end
44
+
45
+ it 'should add an element to the queue' do
46
+ @queue << 'a'
47
+ @queue.size.should be == 1
48
+ end
49
+
50
+ it 'should add an element to the front of the queue' do
51
+ @queue.unshift 'a'
52
+ @queue.unshift 'b'
53
+ @queue.size.should be == 2
54
+
55
+ message = @queue.pop(true)
56
+ message.should be == 'b'
57
+ end
58
+
59
+ it 'should return an element from the queue' do
60
+ @queue << 'a'
61
+ message = @queue.pop(true)
62
+ message.should be == 'a'
63
+ end
64
+
65
+ it 'should remove the element from bp_queue if commit is called' do
66
+ @queue << 'a'
67
+ @queue.pop true
68
+
69
+ @redis.llen('bp__test').should be == 1
70
+ @queue.commit
71
+ @redis.llen('bp__test').should be == 0
72
+ end
73
+
74
+ it 'should implements fifo pattern' do
75
+ @queue.clear
76
+ payload = %w[a b c d e]
77
+ payload.each { |e| @queue << e }
78
+ test = []
79
+ while (e = @queue.pop(true))
80
+ test << e
81
+ end
82
+ payload.should be == test
83
+ end
84
+
85
+ it 'should implement lifo pattern with unshift' do
86
+ payload = %w(a b c d e)
87
+ payload.each { |e| @queue.unshift e }
88
+ test = []
89
+ while (e = @queue.pop(true))
90
+ test << e
91
+ end
92
+ test.should be == payload.reverse
93
+ end
94
+
95
+ it 'should remove all of the elements from the main queue' do
96
+ %w[a b c d e].each { |e| @queue << e }
97
+ @queue.size.should be > 0
98
+ @queue.pop(true)
99
+ @queue.clear
100
+ @redis.llen('bp__test').should be > 0
101
+ end
102
+
103
+ it 'should reset queues content' do
104
+ @queue.clear(true)
105
+ @redis.llen('bp__test').should be == 0
106
+ end
107
+
108
+ it 'should prcess a message' do
109
+ @queue << 'a'
110
+ @queue.process(true) { |m| m.should be == 'a'; true }
111
+ end
112
+
113
+ it 'should prcess a message leaving it into the bp_queue' do
114
+ @queue << 'a'
115
+ @queue << 'a'
116
+ @queue.process(true) { |m| m.should be == 'a'; false }
117
+ @redis.lrange('bp__test', 0, -1).should be == %w[a a]
118
+ end
119
+
120
+ it 'should refill a main queue' do
121
+ @queue.clear(true)
122
+ @queue << 'a'
123
+ @queue << 'a'
124
+ @queue.process(true) { |m| m.should be == 'a'; false }
125
+ @redis.lrange('bp__test', 0, -1).should be == %w[a a]
126
+ @queue.refill
127
+ @redis.lrange('__test', 0, -1).should be == %w[a a]
128
+ @redis.llen('bp__test').should be == 0
129
+ end
130
+
131
+ it 'should work with the timeout parameters' do
132
+ @queue.clear(true)
133
+ 2.times { @queue << rand(100) }
134
+ is_ok = true
135
+ begin
136
+ Timeout.timeout(3) do
137
+ @queue.process(false, 2) { |_m| true }
138
+ end
139
+ rescue Timeout::Error => _e
140
+ is_ok = false
141
+ end
142
+
143
+ is_ok.should be_truthy
144
+ end
145
+
146
+ it 'should honor the timeout param in the initializer' do
147
+ redis = Redis.new
148
+ queue = Redis::Deque.new('__test_tm', process_queue_name: 'bp__test_tm', redis: redis, timeout: 2)
149
+ queue.clear true
150
+
151
+ is_ok = true
152
+ begin
153
+ Timeout.timeout(4) do
154
+ queue.pop
155
+ end
156
+ rescue Timeout::Error => _e
157
+ is_ok = false
158
+ end
159
+ queue.clear
160
+ is_ok.should be_truthy
161
+ end
162
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ require 'rspec'
6
+
7
+ RSpec.configure do |config|
8
+ config.color = true
9
+ end
10
+
11
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
12
+ require 'redis-deque'
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redis-deque
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jaakko Rinta-Filppula
8
+ - Francesco Laurita
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2020-01-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: redis
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: 3.3.5
21
+ - - "<"
22
+ - !ruby/object:Gem::Version
23
+ version: '5'
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ version: 3.3.5
31
+ - - "<"
32
+ - !ruby/object:Gem::Version
33
+ version: '5'
34
+ - !ruby/object:Gem::Dependency
35
+ name: rspec
36
+ requirement: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 2.13.0
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '2.13'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: 2.13.0
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '2.13'
54
+ description: "\n Adds Redis::Deque class which can be used as Distributed-Deque
55
+ based on Redis.\n Redis is often used as a messaging server to implement processing
56
+ of background jobs or other kinds of messaging tasks.\n It implements Reliable-queue
57
+ pattern decribed here: http://redis.io/commands/rpoplpush\n "
58
+ email:
59
+ - jaakko@r-f.fi
60
+ - francesco.laurita@gmail.com
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - ".gitignore"
66
+ - Gemfile
67
+ - LICENSE.txt
68
+ - README.md
69
+ - Rakefile
70
+ - example/blocking.rb
71
+ - example/non-blocking.rb
72
+ - lib/redis-deque.rb
73
+ - lib/redis/deque.rb
74
+ - redis-deque.gemspec
75
+ - spec/redis_deque_spec.rb
76
+ - spec/spec_helper.rb
77
+ homepage: https://github.com/tophattom/redis-deque
78
+ licenses:
79
+ - MIT
80
+ metadata: {}
81
+ post_install_message:
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ requirements: []
96
+ rubygems_version: 3.0.3
97
+ signing_key:
98
+ specification_version: 4
99
+ summary: A distributed deque based on Redis
100
+ test_files:
101
+ - spec/redis_deque_spec.rb
102
+ - spec/spec_helper.rb