hutch-schedule 0.2.0 → 0.3.0
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 +67 -3
- data/hutch-schedule.gemspec +2 -1
- data/lib/active_job/queue_adapters/hutch_adapter.rb +5 -0
- data/lib/hutch/enqueue.rb +13 -5
- data/lib/hutch/error_handlers/max_retry.rb +41 -6
- data/lib/hutch/schedule.rb +2 -0
- data/lib/hutch/schedule/version.rb +1 -1
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5cdb316a95fe95813079a1b059cd8221d1eabfec
|
4
|
+
data.tar.gz: 00057ddbb5ea2c6c810f292edf04a680439125f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 693efd110acf103a7d6513bfab197460f051676a1266c33a75f12936870b44c31fdaa1b67b20cd63adde014063a9d33eb522e3de26c24dce6628a41fd2c3fa39
|
7
|
+
data.tar.gz: 0b4cd7093767aba2b28128ca4fed1d8579354f23082a220c769e4f7ed707a9404e9363aaedf6efa041254a156de3eef3cbfb1e98c9ec5fe328a7223f60d9c76e
|
data/README.md
CHANGED
@@ -23,9 +23,8 @@ Or install it yourself as:
|
|
23
23
|
Use the code below to initialize the Hutch::Schedule
|
24
24
|
|
25
25
|
```ruby
|
26
|
-
Hutch
|
27
|
-
|
28
|
-
}
|
26
|
+
Hutch.connect
|
27
|
+
Hutch::Schedule.connect
|
29
28
|
```
|
30
29
|
|
31
30
|
They will do something below:
|
@@ -34,6 +33,71 @@ They will do something below:
|
|
34
33
|
2. Declear an queue named `<hutch>_schedule_queue` and with some params:
|
35
34
|
- Set `x-dead-letter-exchange: <hutch>`: let queue republish message to default <hutch> exchange.
|
36
35
|
- Set `x-message-ttl: <30.days>`: to avoid the queue is to large, because there is no consumer with this queue.
|
36
|
+
3. If ActiveJob is loaded. it will use `ActiveJob::Base.descendants` to register all ActiveJob class to one-job-per-consumer to Hutch::Consumer
|
37
|
+
|
38
|
+
|
39
|
+
### Error Retry
|
40
|
+
If you want use error retry, then:
|
41
|
+
|
42
|
+
1. Add `Hutch::ErrorHandlers::MaxRetry` to `Hutch::Config.error_handlers` like below
|
43
|
+
```ruby
|
44
|
+
Hutch::Config.error_handlers << Hutch::ErrorHandlers::MaxRetry.new
|
45
|
+
```
|
46
|
+
|
47
|
+
2. Let `Hutch::Consumer` to include `Hutch::Enqueue` and setup `attempts`
|
48
|
+
```ruby
|
49
|
+
class PlanConsumer
|
50
|
+
include Hutch::Consumer
|
51
|
+
include Hutch::Enqueue
|
52
|
+
|
53
|
+
attempts 3
|
54
|
+
consume 'abc.plan'
|
55
|
+
end
|
56
|
+
```
|
57
|
+
|
58
|
+
Error retry will use ActiveJob `exponentially_longer` algorithm `(executes**4) + 2` seconds
|
59
|
+
|
60
|
+
|
61
|
+
## Rails
|
62
|
+
|
63
|
+
### Work with Hutch it`s self
|
64
|
+
Add an `hutch.rb` to `conf/initializers`:
|
65
|
+
```ruby
|
66
|
+
# reuse Hutch config.yaml file
|
67
|
+
Hutch::Config.load_from_file(Rails.root.join('config', 'config.yaml'))
|
68
|
+
# replace error_handlers with Hutch::ErrorHandlers::MaxRetry
|
69
|
+
Hutch::Config.error_handlers = [Hutch::ErrorHandlers::MaxRetry.new]
|
70
|
+
# Init Hutch
|
71
|
+
Hutch.connect
|
72
|
+
# Init Hutch::Schedule
|
73
|
+
Hutch::Schedule.connect
|
74
|
+
```
|
75
|
+
|
76
|
+
Then you can enqueue message in Rails console like below:
|
77
|
+
```ruby
|
78
|
+
PlanConsumer.enqueue(a: 1)
|
79
|
+
# or schedule message
|
80
|
+
PlanConsumer.enqueue_in(5.seconds, a: 1)
|
81
|
+
```
|
82
|
+
|
83
|
+
### Work with ActiveJob
|
84
|
+
```ruby
|
85
|
+
class EmailJob < ApplicationJob
|
86
|
+
queue_as :email
|
87
|
+
|
88
|
+
retry_on StandardError, wait: :exponentially_longer
|
89
|
+
|
90
|
+
def perform(user_id)
|
91
|
+
user = User.find(user_id)
|
92
|
+
user.send_email
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# in rails console, you can
|
97
|
+
EmailJob.perform_later(user.id)
|
98
|
+
# or
|
99
|
+
EmailJob.set(wait: 5.seconds).perform_later(user.id)
|
100
|
+
```
|
37
101
|
|
38
102
|
## Development
|
39
103
|
|
data/hutch-schedule.gemspec
CHANGED
@@ -6,7 +6,7 @@ require 'hutch/schedule/version'
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "hutch-schedule"
|
8
8
|
spec.version = Hutch::Schedule::VERSION
|
9
|
-
spec.authors = ["
|
9
|
+
spec.authors = ["Wyatt pan"]
|
10
10
|
spec.email = ["wppurking@gmail.com"]
|
11
11
|
|
12
12
|
spec.summary = %q{Add Schedule and Error Retry To Hutch.}
|
@@ -22,6 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.require_paths = ["lib"]
|
23
23
|
|
24
24
|
spec.add_runtime_dependency 'hutch', '~> 0.24'
|
25
|
+
spec.add_runtime_dependency "multi_json"
|
25
26
|
|
26
27
|
spec.add_development_dependency "activejob"
|
27
28
|
spec.add_development_dependency "bundler", "~> 1.14"
|
data/lib/hutch/enqueue.rb
CHANGED
@@ -17,16 +17,16 @@ module Hutch
|
|
17
17
|
# publish message at a delay times
|
18
18
|
# interval: delay interval
|
19
19
|
# message: publish message
|
20
|
-
def enqueue_in(interval, message)
|
21
|
-
|
22
|
-
Hutch::Schedule.publish(enqueue_routing_key, message,
|
20
|
+
def enqueue_in(interval, message, props = {})
|
21
|
+
properties = props.merge(expiration: interval.in_milliseconds.to_i)
|
22
|
+
Hutch::Schedule.publish(enqueue_routing_key, message, properties)
|
23
23
|
end
|
24
24
|
|
25
25
|
# delay at exatly time point
|
26
|
-
def enqueue_at(time, message)
|
26
|
+
def enqueue_at(time, message, props = {})
|
27
27
|
# if time is early then now then just delay 1 second
|
28
28
|
interval = [(time.utc - Time.now.utc), 1.second].max
|
29
|
-
enqueue_in(interval, message)
|
29
|
+
enqueue_in(interval, message, props)
|
30
30
|
end
|
31
31
|
|
32
32
|
# routing_key: the purpose is to send message to hutch exchange and then routing to the correct queue,
|
@@ -35,6 +35,14 @@ module Hutch
|
|
35
35
|
raise "Routing Keys is not set!" if routing_keys.size < 1
|
36
36
|
routing_keys.to_a.last
|
37
37
|
end
|
38
|
+
|
39
|
+
def attempts(times)
|
40
|
+
@max_retries = [times, 0].max
|
41
|
+
end
|
42
|
+
|
43
|
+
def max_attempts
|
44
|
+
@max_retries || 0
|
45
|
+
end
|
38
46
|
end
|
39
47
|
end
|
40
48
|
end
|
@@ -1,15 +1,14 @@
|
|
1
1
|
require 'hutch/logging'
|
2
|
+
require 'active_support/core_ext/object/blank'
|
2
3
|
|
3
4
|
module Hutch
|
4
5
|
module ErrorHandlers
|
6
|
+
|
7
|
+
# When reach the Max Attempts, republish this message to RabbitMQ,
|
8
|
+
# And persisted the properties[:headers] to tell RabbitMQ the `x-dead.count`
|
5
9
|
class MaxRetry
|
6
10
|
include Logging
|
7
11
|
|
8
|
-
# TODO: Need to be implement.
|
9
|
-
# 1. 获取 hutch 本身记录的 x-death 中的错误次数
|
10
|
-
# 2. 从每一个 consumer 身上寻找 max_retry 的次数, 不超过则进行延迟重试
|
11
|
-
# 3. 根据错误次数计算类似 active_job 的 exponentially_longer 延迟时间
|
12
|
-
#
|
13
12
|
# properties.headers example:
|
14
13
|
# {
|
15
14
|
# "x-death": [
|
@@ -37,7 +36,43 @@ module Hutch
|
|
37
36
|
# ]
|
38
37
|
# }
|
39
38
|
def handle(properties, payload, consumer, ex)
|
40
|
-
|
39
|
+
unless consumer.ancestors.include?(Hutch::Enqueue)
|
40
|
+
logger.warn("Consumer: #{consumer} is not include Hutch::Enqueue can`t use #enqueue_in`")
|
41
|
+
return false
|
42
|
+
end
|
43
|
+
|
44
|
+
prop_headers = properties[:headers] || {}
|
45
|
+
attempts = failure_count(prop_headers, consumer) + 1
|
46
|
+
if attempts <= consumer.max_attempts
|
47
|
+
logger.debug("retrying, count=#{attempts}, headers:#{prop_headers}")
|
48
|
+
# execute_times = attempts - 1
|
49
|
+
consumer.enqueue_in(retry_delay(attempts - 1), MultiJson.decode(payload), { headers: prop_headers })
|
50
|
+
else
|
51
|
+
logger.debug("failing, retry_count=#{attempts}, ex:#{ex}")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def retry_delay(executes)
|
56
|
+
(executes**4) + 2
|
57
|
+
end
|
58
|
+
|
59
|
+
def failure_count(headers, consumer)
|
60
|
+
if headers.nil? || headers['x-death'].nil?
|
61
|
+
0
|
62
|
+
else
|
63
|
+
x_death_array = headers['x-death'].select do |x_death|
|
64
|
+
# http://ruby-doc.org/stdlib-2.2.3/libdoc/set/rdoc/Set.html#method-i-intersect-3F
|
65
|
+
(x_death['routing-keys'].presence || []).to_set.intersect?(consumer.routing_keys)
|
66
|
+
end
|
67
|
+
|
68
|
+
if x_death_array.count > 0 && x_death_array.first['count']
|
69
|
+
# Newer versions of RabbitMQ return headers with a count key
|
70
|
+
x_death_array.inject(0) { |sum, x_death| sum + x_death['count'] }
|
71
|
+
else
|
72
|
+
# Older versions return a separate x-death header for each failure
|
73
|
+
x_death_array.count
|
74
|
+
end
|
75
|
+
end
|
41
76
|
end
|
42
77
|
end
|
43
78
|
end
|
data/lib/hutch/schedule.rb
CHANGED
@@ -2,6 +2,7 @@ require 'active_support/core_ext/module/delegation'
|
|
2
2
|
require 'active_support/core_ext/object/blank'
|
3
3
|
require 'hutch'
|
4
4
|
require 'hutch/enqueue'
|
5
|
+
require 'hutch/error_handlers/max_retry'
|
5
6
|
require 'hutch/schedule/core'
|
6
7
|
|
7
8
|
# If ActiveJob is requried then required the adapter
|
@@ -16,6 +17,7 @@ module Hutch
|
|
16
17
|
module Schedule
|
17
18
|
|
18
19
|
def self.connect(broker = Hutch.broker)
|
20
|
+
raise "Please invoke Hutch.connect before Hutch::Schedule.connect, Hutch::Schedule need Hutch.broker" unless Hutch.connected?
|
19
21
|
return if core.present?
|
20
22
|
@core = Hutch::Schedule::Core.new(broker)
|
21
23
|
@core.connect!
|
metadata
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hutch-schedule
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- Wyatt pan
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0.24'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: multi_json
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: activejob
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|