localjob 0.0.1 → 0.0.2
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 +89 -9
- data/ext/mkrf_conf.rb +17 -0
- data/lib/localjob/channel.rb +6 -4
- data/lib/localjob/linux_adapter.rb +34 -0
- data/lib/localjob/mock_adapter.rb +32 -0
- data/lib/localjob/version.rb +1 -1
- data/lib/localjob.rb +26 -24
- data/localjob.gemspec +1 -1
- data/test/localjob_test.rb +8 -6
- data/test/mock_adapter.rb +22 -0
- data/test/test_helper.rb +6 -0
- data/test/worker_test.rb +26 -26
- metadata +8 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6c33dc84596d653899831a3697428d950028e0c5
|
4
|
+
data.tar.gz: 8547e6ca4022b618956578e8dc3f11fe65ae8b00
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 22eab445b06c1011c53a244a2677feec403a4f501e0dc35ca663b405d4d754f3f891914420fb811a5a840076f1fbd6af71f7afc31d5f1e9c76a297730fbf281c
|
7
|
+
data.tar.gz: d7ede95238c734c9ab043b2861152203197fb7606a86ee6b81083ce304c9f479cbaf50842515301518f6566733b7978a1b77dff828827df476e9dd5505191199
|
data/README.md
CHANGED
@@ -12,18 +12,18 @@ project is to be able to migrate to another background queue system by switching
|
|
12
12
|
adapter: `Localjob.adapter = Resque` to switch to Resque, without changes to
|
13
13
|
your own code.
|
14
14
|
|
15
|
-
The POSIX message queue is persistent till reboot. You will need to tune system
|
16
|
-
parameters for your application
|
15
|
+
The POSIX message queue is persistent till reboot. You **will need to tune system
|
16
|
+
parameters for your application**, please consult [posix-mqueue][pmq-gem]'s
|
17
17
|
documentation.
|
18
18
|
|
19
|
-
Localjob works on Ruby >= 2.0.0
|
19
|
+
Localjob works on Ruby >= 2.0.0. On Linux, it will use the POSIX message queue.
|
20
|
+
On OS X (and Windows, not tested) it will use a mock class instead of the
|
21
|
+
message queue, to aid you in testing and running Localjob in development.
|
20
22
|
|
21
|
-
|
22
|
-
doesn't work! It's not released as a gem yet, so add the following to your
|
23
|
-
Gemfile to use it:
|
23
|
+
Add it to your Gemfile:
|
24
24
|
|
25
25
|
```ruby
|
26
|
-
gem 'localjob'
|
26
|
+
gem 'localjob'
|
27
27
|
```
|
28
28
|
|
29
29
|
## Usage
|
@@ -49,8 +49,88 @@ queue = Localjob.new
|
|
49
49
|
queue << EmailJob.new(current_user.id, welcome_email)
|
50
50
|
```
|
51
51
|
|
52
|
-
|
53
|
-
|
52
|
+
A job is serialized with YAML and pushed onto a persistent POSIX message queue.
|
53
|
+
This means a worker does not have to listen on the queue to push things to it.
|
54
|
+
Workers will pop off the message queue, but only one will receive the job.
|
55
|
+
Deserialize the message to create an instance of your object, and call
|
56
|
+
`#perform` on the object.
|
57
|
+
|
58
|
+
### Rails initializer
|
59
|
+
|
60
|
+
For easy access to your queues in Rails, you can add an initializer to set up a
|
61
|
+
constant referencing each of your queues. This allows easy access anywhere in
|
62
|
+
your app. In `config/initializers/localjob.rb`:
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
BackgroundQueue = Localjob.new("main-queue")
|
66
|
+
```
|
67
|
+
|
68
|
+
Then in your app you can simply reference the constant to push to the queue:
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
BackgroundQueue << EmailJob.new(current_user.id, welcome_email)
|
72
|
+
```
|
73
|
+
|
74
|
+
### Managing workers
|
75
|
+
|
76
|
+
Spawning workers can be done with `localjob`. Run `localjob work` to spawn a
|
77
|
+
single worker. It takes a few arguments. The most important being `--require`
|
78
|
+
which takes a path the worker will require before processing jobs. For Rails,
|
79
|
+
you can run `localjob work` without any arguments. `localjob(2)` has a few other
|
80
|
+
commands such as `list` to list all queues and `size` to list the size of all
|
81
|
+
queues. `localjob help` to list all commands.
|
82
|
+
|
83
|
+
Gracefully shut down workers by sending `SIGQUIT` to them. This will make sure
|
84
|
+
the worker completes its current job before shutting down. Jobs can be sent to
|
85
|
+
the queue meanwhile, and the worker will process them once it starts again.
|
86
|
+
|
87
|
+
### Queues
|
88
|
+
|
89
|
+
Localjobs supports multiple queues, and workers can be assigned to queues. By
|
90
|
+
default everything is on a single queue. To push to a named queue:
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
email = Localjob.new("email")
|
94
|
+
email << EmailJob.new(current_user.id, welcome_email)
|
95
|
+
```
|
96
|
+
|
97
|
+
The worker spawn command `localjob work` takes a `--queues` argument which is a
|
98
|
+
comma seperated list of queues to listen on, e.g. `localjob work --queues email,webhooks`.
|
99
|
+
|
100
|
+
### Testing
|
101
|
+
|
102
|
+
Create your instance of the queue as normal in your setup:
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
def setup
|
106
|
+
@queue = LocalJob.new("test-queue")
|
107
|
+
end
|
108
|
+
```
|
109
|
+
|
110
|
+
In your `teardown` you'll want to destroy your queue:
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
def teardown
|
114
|
+
@queue.destroy
|
115
|
+
end
|
116
|
+
```
|
117
|
+
|
118
|
+
You can get the size of your queue by calling `@queue.size`. You pop off the
|
119
|
+
queue with `@queue.shift`. Other than that, just use the normal API. You can
|
120
|
+
also read the tests for Localjob to get an idea of how to test. Sample test:
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
def test_pop_and_send_to_worker
|
124
|
+
WalrusJob.any_instance.expects(:perform)
|
125
|
+
|
126
|
+
@localjob << WalrusJob.new("move")
|
127
|
+
|
128
|
+
job = @localjob.shift
|
129
|
+
@worker.process(job)
|
130
|
+
|
131
|
+
assert_equal 0, @localjob.size
|
132
|
+
end
|
133
|
+
```
|
54
134
|
|
55
135
|
[pmq]: http://linux.die.net/man/7/mq_overview
|
56
136
|
[pmq-gem]: https://github.com/Sirupsen/posix-mqueue
|
data/ext/mkrf_conf.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rubygems/command.rb'
|
3
|
+
require 'rubygems/dependency_installer.rb'
|
4
|
+
begin
|
5
|
+
Gem::Command.build_args = ARGV
|
6
|
+
rescue NoMethodError
|
7
|
+
end
|
8
|
+
|
9
|
+
inst = Gem::DependencyInstaller.new
|
10
|
+
|
11
|
+
begin
|
12
|
+
if RUBY_PLATFORM =~ /linux/
|
13
|
+
inst.install "posix-mqueue", "0.0.7"
|
14
|
+
end
|
15
|
+
rescue
|
16
|
+
exit(1)
|
17
|
+
end
|
data/lib/localjob/channel.rb
CHANGED
@@ -11,10 +11,12 @@ class Localjob
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def shift
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
begin
|
15
|
+
(queue,), = IO.select(@queues)
|
16
|
+
queue.shift
|
17
|
+
rescue POSIX::Mqueue::QueueEmpty
|
18
|
+
retry
|
19
|
+
end
|
18
20
|
end
|
19
21
|
|
20
22
|
private
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class Localjob
|
2
|
+
class LinuxAdapter
|
3
|
+
attr_reader :mqueue
|
4
|
+
|
5
|
+
def initialize(name)
|
6
|
+
@mqueue = POSIX::Mqueue.new(fix_queue_name(name))
|
7
|
+
end
|
8
|
+
|
9
|
+
def receive
|
10
|
+
@mqueue.timedreceive
|
11
|
+
end
|
12
|
+
|
13
|
+
def send(message)
|
14
|
+
@mqueue.timedsend message
|
15
|
+
end
|
16
|
+
|
17
|
+
def size
|
18
|
+
@mqueue.size
|
19
|
+
end
|
20
|
+
|
21
|
+
def destroy
|
22
|
+
@mqueue.unlink
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_io
|
26
|
+
@mqueue.to_io
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def fix_queue_name(name)
|
31
|
+
name.start_with?('/') ? name : "/#{name}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class Localjob
|
2
|
+
class Channel
|
3
|
+
def shift
|
4
|
+
queue = @queues.find { |q| q.size > 0 }
|
5
|
+
return queue.shift
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class MockAdapter
|
10
|
+
def initialize(name = 'default')
|
11
|
+
@@queues ||= {}
|
12
|
+
@name = name
|
13
|
+
@@queues[@name] ||= []
|
14
|
+
end
|
15
|
+
|
16
|
+
def receive
|
17
|
+
@@queues[@name].shift
|
18
|
+
end
|
19
|
+
|
20
|
+
def send(message)
|
21
|
+
@@queues[@name] << message
|
22
|
+
end
|
23
|
+
|
24
|
+
def size
|
25
|
+
@@queues[@name].size
|
26
|
+
end
|
27
|
+
|
28
|
+
def destroy
|
29
|
+
@@queues[@name] = nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/localjob/version.rb
CHANGED
data/lib/localjob.rb
CHANGED
@@ -1,16 +1,26 @@
|
|
1
|
-
|
1
|
+
begin
|
2
|
+
require 'posix/mqueue'
|
3
|
+
rescue LoadError
|
4
|
+
end
|
5
|
+
|
2
6
|
require 'yaml'
|
3
7
|
require 'logger'
|
8
|
+
require 'forwardable'
|
4
9
|
|
5
10
|
require "localjob/version"
|
6
11
|
require 'localjob/channel'
|
7
12
|
require 'localjob/worker'
|
8
13
|
|
9
14
|
class Localjob
|
10
|
-
|
15
|
+
extend Forwardable
|
16
|
+
|
17
|
+
attr_reader :name
|
18
|
+
attr_accessor :queue
|
19
|
+
|
20
|
+
def_delegators :queue, :to_io, :destroy, :size
|
11
21
|
|
12
|
-
def initialize(
|
13
|
-
@
|
22
|
+
def initialize(name = "localjob")
|
23
|
+
@name = name
|
14
24
|
end
|
15
25
|
|
16
26
|
def serializer
|
@@ -18,31 +28,23 @@ class Localjob
|
|
18
28
|
end
|
19
29
|
|
20
30
|
def queue
|
21
|
-
@queue
|
22
|
-
end
|
31
|
+
return @queue if @queue
|
23
32
|
|
24
|
-
|
25
|
-
|
33
|
+
case RUBY_PLATFORM
|
34
|
+
when /linux/
|
35
|
+
require 'localjob/linux_adapter'
|
36
|
+
@queue = LinuxAdapter.new(@name)
|
37
|
+
else
|
38
|
+
require 'localjob/mock_adapter'
|
39
|
+
@queue = MockAdapter.new(@name)
|
40
|
+
end
|
26
41
|
end
|
27
42
|
|
28
|
-
def
|
29
|
-
queue.
|
43
|
+
def <<(object)
|
44
|
+
queue.send serializer.dump(object)
|
30
45
|
end
|
31
46
|
|
32
47
|
def shift
|
33
|
-
serializer.load queue.
|
34
|
-
end
|
35
|
-
|
36
|
-
def destroy
|
37
|
-
queue.unlink
|
38
|
-
end
|
39
|
-
|
40
|
-
def to_io
|
41
|
-
queue.to_io
|
42
|
-
end
|
43
|
-
|
44
|
-
private
|
45
|
-
def fix_queue_name(queue)
|
46
|
-
queue.start_with?('/') ? queue : "/#{queue}"
|
48
|
+
serializer.load queue.receive
|
47
49
|
end
|
48
50
|
end
|
data/localjob.gemspec
CHANGED
@@ -17,8 +17,8 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
|
+
spec.extensions = ["ext/mkrf_conf.rb"]
|
20
21
|
|
21
|
-
spec.add_dependency "posix-mqueue", "0.0.9"
|
22
22
|
spec.add_dependency "thor", "0.18.1"
|
23
23
|
|
24
24
|
spec.add_development_dependency "bundler", "~> 1.3"
|
data/test/localjob_test.rb
CHANGED
@@ -18,12 +18,6 @@ class LocaljobTest < LocaljobTestCase
|
|
18
18
|
assert_equal "move", job.action
|
19
19
|
end
|
20
20
|
|
21
|
-
def test_throws_error_if_message_is_too_large
|
22
|
-
assert_raises Errno::EMSGSIZE do
|
23
|
-
@localjob << AngryWalrusJob.new("f" * @localjob.queue.msgsize)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
21
|
def test_handles_multiple_queues
|
28
22
|
@localjob << WalrusJob.new("move")
|
29
23
|
|
@@ -33,4 +27,12 @@ class LocaljobTest < LocaljobTestCase
|
|
33
27
|
assert_equal 1, @localjob.size
|
34
28
|
assert_equal 1, other.size
|
35
29
|
end
|
30
|
+
|
31
|
+
on_platform 'linux' do
|
32
|
+
def test_throws_error_if_message_is_too_large
|
33
|
+
assert_raises Errno::EMSGSIZE do
|
34
|
+
@localjob << AngryWalrusJob.new("f" * @localjob.queue.mqueue.msgsize)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
36
38
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class MockAdapterTest < LocaljobTestCase
|
4
|
+
def setup
|
5
|
+
@localjob = queue
|
6
|
+
@localjob.queue = Localjob::MockAdapter.new("localjob")
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_push_to_queue
|
10
|
+
@localjob << "hello world"
|
11
|
+
assert_equal 1, @localjob.size
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_push_and_pop_from_queue
|
15
|
+
@localjob << "hello world"
|
16
|
+
assert_equal "hello world", @localjob.shift
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_destroy_queue
|
20
|
+
@localjob << "hello world"
|
21
|
+
end
|
22
|
+
end
|
data/test/test_helper.rb
CHANGED
data/test/worker_test.rb
CHANGED
@@ -18,38 +18,26 @@ class WorkerTest < LocaljobTestCase
|
|
18
18
|
def test_working_off_queue_in_child
|
19
19
|
@localjob << WalrusJob.new("move")
|
20
20
|
|
21
|
-
|
21
|
+
a = Thread.start {
|
22
22
|
job = @localjob.shift
|
23
23
|
@worker.process(job)
|
24
|
-
|
25
|
-
|
26
|
-
Process.wait
|
27
|
-
assert_equal 0, @localjob.size
|
28
|
-
end
|
29
|
-
|
30
|
-
def test_sigquit_terminates_the_worker
|
31
|
-
@localjob << WalrusJob.new("move")
|
32
|
-
|
33
|
-
assert_equal 1, @localjob.size
|
34
|
-
|
35
|
-
pid = fork { @worker.work }
|
24
|
+
}
|
36
25
|
|
37
|
-
|
38
|
-
Process.wait
|
26
|
+
a.join
|
39
27
|
|
40
28
|
assert_equal 0, @localjob.size
|
41
29
|
end
|
42
30
|
|
31
|
+
|
43
32
|
def test_doesnt_stop_on_error
|
44
33
|
@localjob << AngryWalrusJob.new(100)
|
45
34
|
@localjob << WalrusJob.new("be happy")
|
46
35
|
|
47
|
-
|
36
|
+
a = Thread.start { @worker.work }
|
48
37
|
|
49
38
|
# Hack to account for race condition, 0.01s should be plenty
|
50
39
|
sleep 0.01
|
51
|
-
|
52
|
-
Process.wait
|
40
|
+
a.kill
|
53
41
|
|
54
42
|
assert_equal 0, @localjob.size
|
55
43
|
end
|
@@ -62,24 +50,36 @@ class WorkerTest < LocaljobTestCase
|
|
62
50
|
|
63
51
|
@worker.channel << 'other-queue'
|
64
52
|
|
65
|
-
|
53
|
+
a = Thread.start { @worker.work }
|
66
54
|
|
67
|
-
# Hack to account for race condition, 0.01s should be plenty
|
68
55
|
sleep 0.01
|
69
|
-
|
70
|
-
Process.wait
|
56
|
+
a.kill
|
71
57
|
|
72
58
|
assert_equal 0, @localjob.size
|
73
59
|
assert_equal 0, other.size
|
74
60
|
end
|
75
61
|
|
76
62
|
def test_worker_doesnt_die_on_bad_serialization
|
77
|
-
@localjob.queue.
|
63
|
+
@localjob.queue.send "--- !ruby/object:Whatever {}\n"
|
78
64
|
|
79
|
-
|
65
|
+
a = Thread.start { @worker.work }
|
80
66
|
|
81
67
|
sleep 0.01
|
82
|
-
|
83
|
-
|
68
|
+
a.kill
|
69
|
+
end
|
70
|
+
|
71
|
+
on_platform 'linux' do
|
72
|
+
def test_sigquit_terminates_the_worker
|
73
|
+
@localjob << WalrusJob.new("move")
|
74
|
+
|
75
|
+
assert_equal 1, @localjob.size
|
76
|
+
|
77
|
+
pid = fork { @worker.work }
|
78
|
+
|
79
|
+
Process.kill("QUIT", pid)
|
80
|
+
Process.wait
|
81
|
+
|
82
|
+
assert_equal 0, @localjob.size
|
83
|
+
end
|
84
84
|
end
|
85
85
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: localjob
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Simon Eskildsen
|
@@ -10,20 +10,6 @@ bindir: bin
|
|
10
10
|
cert_chain: []
|
11
11
|
date: 2013-09-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: posix-mqueue
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - '='
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 0.0.9
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - '='
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: 0.0.9
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: thor
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,7 +72,8 @@ email:
|
|
86
72
|
- sirup@sirupsen.com
|
87
73
|
executables:
|
88
74
|
- localjob
|
89
|
-
extensions:
|
75
|
+
extensions:
|
76
|
+
- ext/mkrf_conf.rb
|
90
77
|
extra_rdoc_files: []
|
91
78
|
files:
|
92
79
|
- .gitignore
|
@@ -95,14 +82,18 @@ files:
|
|
95
82
|
- README.md
|
96
83
|
- Rakefile
|
97
84
|
- bin/localjob
|
85
|
+
- ext/mkrf_conf.rb
|
98
86
|
- lib/localjob.rb
|
99
87
|
- lib/localjob/channel.rb
|
100
88
|
- lib/localjob/cli.rb
|
89
|
+
- lib/localjob/linux_adapter.rb
|
90
|
+
- lib/localjob/mock_adapter.rb
|
101
91
|
- lib/localjob/version.rb
|
102
92
|
- lib/localjob/worker.rb
|
103
93
|
- localjob.gemspec
|
104
94
|
- test/jobs.rb
|
105
95
|
- test/localjob_test.rb
|
96
|
+
- test/mock_adapter.rb
|
106
97
|
- test/test_helper.rb
|
107
98
|
- test/worker_test.rb
|
108
99
|
homepage: ''
|
@@ -133,5 +124,6 @@ summary: Simple, self-contained background queue built on top of POSIX message q
|
|
133
124
|
test_files:
|
134
125
|
- test/jobs.rb
|
135
126
|
- test/localjob_test.rb
|
127
|
+
- test/mock_adapter.rb
|
136
128
|
- test/test_helper.rb
|
137
129
|
- test/worker_test.rb
|