message 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/xli/message.svg?branch=master)](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
|