leveret 0.1.4 → 0.1.5
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/lib/leveret/configuration.rb +14 -1
- data/lib/leveret/delay_queue.rb +37 -0
- data/lib/leveret/job.rb +9 -5
- data/lib/leveret/message.rb +27 -0
- data/lib/leveret/queue.rb +5 -6
- data/lib/leveret/result_handler.rb +58 -0
- data/lib/leveret/version.rb +1 -1
- data/lib/leveret/worker.rb +9 -31
- data/lib/leveret.rb +8 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c09edf84323e1f658a68c1a63e642fbe6a24570e
|
4
|
+
data.tar.gz: d81b7f5a2637e8a3b6831db528a0aed27b17458e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 94522b76698bc0bac4d171aad0b9a91a2ff9c7ded392539a78c450e97a4e910cc796258ca2f94d535a8df6a84eebe74f01dca445e5d093bec93fa5700b54eb6d
|
7
|
+
data.tar.gz: c5b6f7493a901b39a6abe11d9de9df0b3d05460915907cc534249220afec2766c2cebe4cc1d66ab9ed312ff9bc7c5e80d27e5a5f07d3acf93ecf56b4f4e1bf53
|
@@ -9,6 +9,16 @@ module Leveret
|
|
9
9
|
# @!attribute queue_name_prefix
|
10
10
|
# @return [String] This value will be prefixed to all queues created on your RabbitMQ instance.
|
11
11
|
# Default: +"leveret_queue"+
|
12
|
+
# @!attribute delay_exchange_name
|
13
|
+
# @return [String] Name of the exchange for Leveret to publish to that should be processed later.
|
14
|
+
# Default: +"leveret_delay_exch"+
|
15
|
+
# @!attribute delay_queue_name
|
16
|
+
# @return [String] Name of the queue for Leveret to use to store messages that should be processed later.
|
17
|
+
# Default: +"leveret_delay_queue"+
|
18
|
+
# @!attribute delay_time
|
19
|
+
# @return [Integer] Amount of time a delayed message should say on the delay queue in milliseconds Default: +10000+
|
20
|
+
# @!attribute delay_exchange_name
|
21
|
+
# @return [String] Name of the exchange for Leveret to publish messages to. Default: +"leveret_exch"+
|
12
22
|
# @!attribute log_file
|
13
23
|
# @return [String] The path where logfiles should be written to. Default: +STDOUT+
|
14
24
|
# @!attribute log_level
|
@@ -24,7 +34,7 @@ module Leveret
|
|
24
34
|
# @return [Integer] The number of jobs that can be processes simultanously. Default: +1+
|
25
35
|
class Configuration
|
26
36
|
attr_accessor :amqp, :exchange_name, :queue_name_prefix, :log_file, :log_level, :default_queue_name, :after_fork,
|
27
|
-
:error_handler, :concurrent_fork_count
|
37
|
+
:error_handler, :concurrent_fork_count, :delay_exchange_name, :delay_queue_name, :delay_time
|
28
38
|
|
29
39
|
# Create a new instance of Configuration with a set of sane defaults.
|
30
40
|
def initialize
|
@@ -40,6 +50,9 @@ module Leveret
|
|
40
50
|
self.log_level = Logger::DEBUG
|
41
51
|
self.queue_name_prefix = 'leveret_queue'
|
42
52
|
self.default_queue_name = 'standard'
|
53
|
+
self.delay_exchange_name = 'leveret_delay_exch'
|
54
|
+
self.delay_queue_name = 'leveret_delay_queue'
|
55
|
+
self.delay_time = 10_000
|
43
56
|
self.after_fork = proc {}
|
44
57
|
self.error_handler = proc { |ex| ex }
|
45
58
|
self.concurrent_fork_count = 1
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Leveret
|
2
|
+
# Connects to a special queue which keeps messages in a holding pattern until a timeout expires and then
|
3
|
+
# publishes those messages back to the main queue for processing.
|
4
|
+
class DelayQueue
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
attr_reader :queue
|
8
|
+
|
9
|
+
def_delegators :Leveret, :configuration, :channel, :log
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@queue = connect_to_queue
|
13
|
+
end
|
14
|
+
|
15
|
+
# Place a message onto the delay queue, which will later be expired and sent back to the main exchange
|
16
|
+
#
|
17
|
+
# @param [Message] A message received and processed already
|
18
|
+
def republish(message)
|
19
|
+
delay_exchange.publish(message.params.serialize, expiration: configuration.delay_time, persistent: true,
|
20
|
+
routing_key: message.routing_key, priority: message.priority)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def connect_to_queue
|
26
|
+
queue = channel.queue(configuration.delay_queue_name, durable: true,
|
27
|
+
arguments: { 'x-dead-letter-exchange': configuration.exchange_name })
|
28
|
+
queue.bind(delay_exchange)
|
29
|
+
log.info "Connected to #{configuration.delay_queue_name}, bound to #{configuration.delay_exchange_name}"
|
30
|
+
queue
|
31
|
+
end
|
32
|
+
|
33
|
+
def delay_exchange
|
34
|
+
@delay_exchange ||= channel.exchange(Leveret.configuration.delay_exchange_name, type: :fanout, durable: :true)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/leveret/job.rb
CHANGED
@@ -36,14 +36,15 @@ module Leveret
|
|
36
36
|
# MyJob.enqueue(test_text: "Hi there, please write this different text to the file", queue_name: 'other_queue')
|
37
37
|
#
|
38
38
|
module Job
|
39
|
-
# Raise this when your job has failed, but try again
|
40
|
-
# available again.
|
39
|
+
# Raise this when your job has failed, but try again as soon as another worker is available.
|
41
40
|
class RequeueJob < StandardError; end
|
42
41
|
|
43
|
-
# Raise this when your job has failed, but you don't want to requeue it
|
44
|
-
# and try again.
|
42
|
+
# Raise this when your job has failed, but you don't want to requeue it and try again.
|
45
43
|
class RejectJob < StandardError; end
|
46
44
|
|
45
|
+
# Raise thie when you want your job to be executed later (later is defined in {Leveret.configuration.delay_time})
|
46
|
+
class DelayJob < StandardError; end
|
47
|
+
|
47
48
|
# Instance methods to mixin with your job
|
48
49
|
module InstanceMethods
|
49
50
|
# @!attribute params
|
@@ -59,7 +60,7 @@ module Leveret
|
|
59
60
|
|
60
61
|
# Runs the job and captures any exceptions to turn them into symbols which represent the status of the job
|
61
62
|
#
|
62
|
-
# @return [Symbol] :success, :requeue, :reject depending on job success
|
63
|
+
# @return [Symbol] :success, :requeue, :reject, :delay depending on job success
|
63
64
|
def run
|
64
65
|
Leveret.log.info "Running #{self.class.name} with #{params}"
|
65
66
|
perform
|
@@ -70,6 +71,9 @@ module Leveret
|
|
70
71
|
rescue Leveret::Job::RejectJob
|
71
72
|
Leveret.log.warn "Rejecting job #{self.class.name} with #{params}"
|
72
73
|
:reject
|
74
|
+
rescue Leveret::Job::DelayJob
|
75
|
+
Leveret.log.warn "Delaying job #{self.class.name} with #{params}"
|
76
|
+
:delay
|
73
77
|
rescue StandardError => e
|
74
78
|
Leveret.log.error "#{e.message} when processing #{self.class.name} with #{params}"
|
75
79
|
Leveret.configuration.error_handler.call(e, self)
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Leveret
|
2
|
+
# Roll up all of the separate parts of an incoming message into a nice convenient object to move around
|
3
|
+
#
|
4
|
+
# @!attribute delivery_info
|
5
|
+
# @return [Bunny::DeliveryInfo] Full of useful things like the delivery_tag and routing key
|
6
|
+
# @!attribute properties
|
7
|
+
# @return [Delivery::Properties] Full of useful things like content-type and priority
|
8
|
+
# @!attribute params
|
9
|
+
# @return [Parameters] Deserialized params
|
10
|
+
class Message
|
11
|
+
extend Forwardable
|
12
|
+
|
13
|
+
attr_accessor :delivery_info, :properties, :params
|
14
|
+
|
15
|
+
def_delegators :delivery_info, :delivery_tag, :routing_key
|
16
|
+
def_delegators :properties, :priority
|
17
|
+
|
18
|
+
# @param [Bunny::DeliveryInfo] delivery_info Full of useful things like the delivery_tag and routing key
|
19
|
+
# @param [Bunny::Properties] properties Full of useful things like content-type and priority
|
20
|
+
# @param [Parameters] Deserialized params parsed from the message
|
21
|
+
def initialize(delivery_info, properties, params)
|
22
|
+
self.delivery_info = delivery_info
|
23
|
+
self.properties = properties
|
24
|
+
self.params = params
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/leveret/queue.rb
CHANGED
@@ -41,7 +41,7 @@ module Leveret
|
|
41
41
|
payload = serialize_payload(payload)
|
42
42
|
|
43
43
|
log.debug "Publishing #{payload.inspect} for queue #{name} (Priority: #{priority_id})"
|
44
|
-
|
44
|
+
exchange.publish(payload, persistent: true, routing_key: name, priority: priority_id)
|
45
45
|
end
|
46
46
|
|
47
47
|
# Subscribe to this queue and yield a block for every message received. This method does not block, receiving and
|
@@ -53,16 +53,15 @@ module Leveret
|
|
53
53
|
#
|
54
54
|
# @note The receiving block is responsible for acking/rejecting the message. Please see the note for more details.
|
55
55
|
#
|
56
|
-
# @yieldparam
|
57
|
-
# @yieldparam delivery_tag [String] The identifier for this message that must be used do ack/reject the message
|
58
|
-
# @yieldparam payload [Parameters] A deserialized version of the payload contained in the message
|
56
|
+
# @yieldparam incoming [Message] Delivery info, properties and the params wrapped up into a convenient object
|
59
57
|
#
|
60
58
|
# @return [void]
|
61
59
|
def subscribe
|
62
60
|
log.info "Subscribing to #{name}"
|
63
|
-
queue.subscribe(manual_ack: true) do |delivery_info,
|
61
|
+
queue.subscribe(manual_ack: true) do |delivery_info, properties, msg|
|
64
62
|
log.debug "Received #{msg} from #{name}"
|
65
|
-
|
63
|
+
incoming = Leveret::Message.new(delivery_info, properties, deserialize_payload(msg))
|
64
|
+
yield incoming
|
66
65
|
end
|
67
66
|
end
|
68
67
|
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Leveret
|
2
|
+
# Handles the acknowledgement or rejection of messages after execution
|
3
|
+
class ResultHandler
|
4
|
+
extend Forwardable
|
5
|
+
|
6
|
+
attr_accessor :incoming_message
|
7
|
+
|
8
|
+
def_delegators :Leveret, :log, :delay_queue
|
9
|
+
|
10
|
+
# @param [Message] incoming_message Contains delivery information such as the delivery_tag
|
11
|
+
def initialize(incoming_message)
|
12
|
+
self.incoming_message = incoming_message
|
13
|
+
end
|
14
|
+
|
15
|
+
# Call the appropriate handling method for the result
|
16
|
+
#
|
17
|
+
# @param [Symbol] result Result returned from running the job, one of +:success+, +:reject+ or +:requeue+
|
18
|
+
def handle(result)
|
19
|
+
log.info "[#{delivery_tag}] Job returned #{result}"
|
20
|
+
send(result) if [:success, :reject, :requeue, :delay].include?(result)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Mark the message as acknowledged
|
24
|
+
def success
|
25
|
+
log.debug "[#{delivery_tag}] Acknowledging message"
|
26
|
+
channel.acknowledge(delivery_tag)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Mark the message as rejected (failure)
|
30
|
+
def reject
|
31
|
+
log.debug "[#{delivery_tag}] Rejecting message"
|
32
|
+
channel.reject(delivery_tag)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Reject the message and reinsert it onto it's queue
|
36
|
+
def requeue
|
37
|
+
log.debug "[#{delivery_tag}] Requeueing message"
|
38
|
+
channel.reject(delivery_tag, true)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Acknowledge the message, but publish it onto the delay queue for execution later
|
42
|
+
def delay
|
43
|
+
log.debug ["[#{delivery_tag}] Delaying message"]
|
44
|
+
channel.acknowledge(delivery_tag)
|
45
|
+
delay_queue.republish(incoming_message)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def channel
|
51
|
+
incoming_message.delivery_info.channel
|
52
|
+
end
|
53
|
+
|
54
|
+
def delivery_tag
|
55
|
+
incoming_message.delivery_info.delivery_tag
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/leveret/version.rb
CHANGED
data/lib/leveret/worker.rb
CHANGED
@@ -69,8 +69,8 @@ module Leveret
|
|
69
69
|
# allow us to gracefully cancel these subscriptions when we need to quit.
|
70
70
|
def start_subscriptions
|
71
71
|
queues.map do |queue|
|
72
|
-
consumers << queue.subscribe do |
|
73
|
-
fork_and_run(
|
72
|
+
consumers << queue.subscribe do |incoming_message|
|
73
|
+
fork_and_run(incoming_message)
|
74
74
|
end
|
75
75
|
end
|
76
76
|
end
|
@@ -88,23 +88,20 @@ module Leveret
|
|
88
88
|
# Detach the main process from the child so we can return to the main loop without waiting for it to finish
|
89
89
|
# processing the job.
|
90
90
|
#
|
91
|
-
# @param [
|
92
|
-
|
93
|
-
# or reject the message after processing.
|
94
|
-
# @param [Parameters] payload The job name and parameters the job requires
|
95
|
-
def fork_and_run(channel, delivery_tag, payload)
|
91
|
+
# @param [Message] payload Message meta and payload to process
|
92
|
+
def fork_and_run(incoming_message)
|
96
93
|
pid = fork do
|
97
94
|
self.process_name = 'leveret-worker-child'
|
98
|
-
log.info "[#{delivery_tag}] Forked to child process #{pid} to run #{payload[:job]}"
|
95
|
+
log.info "[#{incoming_message.delivery_tag}] Forked to child process #{pid} to run #{payload[:job]}"
|
99
96
|
|
100
97
|
Leveret.reset_connection!
|
101
98
|
Leveret.configuration.after_fork.call
|
102
99
|
|
103
|
-
result = perform_job(
|
104
|
-
|
105
|
-
|
100
|
+
result = perform_job(incoming_message.params)
|
101
|
+
result_handler = Leveret::ResultHandler.new(incoming_message)
|
102
|
+
result_handler.handle(result)
|
106
103
|
|
107
|
-
log.info "[#{delivery_tag}] Exiting child process #{pid}"
|
104
|
+
log.info "[#{incoming_message.delivery_tag}] Exiting child process #{pid}"
|
108
105
|
exit!(0)
|
109
106
|
end
|
110
107
|
|
@@ -120,24 +117,5 @@ module Leveret
|
|
120
117
|
job_klass = Object.const_get(payload[:job])
|
121
118
|
job_klass.perform(Leveret::Parameters.new(payload[:params]))
|
122
119
|
end
|
123
|
-
|
124
|
-
# Sends a message back to RabbitMQ confirming the completed execution of the message
|
125
|
-
#
|
126
|
-
# @param [Bunny::Channel] channel RabbitMQ channel to send the ack message on
|
127
|
-
# @param [String] delivery_tag The identifier that RabbitMQ uses to track the message. This will be used to ack
|
128
|
-
# or reject the message after processing.
|
129
|
-
# @param [Symbol] result :success, :reject or :requeue depending on how we want to acknowledge the message
|
130
|
-
def ack_message(channel, delivery_tag, result)
|
131
|
-
if result == :reject
|
132
|
-
log.debug "[#{delivery_tag}] Rejecting message"
|
133
|
-
channel.reject(delivery_tag)
|
134
|
-
elsif result == :requeue
|
135
|
-
log.debug "[#{delivery_tag}] Requeueing message"
|
136
|
-
channel.reject(delivery_tag, true)
|
137
|
-
else
|
138
|
-
log.debug "[#{delivery_tag}] Acknowledging message"
|
139
|
-
channel.acknowledge(delivery_tag)
|
140
|
-
end
|
141
|
-
end
|
142
120
|
end
|
143
121
|
end
|
data/lib/leveret.rb
CHANGED
@@ -3,10 +3,13 @@ require 'json'
|
|
3
3
|
require 'logger'
|
4
4
|
|
5
5
|
require 'leveret/configuration'
|
6
|
+
require 'leveret/delay_queue'
|
6
7
|
require 'leveret/job'
|
7
8
|
require 'leveret/log_formatter'
|
9
|
+
require 'leveret/message'
|
8
10
|
require 'leveret/parameters'
|
9
11
|
require 'leveret/queue'
|
12
|
+
require 'leveret/result_handler'
|
10
13
|
require 'leveret/worker'
|
11
14
|
require "leveret/version"
|
12
15
|
|
@@ -54,9 +57,14 @@ module Leveret
|
|
54
57
|
end
|
55
58
|
end
|
56
59
|
|
60
|
+
def delay_queue
|
61
|
+
@delay_queue ||= Leveret::DelayQueue.new
|
62
|
+
end
|
63
|
+
|
57
64
|
def reset_connection!
|
58
65
|
@mq_connection = nil
|
59
66
|
@channel = nil
|
67
|
+
@delay_queue = nil
|
60
68
|
end
|
61
69
|
|
62
70
|
# Logger used throughout Leveret, see {Configuration} for config options.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: leveret
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dan Wentworth
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-08-
|
11
|
+
date: 2016-08-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bunny
|
@@ -101,10 +101,13 @@ files:
|
|
101
101
|
- lib/leveret.rb
|
102
102
|
- lib/leveret/cli.rb
|
103
103
|
- lib/leveret/configuration.rb
|
104
|
+
- lib/leveret/delay_queue.rb
|
104
105
|
- lib/leveret/job.rb
|
105
106
|
- lib/leveret/log_formatter.rb
|
107
|
+
- lib/leveret/message.rb
|
106
108
|
- lib/leveret/parameters.rb
|
107
109
|
- lib/leveret/queue.rb
|
110
|
+
- lib/leveret/result_handler.rb
|
108
111
|
- lib/leveret/version.rb
|
109
112
|
- lib/leveret/worker.rb
|
110
113
|
homepage: https://github.com/darkphnx/leveret
|