eq 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/README.md +51 -11
- data/TODO.md +10 -0
- data/benchmarks/all.rb +13 -0
- data/benchmarks/parallel.rb +23 -0
- data/benchmarks/queue_backend_benchmark.rb +27 -0
- data/benchmarks/queueing.rb +13 -23
- data/benchmarks/working.rb +14 -25
- data/eq.gemspec +12 -2
- data/examples/queueing.rb +2 -2
- data/examples/scheduling.rb +19 -0
- data/examples/simple_usage.rb +20 -8
- data/examples/working.rb +2 -2
- data/lib/eq-queueing.rb +4 -13
- data/lib/eq-queueing/backends.rb +30 -1
- data/lib/eq-queueing/backends/leveldb.rb +232 -0
- data/lib/eq-queueing/backends/sequel.rb +34 -17
- data/lib/eq-queueing/queue.rb +26 -20
- data/lib/eq-scheduling.rb +33 -0
- data/lib/eq-scheduling/scheduler.rb +19 -0
- data/lib/eq-web.rb +5 -0
- data/lib/eq-web/server.rb +39 -0
- data/lib/eq-web/views/index.erb +45 -0
- data/lib/eq-working.rb +15 -7
- data/lib/eq-working/worker.rb +30 -3
- data/lib/eq.rb +39 -31
- data/lib/eq/boot/all.rb +1 -0
- data/lib/eq/boot/scheduling.rb +1 -0
- data/lib/eq/error.rb +4 -0
- data/lib/eq/job.rb +22 -16
- data/lib/eq/version.rb +1 -1
- data/log/.gitkeep +1 -0
- data/spec/lib/eq-queueing/backends/leveldb_spec.rb +32 -0
- data/spec/lib/eq-queueing/backends/sequel_spec.rb +5 -4
- data/spec/lib/eq-queueing/queue_spec.rb +27 -58
- data/spec/lib/eq-queueing_spec.rb +16 -0
- data/spec/lib/eq-scheduling_spec.rb +7 -0
- data/spec/lib/eq-working/worker_spec.rb +13 -0
- data/spec/lib/eq/job_spec.rb +16 -11
- data/spec/lib/eq_spec.rb +1 -1
- data/spec/mocks/a_job.rb +4 -0
- data/spec/mocks/a_unique_job.rb +6 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/support/shared_examples_for_queue.rb +60 -31
- metadata +80 -8
- data/lib/eq-working/manager.rb +0 -31
- data/lib/eq-working/system.rb +0 -10
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# EQ - Embedded Queueing
|
4
4
|
|
5
|
-
EQ is a little framework to queue tasks within a process.
|
5
|
+
EQ is a little framework to queue and perform background tasks within a single-process ruby application. It uses the Celluloid actor framework to do the work in the background. Its queue backends persist your jobs. So your jobs will survive application stop/restart.
|
6
6
|
|
7
7
|
[![Travis-CI Build Status](https://secure.travis-ci.org/dpree/eq.png)](https://secure.travis-ci.org/dpree/eq)
|
8
8
|
[![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/dpree/eq)
|
@@ -31,27 +31,67 @@ If you want to execute a simple example you can just run [examples/simple_usage.
|
|
31
31
|
|
32
32
|
EQ.boot
|
33
33
|
|
34
|
-
**3.
|
34
|
+
**3. Enqueue some jobs in the EQ queue.**
|
35
35
|
|
36
|
-
EQ.
|
37
|
-
EQ.
|
36
|
+
EQ.push MyJob, 'foo'
|
37
|
+
EQ.push MyJob, 'bar'
|
38
38
|
…
|
39
39
|
|
40
|
+
**5. Let EQ do your work.**
|
41
|
+
|
42
|
+
# EQ will spawn and maintain worker threads that execute the following for you:
|
43
|
+
MyJob.perform 'foo'
|
44
|
+
MyJob.perform 'bar'
|
45
|
+
|
46
|
+
**6. Shutdown EQ gracefully when you're application is done.***
|
47
|
+
|
48
|
+
EQ.shutdown
|
49
|
+
|
50
|
+
### Optional: Schedule jobs using Clockwork
|
51
|
+
|
52
|
+
module Clockwork
|
53
|
+
every(5.seconds, MyJob)
|
54
|
+
every(1.day, MyJob, :at => '00:00')
|
55
|
+
end
|
56
|
+
|
40
57
|
## Configuration
|
41
58
|
|
42
|
-
Right now there
|
59
|
+
Right now there are two queueing backends available, one that is based on the Sequel gem and one based on LevelDB. With Sequel basically any SQL database might be used. Just make sure that you install the Backend before running the application.
|
60
|
+
|
61
|
+
### Sequel
|
43
62
|
|
44
|
-
|
63
|
+
Gemfile
|
64
|
+
|
65
|
+
gem 'sequel'
|
66
|
+
gem 'sqlite3'
|
67
|
+
|
68
|
+
Configuration
|
69
|
+
|
70
|
+
EQ.config.queue = 'sequel'
|
45
71
|
|
46
|
-
# SQLite3 in-memory (default) using String syntax
|
47
|
-
|
72
|
+
# With SQLite3 in-memory (default) using String syntax
|
73
|
+
# Caution: This won't persist your jobs!
|
74
|
+
EQ.config.sequel = 'sqlite:/'
|
48
75
|
|
49
|
-
# SQLite3 file using Hash syntax
|
76
|
+
# With SQLite3 file using Hash syntax
|
50
77
|
EQ.config.sequel = {adapter: 'sqlite', database: 'my_db.sqlite3'}
|
51
78
|
|
52
|
-
# Postgres
|
79
|
+
# With Postgres
|
53
80
|
EQ.config.sequel = 'postgres://user:password@host:port/my_db'
|
54
81
|
|
82
|
+
# Mysql, Oracle, etc.
|
83
|
+
# ...
|
84
|
+
|
85
|
+
# LevelDB
|
86
|
+
|
87
|
+
Gemfile
|
88
|
+
|
89
|
+
gem 'leveldb-ruby'
|
90
|
+
|
91
|
+
Configuration
|
92
|
+
|
93
|
+
EQ.config.queue = 'leveldb'
|
94
|
+
EQ.config.leveldb = 'path/to/my/queue.leveldb'
|
55
95
|
|
56
96
|
### Logging
|
57
97
|
|
@@ -62,7 +102,7 @@ EQ uses the logging mechanism of the underlying Celluloid (`Celluloid.logger`) f
|
|
62
102
|
# Use the logger of your Rails application.
|
63
103
|
Celluloid.logger = Rails.logger
|
64
104
|
|
65
|
-
# No
|
105
|
+
# No logging at all.
|
66
106
|
Celluloid.logger = Logger.new('/dev/null')
|
67
107
|
|
68
108
|
## Contributing
|
data/TODO.md
ADDED
data/benchmarks/all.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
puts ""
|
4
|
+
puts "QUEUEING"
|
5
|
+
system "#{File.join(File.dirname(__FILE__), 'queueing.rb')}"
|
6
|
+
|
7
|
+
puts ""
|
8
|
+
puts "WORKING"
|
9
|
+
system "#{File.join(File.dirname(__FILE__), 'working.rb')}"
|
10
|
+
|
11
|
+
puts ""
|
12
|
+
puts "BOTH"
|
13
|
+
system "#{File.join(File.dirname(__FILE__), 'parallel.rb')}"
|
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require File.join(File.dirname(__FILE__), 'queue_backend_benchmark')
|
4
|
+
|
5
|
+
class MyJob
|
6
|
+
def self.perform stuff
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class Executor < Struct.new(:n, :benchmark)
|
11
|
+
def report name, &configure
|
12
|
+
EQ.config &configure
|
13
|
+
EQ.boot
|
14
|
+
benchmark.report name do
|
15
|
+
n.times { |i| EQ.push MyJob, i }
|
16
|
+
sleep 0.01 until EQ.count(:waiting) == 0
|
17
|
+
end
|
18
|
+
EQ.shutdown
|
19
|
+
sleep 0.05
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
QueueBackendBenchmark.new(Executor.new(100)).run
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require 'tmpdir'
|
3
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'eq', 'boot', 'all')
|
4
|
+
|
5
|
+
EQ.logger.level = Logger::Severity::ERROR
|
6
|
+
|
7
|
+
class QueueBackendBenchmark < Struct.new(:executor)
|
8
|
+
def run
|
9
|
+
Benchmark.bm(50) do |benchmark|
|
10
|
+
executor.benchmark = benchmark
|
11
|
+
|
12
|
+
executor.report 'sequel with sqlite3 (in-memory)' do |config|
|
13
|
+
config.queue = 'sequel'
|
14
|
+
end
|
15
|
+
|
16
|
+
executor.report 'sequel with sqlite3 (file)' do |config|
|
17
|
+
config.queue = 'sequel'
|
18
|
+
config.sequel = "sqlite://#{Dir.mktmpdir}/benchmark.sqlite3"
|
19
|
+
end
|
20
|
+
|
21
|
+
executor.report 'leveldb' do |config|
|
22
|
+
config.queue = 'leveldb'
|
23
|
+
config.leveldb = Dir.mktmpdir
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/benchmarks/queueing.rb
CHANGED
@@ -1,33 +1,23 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require File.join(File.dirname(__FILE__), '
|
4
|
-
|
5
|
-
require 'benchmark'
|
6
|
-
require 'tempfile'
|
7
|
-
|
8
|
-
EQ.logger.level = Logger::Severity::ERROR
|
3
|
+
require File.join(File.dirname(__FILE__), 'queue_backend_benchmark')
|
9
4
|
|
10
5
|
class MyJob
|
11
6
|
def self.perform stuff
|
12
7
|
end
|
13
8
|
end
|
14
9
|
|
15
|
-
n
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
config[:sqlite] = Tempfile.new('').path
|
26
|
-
end
|
27
|
-
EQ.boot
|
28
|
-
b.report('file-based sqlite') do
|
29
|
-
n.times { |i| EQ.queue.push! MyJob, i }
|
30
|
-
EQ.queue.waiting_count # block
|
10
|
+
class Executor < Struct.new(:n, :benchmark)
|
11
|
+
def report name, &configure
|
12
|
+
EQ.config &configure
|
13
|
+
EQ.boot :queue
|
14
|
+
benchmark.report name do
|
15
|
+
n.times { |i| EQ.push! MyJob, i }
|
16
|
+
EQ.count
|
17
|
+
end
|
18
|
+
EQ.shutdown
|
19
|
+
sleep 0.05
|
31
20
|
end
|
32
|
-
EQ.shutdown
|
33
21
|
end
|
22
|
+
|
23
|
+
QueueBackendBenchmark.new(Executor.new(1000)).run
|
data/benchmarks/working.rb
CHANGED
@@ -1,35 +1,24 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require File.join(File.dirname(__FILE__), '
|
4
|
-
|
5
|
-
require 'benchmark'
|
6
|
-
require 'tempfile'
|
7
|
-
|
8
|
-
EQ.logger.level = Logger::Severity::ERROR
|
3
|
+
require File.join(File.dirname(__FILE__), 'queue_backend_benchmark')
|
9
4
|
|
10
5
|
class MyJob
|
11
6
|
def self.perform stuff
|
12
7
|
end
|
13
8
|
end
|
14
9
|
|
15
|
-
n
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
config[:sqlite] = Tempfile.new('').path
|
27
|
-
end
|
28
|
-
EQ.boot_queueing
|
29
|
-
n.times { |i| EQ.queue.push! MyJob, i }
|
30
|
-
b.report('file-based sqlite') do
|
31
|
-
EQ.boot_working
|
32
|
-
sleep 0.01 until EQ.queue.waiting_count == 0
|
10
|
+
class Executor < Struct.new(:n, :benchmark)
|
11
|
+
def report name, &configure
|
12
|
+
EQ.config &configure
|
13
|
+
EQ.boot :queue
|
14
|
+
n.times { |i| EQ.push MyJob, i }
|
15
|
+
benchmark.report name do
|
16
|
+
EQ.boot :worker
|
17
|
+
sleep 0.01 until EQ.count(:waiting) == 0
|
18
|
+
end
|
19
|
+
EQ.shutdown
|
20
|
+
sleep 0.05
|
33
21
|
end
|
34
|
-
EQ.shutdown
|
35
22
|
end
|
23
|
+
|
24
|
+
QueueBackendBenchmark.new(Executor.new(1000)).run
|
data/eq.gemspec
CHANGED
@@ -15,9 +15,19 @@ Gem::Specification.new do |gem|
|
|
15
15
|
gem.require_paths = ["lib"]
|
16
16
|
gem.version = EQ::VERSION
|
17
17
|
|
18
|
-
gem.add_dependency "sqlite3"
|
19
|
-
gem.add_dependency "sequel"
|
20
18
|
gem.add_dependency "celluloid"
|
19
|
+
|
20
|
+
# just to test the web view
|
21
|
+
gem.add_development_dependency "sinatra"
|
22
|
+
|
23
|
+
# just to test the scheduling
|
24
|
+
gem.add_development_dependency "clockwork"
|
25
|
+
|
26
|
+
# just to test the queueing backends
|
27
|
+
gem.add_development_dependency "sequel" # sequel backend
|
28
|
+
gem.add_development_dependency "sqlite3" # sequel with sqlite
|
29
|
+
gem.add_development_dependency "leveldb-ruby" # leveldb backend
|
30
|
+
|
21
31
|
gem.add_development_dependency "guard"
|
22
32
|
gem.add_development_dependency "guard-rspec"
|
23
33
|
gem.add_development_dependency "rspec"
|
data/examples/queueing.rb
CHANGED
@@ -3,7 +3,7 @@ require File.join(File.dirname(__FILE__), '..', 'lib', 'eq', 'boot', 'queueing')
|
|
3
3
|
|
4
4
|
EQ.logger.level = Logger::Severity::INFO
|
5
5
|
EQ.config do |config|
|
6
|
-
config
|
6
|
+
config.sequel = "sqlite://foo.sqlite"
|
7
7
|
end
|
8
8
|
|
9
9
|
def say words; EQ.logger.info(words); end
|
@@ -16,7 +16,7 @@ end
|
|
16
16
|
|
17
17
|
require 'timeout'
|
18
18
|
begin
|
19
|
-
Timeout.timeout(
|
19
|
+
Timeout.timeout(10) do
|
20
20
|
EQ.boot
|
21
21
|
loop do
|
22
22
|
say "pushed!"
|
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'eq', 'boot', 'all')
|
3
|
+
|
4
|
+
class MyJob
|
5
|
+
def self.perform
|
6
|
+
puts 'perfoming...'
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module Clockwork
|
11
|
+
every(1.seconds, MyJob)
|
12
|
+
every(1.day, MyJob, :at => '00:00')
|
13
|
+
end
|
14
|
+
|
15
|
+
EQ.boot
|
16
|
+
|
17
|
+
sleep 3
|
18
|
+
|
19
|
+
EQ.shutdown
|
data/examples/simple_usage.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
require File.join(File.dirname(__FILE__), '..', 'lib', 'eq', 'boot', 'all')
|
3
3
|
|
4
|
+
Celluloid.logger = Logger.new('/dev/null')
|
5
|
+
|
4
6
|
# Define a Job class with a perform method.
|
5
7
|
class MyJob
|
6
8
|
RESULT_PATH = 'my_job_result.txt'
|
@@ -21,15 +23,25 @@ end
|
|
21
23
|
# Cleanup results file.
|
22
24
|
File.delete MyJob::RESULT_PATH if File.exists? MyJob::RESULT_PATH
|
23
25
|
|
24
|
-
|
25
|
-
|
26
|
+
EQ.config do |c|
|
27
|
+
c.sequel = 'sqlite://examples/foo'
|
28
|
+
end
|
29
|
+
|
30
|
+
require 'timeout'
|
31
|
+
begin
|
32
|
+
Timeout.timeout(12) do
|
26
33
|
|
27
|
-
#
|
28
|
-
EQ.
|
29
|
-
EQ.queue.push MyJob, Time.now, 2
|
34
|
+
# Start the EQ system.
|
35
|
+
EQ.boot
|
30
36
|
|
31
|
-
#
|
32
|
-
|
37
|
+
# Enqueue some work.
|
38
|
+
EQ.queue.push! MyJob, Time.now, 0.1
|
39
|
+
EQ.queue.push! MyJob, Time.now, 0.5
|
40
|
+
|
41
|
+
# Wait some time to get the work done.
|
42
|
+
sleep 1
|
43
|
+
end
|
44
|
+
end
|
33
45
|
|
34
46
|
# Read the results file.
|
35
|
-
puts File.read
|
47
|
+
puts File.read(MyJob::RESULT_PATH)
|
data/examples/working.rb
CHANGED
@@ -3,7 +3,7 @@ require File.join(File.dirname(__FILE__), '..', 'lib', 'eq', 'boot', 'all')
|
|
3
3
|
|
4
4
|
EQ.logger.level = Logger::Severity::INFO
|
5
5
|
EQ.config do |config|
|
6
|
-
config
|
6
|
+
config.sequel = "sqlite://foo.sqlite"
|
7
7
|
end
|
8
8
|
|
9
9
|
def say words; EQ.logger.info(words); end
|
@@ -17,7 +17,7 @@ end
|
|
17
17
|
|
18
18
|
require 'timeout'
|
19
19
|
begin
|
20
|
-
Timeout.timeout(
|
20
|
+
Timeout.timeout(10) do
|
21
21
|
EQ.boot
|
22
22
|
sleep 0.5 while EQ.working?
|
23
23
|
end
|
data/lib/eq-queueing.rb
CHANGED
@@ -5,8 +5,10 @@ require File.join(File.dirname(__FILE__), 'eq-queueing', 'queue')
|
|
5
5
|
module EQ::Queueing
|
6
6
|
module_function
|
7
7
|
|
8
|
+
EQ_QUEUE = :_eq_queueing
|
9
|
+
|
8
10
|
def boot
|
9
|
-
EQ::Queueing::Queue.supervise_as
|
11
|
+
EQ::Queueing::Queue.supervise_as EQ_QUEUE, EQ::Queueing::Backends.init(EQ.config)
|
10
12
|
end
|
11
13
|
|
12
14
|
def shutdown
|
@@ -14,17 +16,6 @@ module EQ::Queueing
|
|
14
16
|
end
|
15
17
|
|
16
18
|
def queue
|
17
|
-
Celluloid::Actor[
|
18
|
-
end
|
19
|
-
|
20
|
-
# @raise ConfigurationError when EQ.config.queue is not supported
|
21
|
-
def initialize_queueing_backend
|
22
|
-
queue_config = EQ.config.send(EQ.config.queue)
|
23
|
-
case EQ.config.queue
|
24
|
-
when 'sequel'
|
25
|
-
EQ::Queueing::Backends::Sequel.new queue_config
|
26
|
-
else
|
27
|
-
raise EQ::ConfigurationError, "EQ.config.queue = '#{EQ.config.queue}' is not supported!"
|
28
|
-
end
|
19
|
+
Celluloid::Actor[EQ_QUEUE]
|
29
20
|
end
|
30
21
|
end
|
data/lib/eq-queueing/backends.rb
CHANGED
@@ -1,6 +1,35 @@
|
|
1
1
|
module EQ::Queueing
|
2
2
|
module Backends
|
3
|
+
class BackendLoadError < LoadError; end
|
4
|
+
|
5
|
+
module_function
|
6
|
+
|
7
|
+
# @params [#queue, #"#{queue}"] config
|
8
|
+
# @raise ConfigurationError when config.queue is not supported
|
9
|
+
def init config
|
10
|
+
if %w[ sequel leveldb ].include? config.queue
|
11
|
+
initialize_queue config
|
12
|
+
else
|
13
|
+
raise EQ::ConfigurationError, "config.queue = '#{config.queue}' is not supported!"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# @raise LoadError when required gem is not available
|
18
|
+
def initialize_queue config
|
19
|
+
queue_config = config.send(config.queue)
|
20
|
+
case EQ.config.queue
|
21
|
+
when 'sequel'
|
22
|
+
require_queue 'sequel'
|
23
|
+
EQ::Queueing::Backends::Sequel.new queue_config
|
24
|
+
when 'leveldb'
|
25
|
+
require_queue 'leveldb'
|
26
|
+
EQ::Queueing::Backends::LevelDB.new queue_config
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def require_queue queue_name
|
31
|
+
require File.join(File.dirname(__FILE__), 'backends', queue_name)
|
32
|
+
end
|
3
33
|
end
|
4
34
|
end
|
5
35
|
|
6
|
-
require File.join(File.dirname(__FILE__), 'backends', 'sequel')
|