message 0.0.3 → 0.0.4
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.
- data/README.md +18 -89
- data/lib/message/filters/benchmarking.rb +20 -0
- data/lib/message/filters/error_handling.rb +21 -0
- data/lib/message/filters/retry_on_error.rb +28 -0
- data/lib/message/filters.rb +20 -49
- data/lib/message/job.rb +12 -7
- data/lib/message/q.rb +7 -0
- metadata +5 -2
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# Message
|
2
2
|
|
3
|
-
https://travis-ci.org/xli/message.svg?branch=master
|
3
|
+
[](https://travis-ci.org/xli/message)
|
4
|
+
|
4
5
|
|
5
6
|
Message provides flexible & reliable background/asynchronous job processing mechanism on top of simple queue interface.
|
6
7
|
|
@@ -22,7 +23,7 @@ and you can easily swap in other queues later.
|
|
22
23
|
### Queuing jobs
|
23
24
|
|
24
25
|
|
25
|
-
|
26
|
+
Call .async.method(params) on any object and it will be processed in the background.
|
26
27
|
|
27
28
|
# without message
|
28
29
|
@img.resize(36)
|
@@ -30,10 +31,22 @@ Inspired by delayed_job API, call .async.method(params) on any object and it wil
|
|
30
31
|
# with message
|
31
32
|
@img.async.resize(36)
|
32
33
|
|
33
|
-
|
34
|
+
The above .async call will enqueue the job to a default job queue (Message.worker.default_job)
|
35
|
+
|
36
|
+
### Start worker to process default job queue
|
34
37
|
|
35
38
|
Message.worker.start
|
36
39
|
|
40
|
+
### Named job queue
|
41
|
+
|
42
|
+
Queuing jobs into speicific queue named 'image-resize-queue':
|
43
|
+
|
44
|
+
@img.async('image-resize-queue').resize(36)
|
45
|
+
|
46
|
+
Start a worker to process queued jobs:
|
47
|
+
|
48
|
+
Message.worker('image-resize-queue').start
|
49
|
+
|
37
50
|
### Change to synchronize mode
|
38
51
|
|
39
52
|
Message.worker.sync = true
|
@@ -48,93 +61,9 @@ For some environment or queue system (e.g. AWS SQS), you will need set an applic
|
|
48
61
|
|
49
62
|
Message.worker.default_job = "app-name-#{Rails.env}-message-default"
|
50
63
|
|
51
|
-
|
52
|
-
|
53
|
-
Job = a queue + message processor
|
54
|
-
|
55
|
-
### initialize a job
|
56
|
-
|
57
|
-
job = Message.job('name') { |msg| ... process msg ... }
|
58
|
-
|
59
|
-
### enq(msg), alias: <<
|
60
|
-
|
61
|
-
Queue up a message for processing:
|
62
|
-
|
63
|
-
job << msg
|
64
|
-
|
65
|
-
### process(size=1)
|
66
|
-
|
67
|
-
Process a message in queue by processor defined when initializing the job
|
68
|
-
|
69
|
-
job.process
|
70
|
-
|
71
|
-
Process multiple messages
|
72
|
-
|
73
|
-
job.process(5)
|
74
|
-
|
75
|
-
## Job filters
|
76
|
-
|
77
|
-
You can add job filter to add additional functions to enqueue and process job message
|
78
|
-
|
79
|
-
Message.job.filter(filter_name) do |next_filter, job|
|
80
|
-
lambda do |*args, &block|
|
81
|
-
next_filter.call(*args, &block)
|
82
|
-
end
|
83
|
-
end
|
64
|
+
### Change backend queue system
|
84
65
|
|
85
|
-
|
86
|
-
Checkout all filters:
|
87
|
-
|
88
|
-
Message.job.filters
|
89
|
-
|
90
|
-
### enq filter
|
91
|
-
|
92
|
-
Message.job.filter(:enq) do |filter, job|
|
93
|
-
lambda do |work|
|
94
|
-
filter.call(work)
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
### process filter
|
99
|
-
|
100
|
-
Message.job.filter(:process) do |filter, job|
|
101
|
-
lambda do |size, &processor|
|
102
|
-
filter.call(size) do |msg|
|
103
|
-
processor.call(msg)
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
## Queue adapters
|
109
|
-
|
110
|
-
Change queue adapter to change different queue implementation. Default is a in memory queue for testing and development environments.
|
111
|
-
|
112
|
-
### Change adapter
|
66
|
+
By change queue adapter:
|
113
67
|
|
114
68
|
Message.queue.adapter = :sqs
|
115
69
|
|
116
|
-
### Add a new adapter
|
117
|
-
|
118
|
-
Message.queue.adapters[:sqs] = Message::SqsQueue
|
119
|
-
|
120
|
-
## Queue interface specification
|
121
|
-
|
122
|
-
To hook up a new queue into Message.
|
123
|
-
|
124
|
-
### name
|
125
|
-
|
126
|
-
Queue name.
|
127
|
-
|
128
|
-
### enq(msg), alias: <<
|
129
|
-
|
130
|
-
Enqueue message, non-blocking.
|
131
|
-
|
132
|
-
### deq(size, &block)
|
133
|
-
|
134
|
-
Dequeue message, non-blocking.
|
135
|
-
It is up to Queue implementation when to delete the message in queue when deq got called with a block.
|
136
|
-
|
137
|
-
### size
|
138
|
-
|
139
|
-
(Approximate) queue size.
|
140
|
-
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
module Message
|
3
|
+
class Filters
|
4
|
+
class Benchmarking
|
5
|
+
def call(filter, job, action)
|
6
|
+
lambda do |msg|
|
7
|
+
return filter.call(msg) unless action == :process
|
8
|
+
|
9
|
+
ret = nil
|
10
|
+
Message.logger.info { "#{job.name}: processing one message"}
|
11
|
+
s = Benchmark.realtime do
|
12
|
+
ret = filter.call(msg)
|
13
|
+
end
|
14
|
+
Message.logger.info { "#{job.name}: processed in #{(1000 * s).to_i}ms" }
|
15
|
+
ret
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Message
|
2
|
+
class Filters
|
3
|
+
class ErrorHandling
|
4
|
+
attr_accessor :callback
|
5
|
+
|
6
|
+
def call(filter, job, action)
|
7
|
+
lambda do |msg|
|
8
|
+
begin
|
9
|
+
filter.call(msg)
|
10
|
+
rescue => e
|
11
|
+
job_name = job.name rescue 'unknown job(find job name failed)'
|
12
|
+
Message.logger.error {"#{action} #{job_name} message failed, #{e.class}: #{e.message}\n#{e.backtrace.join("\n")}"}
|
13
|
+
if self.callback
|
14
|
+
self.callback.call(e, msg, job, action)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Message
|
2
|
+
class Filters
|
3
|
+
class RetryOnError
|
4
|
+
attr_accessor :tries, :on, :sleep
|
5
|
+
def initialize
|
6
|
+
@tries = 3
|
7
|
+
@on = StandardError
|
8
|
+
@sleep = 0.001
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(filter, _, _)
|
12
|
+
lambda do |arg|
|
13
|
+
@try = 0
|
14
|
+
begin
|
15
|
+
filter.call(arg)
|
16
|
+
rescue self.on => e
|
17
|
+
@try += 1
|
18
|
+
if @try < self.tries
|
19
|
+
Kernel.sleep self.sleep.to_f
|
20
|
+
retry
|
21
|
+
end
|
22
|
+
raise
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/message/filters.rb
CHANGED
@@ -1,66 +1,37 @@
|
|
1
|
-
require '
|
1
|
+
require 'message/filters/error_handling'
|
2
|
+
require 'message/filters/benchmarking'
|
3
|
+
require 'message/filters/retry_on_error'
|
2
4
|
|
3
5
|
module Message
|
4
6
|
class Filters
|
5
|
-
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
attr_accessor :error_handling, :benchmarking, :retry_on_error
|
6
10
|
|
7
11
|
def initialize
|
8
|
-
@data =
|
9
|
-
@
|
10
|
-
|
11
|
-
|
12
|
-
defaults
|
13
|
-
self[t] << [m, method(m)]
|
14
|
-
end
|
12
|
+
@data = []
|
13
|
+
@error_handling = ErrorHandling.new
|
14
|
+
@benchmarking = Benchmarking.new
|
15
|
+
@retry_on_error = RetryOnError.new
|
16
|
+
load(defaults)
|
15
17
|
end
|
16
18
|
|
17
|
-
def
|
18
|
-
@data
|
19
|
+
def <<(filter)
|
20
|
+
@data << filter
|
19
21
|
end
|
20
22
|
|
21
|
-
def
|
22
|
-
|
23
|
-
[
|
24
|
-
[:enq, :error_handling],
|
25
|
-
[:process, :benchmarking]
|
26
|
-
]
|
27
|
-
end
|
28
|
-
|
29
|
-
def error_handling(filter, job)
|
30
|
-
lambda do |arg, &processor|
|
31
|
-
type = processor ? :process : :enq
|
32
|
-
log_error(type, job, (type == :enq ? arg : nil)) do
|
33
|
-
if processor
|
34
|
-
filter.call(arg) do |msg|
|
35
|
-
log_error(type, job, msg) { processor.call(msg) }
|
36
|
-
end
|
37
|
-
else
|
38
|
-
filter.call(arg)
|
39
|
-
end
|
40
|
-
end
|
23
|
+
def load(data)
|
24
|
+
data.each do |m|
|
25
|
+
@data << [m, send(m)]
|
41
26
|
end
|
42
27
|
end
|
43
28
|
|
44
|
-
def
|
45
|
-
|
46
|
-
filter.call(size) do |msg|
|
47
|
-
ret = nil
|
48
|
-
Message.logger.info { "#{job.name}: processing one message"}
|
49
|
-
s = Benchmark.realtime do
|
50
|
-
ret = processor.call(msg)
|
51
|
-
end
|
52
|
-
Message.logger.info { "#{job.name}: processed in #{(1000 * s).to_i}ms" }
|
53
|
-
ret
|
54
|
-
end
|
55
|
-
end
|
29
|
+
def defaults
|
30
|
+
[:error_handling, :benchmarking, :retry_on_error]
|
56
31
|
end
|
57
32
|
|
58
|
-
|
59
|
-
|
60
|
-
block.call
|
61
|
-
rescue => e
|
62
|
-
Message.logger.error {"#{type} #{job.name} message failed, #{e.class}: #{e.message}\n#{e.backtrace.join("\n")}"}
|
63
|
-
config[:error_handling_callback].call(type, job, msg, e)
|
33
|
+
def each(&block)
|
34
|
+
@data.each(&block)
|
64
35
|
end
|
65
36
|
end
|
66
37
|
end
|
data/lib/message/job.rb
CHANGED
@@ -7,8 +7,8 @@ module Message
|
|
7
7
|
@filters ||= Filters.new
|
8
8
|
end
|
9
9
|
|
10
|
-
def filter(
|
11
|
-
filters
|
10
|
+
def filter(name, &block)
|
11
|
+
filters << [name, block]
|
12
12
|
end
|
13
13
|
|
14
14
|
def reset
|
@@ -30,18 +30,23 @@ module Message
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def enq(msg)
|
33
|
-
chain(:enq, @queue.
|
33
|
+
chain(:enq, lambda {|msg| @queue.enq(msg)}).call(msg)
|
34
34
|
end
|
35
35
|
alias :<< :enq
|
36
36
|
|
37
37
|
def process(size=1)
|
38
|
-
|
38
|
+
deq = lambda do |size|
|
39
|
+
@queue.deq(size) do |msg|
|
40
|
+
chain(:process, @processor).call(msg)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
chain(:deq, deq).call(size)
|
39
44
|
end
|
40
45
|
|
41
46
|
private
|
42
|
-
def chain(
|
43
|
-
Job.filters
|
44
|
-
f[1].call(m, self)
|
47
|
+
def chain(action, base)
|
48
|
+
Job.filters.to_a.reverse.inject(base) do |m, f|
|
49
|
+
f[1].call(m, self, action)
|
45
50
|
end
|
46
51
|
end
|
47
52
|
end
|
data/lib/message/q.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
require 'message/in_memory_queue'
|
2
2
|
module Message
|
3
|
+
class AdapterNotFoundError < StandardError
|
4
|
+
end
|
5
|
+
|
3
6
|
module Q
|
4
7
|
module_function
|
5
8
|
def init(name)
|
@@ -15,6 +18,10 @@ module Message
|
|
15
18
|
end
|
16
19
|
|
17
20
|
def adapter=(name)
|
21
|
+
name = name.to_sym
|
22
|
+
unless adapters.has_key?(name)
|
23
|
+
raise AdapterNotFoundError, "Could not find adapter named #{name.inspect}"
|
24
|
+
end
|
18
25
|
@adapter = name
|
19
26
|
end
|
20
27
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: message
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-09-
|
12
|
+
date: 2014-09-13 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: ! 'Message provides flexible & reliable background/asynchronous job processing
|
15
15
|
mechanism on top of simple queue interface.
|
@@ -31,6 +31,9 @@ extensions: []
|
|
31
31
|
extra_rdoc_files: []
|
32
32
|
files:
|
33
33
|
- README.md
|
34
|
+
- lib/message/filters/benchmarking.rb
|
35
|
+
- lib/message/filters/error_handling.rb
|
36
|
+
- lib/message/filters/retry_on_error.rb
|
34
37
|
- lib/message/filters.rb
|
35
38
|
- lib/message/in_memory_queue.rb
|
36
39
|
- lib/message/job.rb
|