services 2.1.0 → 2.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/lib/services/base.rb +0 -3
- data/lib/services/modules/call_logger.rb +6 -4
- data/lib/services/modules/uniqueness_checker.rb +38 -26
- data/lib/services/version.rb +1 -1
- data/spec/services/modules/uniqueness_checker_spec.rb +39 -18
- data/spec/support/test_services.rb +8 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1e3f42f5b3d4869497acf2e3405f0f278b25582d
|
4
|
+
data.tar.gz: 8c4d047373b919125c3117da284fd345f7a81816
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3c776d68b4a336b6a7a04a0003137a176447d1a13a0061c25d3e78806b2243ee81c1cec2a46951be2713acabef16d734681a911cb81eb2ca4a2667b5ea1ae4df
|
7
|
+
data.tar.gz: 933e0c58c79076b20ab1448b7c6105154703b5aea272ef590d17002d4b092fea4cc5149fd385d1b8d1d6c1af7114fa6446501ef104ac141ec09ed58a4e95a4db
|
data/CHANGELOG.md
CHANGED
data/lib/services/base.rb
CHANGED
@@ -73,9 +73,6 @@ module Services
|
|
73
73
|
|
74
74
|
def controller
|
75
75
|
@controller ||= begin
|
76
|
-
# raise "You must use Rails to use the `controller` helper." unless defined?(Rails)
|
77
|
-
# host = Rails.application.routes.default_url_options[:host] || ActionMailer::Base.default_url_options[:host]
|
78
|
-
# Rails.application.routes.default_url_options[:host]
|
79
76
|
raise 'Please configure host.' if Services.configuration.host.nil?
|
80
77
|
request = ActionDispatch::TestRequest.new
|
81
78
|
request.host = Services.configuration.host
|
@@ -12,16 +12,18 @@ module Services
|
|
12
12
|
end
|
13
13
|
|
14
14
|
module ClassMethods
|
15
|
-
@
|
15
|
+
@_call_logging_disabled = false
|
16
16
|
|
17
|
-
|
17
|
+
def call_logging_disabled
|
18
|
+
@_call_logging_disabled
|
19
|
+
end
|
18
20
|
|
19
21
|
def disable_call_logging
|
20
|
-
@
|
22
|
+
@_call_logging_disabled = true
|
21
23
|
end
|
22
24
|
|
23
25
|
def enable_call_logging
|
24
|
-
@
|
26
|
+
@_call_logging_disabled = false
|
25
27
|
end
|
26
28
|
end
|
27
29
|
|
@@ -10,6 +10,7 @@ module Services
|
|
10
10
|
fail
|
11
11
|
ignore
|
12
12
|
reschedule
|
13
|
+
return
|
13
14
|
)
|
14
15
|
|
15
16
|
MAX_RETRIES = 10
|
@@ -21,45 +22,56 @@ module Services
|
|
21
22
|
|
22
23
|
def check_uniqueness(*args, on_error: :fail)
|
23
24
|
raise "on_error must be one of #{ON_ERROR.join(', ')}, but was #{on_error}" unless ON_ERROR.include?(on_error.to_sym)
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
if @
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
else
|
36
|
-
increase_error_count
|
37
|
-
reschedule
|
38
|
-
end
|
25
|
+
@_on_error = on_error
|
26
|
+
raise 'Service args not found.' if @_service_args.nil?
|
27
|
+
@_uniqueness_args = args.empty? ? @_service_args : args
|
28
|
+
new_uniqueness_key = uniqueness_key(@_uniqueness_args)
|
29
|
+
raise "A uniqueness key with args #{@_uniqueness_args.inspect} already exists." if @_uniqueness_keys && @_uniqueness_keys.include?(new_uniqueness_key)
|
30
|
+
if @_similar_service_id = Services.configuration.redis.get(new_uniqueness_key)
|
31
|
+
if on_error.to_sym == :ignore
|
32
|
+
return false
|
33
|
+
else
|
34
|
+
@_retries_exhausted = on_error.to_sym == :reschedule && error_count >= MAX_RETRIES
|
35
|
+
raise_not_unique_error
|
39
36
|
end
|
40
|
-
false
|
41
37
|
else
|
42
|
-
@
|
43
|
-
@
|
38
|
+
@_uniqueness_keys ||= []
|
39
|
+
@_uniqueness_keys << new_uniqueness_key
|
44
40
|
Services.configuration.redis.setex new_uniqueness_key, ONE_HOUR, @id
|
45
41
|
true
|
46
42
|
end
|
47
43
|
end
|
48
44
|
|
49
45
|
def call(*args)
|
50
|
-
@
|
46
|
+
@_service_args = args
|
51
47
|
super
|
48
|
+
rescue self.class::NotUniqueError => e
|
49
|
+
case @_on_error.to_sym
|
50
|
+
when :fail
|
51
|
+
raise e
|
52
|
+
when :reschedule
|
53
|
+
if @_retries_exhausted
|
54
|
+
raise e
|
55
|
+
else
|
56
|
+
increase_error_count
|
57
|
+
reschedule
|
58
|
+
end
|
59
|
+
when :return
|
60
|
+
return e
|
61
|
+
else
|
62
|
+
raise "Unexpected on_error: #{@_on_error}"
|
63
|
+
end
|
52
64
|
ensure
|
53
|
-
Services.configuration.redis.del @
|
65
|
+
Services.configuration.redis.del @_uniqueness_keys unless @_uniqueness_keys.nil? || @_uniqueness_keys.empty?
|
54
66
|
Services.configuration.redis.del error_count_key
|
55
67
|
end
|
56
68
|
|
57
69
|
private
|
58
70
|
|
59
|
-
def
|
60
|
-
message = "Service #{self.class} #{@id} with uniqueness args #{@
|
61
|
-
message << " The service has been retried #{MAX_RETRIES} times."
|
62
|
-
raise self.class::NotUniqueError
|
71
|
+
def raise_not_unique_error
|
72
|
+
message = "Service #{self.class} #{@id} with uniqueness args #{@_uniqueness_args} is not unique, a similar service is already running: #{@_similar_service_id}."
|
73
|
+
message << " The service has been retried #{MAX_RETRIES} times." if @_retries_exhausted
|
74
|
+
raise self.class::NotUniqueError.new(message)
|
63
75
|
end
|
64
76
|
|
65
77
|
def convert_for_rescheduling(arg)
|
@@ -79,7 +91,7 @@ module Services
|
|
79
91
|
|
80
92
|
def reschedule
|
81
93
|
# Convert service args for rescheduling first
|
82
|
-
reschedule_args = @
|
94
|
+
reschedule_args = @_service_args.map do |arg|
|
83
95
|
convert_for_rescheduling arg
|
84
96
|
end
|
85
97
|
log "Rescheduling to be executed in #{retry_delay} seconds." if self.respond_to?(:log)
|
@@ -109,7 +121,7 @@ module Services
|
|
109
121
|
'errors',
|
110
122
|
self.class.to_s.gsub(':', '_')
|
111
123
|
].tap do |key|
|
112
|
-
key << Digest::MD5.hexdigest(@
|
124
|
+
key << Digest::MD5.hexdigest(@_service_args.to_s) unless @_service_args.empty?
|
113
125
|
end.join(':')
|
114
126
|
end
|
115
127
|
|
data/lib/services/version.rb
CHANGED
@@ -2,52 +2,73 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
shared_examples 'checking the uniqueness properly' do
|
4
4
|
it 'notices when the same job is executed multiple times' do
|
5
|
-
wait_for_job_to_run_and_finish
|
5
|
+
wait_for_job_to_run_and_finish service_class, *args, 'fail', true do
|
6
6
|
# Check that error is raised when on_error is "fail"
|
7
|
+
puts 'Checking on_error = fail'
|
7
8
|
if defined?(fail_args)
|
9
|
+
puts "* with fail args #{fail_args}"
|
8
10
|
3.times do
|
9
11
|
fail_args.each do |fail_arg_group|
|
10
|
-
|
12
|
+
service = service_class.new
|
13
|
+
expect(service).to_not receive(:do_work)
|
14
|
+
expect { service.call(*fail_arg_group, 'fail', false) }.to raise_error(service_class::NotUniqueError)
|
11
15
|
end
|
12
16
|
end
|
13
17
|
end
|
14
18
|
if defined?(pass_args)
|
19
|
+
puts "* with pass args #{pass_args}"
|
15
20
|
3.times do
|
16
21
|
pass_args.each do |pass_arg_group|
|
22
|
+
service = service_class.new
|
23
|
+
expect(service).to receive(:do_work)
|
17
24
|
expect { service.call(*pass_arg_group, 'fail', false) }.to_not raise_error
|
18
25
|
end
|
19
26
|
end
|
20
27
|
end
|
21
28
|
|
22
29
|
# Check that no error is raised when on_error is "ignore"
|
30
|
+
puts 'Checking on_error = ignore'
|
23
31
|
if defined?(fail_args)
|
32
|
+
puts "* with fail args #{fail_args}"
|
24
33
|
3.times do
|
25
34
|
fail_args.each do |fail_arg_group|
|
35
|
+
service = service_class.new
|
36
|
+
expect(service).to receive(:do_work)
|
26
37
|
expect { service.call(*fail_arg_group, 'ignore', false) }.to_not raise_error
|
27
38
|
end
|
28
39
|
end
|
29
40
|
end
|
30
41
|
if defined?(pass_args)
|
42
|
+
puts "* with pass args #{pass_args}"
|
31
43
|
3.times do
|
32
44
|
pass_args.each do |pass_arg_group|
|
45
|
+
service = service_class.new
|
46
|
+
expect(service).to receive(:do_work)
|
33
47
|
expect { service.call(*pass_arg_group, 'ignore', false) }.to_not raise_error
|
34
48
|
end
|
35
49
|
end
|
36
50
|
end
|
37
51
|
|
38
52
|
# Check that service is rescheduled when on_error is "reschedule"
|
53
|
+
puts 'Checking on_error = reschedule'
|
39
54
|
if defined?(fail_args)
|
55
|
+
puts "* with fail args #{fail_args}"
|
40
56
|
3.times do
|
41
57
|
fail_args.each do |fail_arg_group|
|
42
|
-
|
58
|
+
service = service_class.new
|
59
|
+
expect(service).to_not receive(:do_work)
|
60
|
+
expect(service_class).to receive(:perform_in).with(an_instance_of(Fixnum), *fail_arg_group, 'reschedule', false)
|
43
61
|
expect { service.call(*fail_arg_group, 'reschedule', false) }.to_not raise_error
|
44
62
|
end
|
45
63
|
end
|
46
64
|
end
|
47
65
|
if defined?(pass_args)
|
66
|
+
puts "* with pass args #{pass_args}"
|
48
67
|
3.times do
|
49
68
|
pass_args.each do |pass_arg_group|
|
50
|
-
|
69
|
+
service = service_class.new
|
70
|
+
expect(service).to receive(:do_work)
|
71
|
+
expect(service_class).to_not receive(:perform_in)
|
51
72
|
expect { service.call(*pass_arg_group, 'reschedule', false) }.to_not raise_error
|
52
73
|
end
|
53
74
|
end
|
@@ -63,35 +84,35 @@ end
|
|
63
84
|
describe Services::Base::UniquenessChecker do
|
64
85
|
context 'when the service checks for uniqueness with the default args' do
|
65
86
|
it_behaves_like 'checking the uniqueness properly' do
|
66
|
-
let(:
|
67
|
-
let(:args)
|
68
|
-
let(:fail_args)
|
87
|
+
let(:service_class) { UniqueService }
|
88
|
+
let(:args) { [] }
|
89
|
+
let(:fail_args) { [] }
|
69
90
|
end
|
70
91
|
end
|
71
92
|
|
72
93
|
context 'when the service checks for uniqueness with custom args' do
|
73
94
|
it_behaves_like 'checking the uniqueness properly' do
|
74
|
-
let(:
|
75
|
-
let(:args)
|
76
|
-
let(:fail_args)
|
77
|
-
let(:pass_args)
|
95
|
+
let(:service_class) { UniqueWithCustomArgsService }
|
96
|
+
let(:args) { ['foo', 1, 'bar'] }
|
97
|
+
let(:fail_args) { [['foo', 1, 'pelle']] }
|
98
|
+
let(:pass_args) { [['foo', 2, 'bar']] }
|
78
99
|
end
|
79
100
|
end
|
80
101
|
|
81
102
|
context 'when the service checks for uniqueness multiple times' do
|
82
103
|
it_behaves_like 'checking the uniqueness properly' do
|
83
|
-
let(:
|
84
|
-
let(:args)
|
85
|
-
let(:fail_args)
|
86
|
-
let(:pass_args)
|
104
|
+
let(:service_class) { UniqueMultipleService }
|
105
|
+
let(:args) { ['foo', 1, true] }
|
106
|
+
let(:fail_args) { args.map { |arg| [arg] } }
|
107
|
+
let(:pass_args) { [['pelle']] }
|
87
108
|
end
|
88
109
|
end
|
89
110
|
|
90
111
|
context 'when the service does not check for uniqueness' do
|
91
112
|
it_behaves_like 'checking the uniqueness properly' do
|
92
|
-
let(:
|
93
|
-
let(:args)
|
94
|
-
let(:pass_args)
|
113
|
+
let(:service_class) { NonUniqueService }
|
114
|
+
let(:args) { [] }
|
115
|
+
let(:pass_args) { [] }
|
95
116
|
end
|
96
117
|
end
|
97
118
|
end
|
@@ -117,15 +117,19 @@ end
|
|
117
117
|
class UniqueService < Services::Base
|
118
118
|
def call(on_error, sleep)
|
119
119
|
check_uniqueness on_error: on_error
|
120
|
+
do_work
|
120
121
|
sleep 0.5 if sleep
|
121
122
|
end
|
123
|
+
def do_work; end
|
122
124
|
end
|
123
125
|
|
124
126
|
class UniqueWithCustomArgsService < Services::Base
|
125
127
|
def call(uniqueness_arg1, uniqueness_arg2, ignore_arg, on_error, sleep)
|
126
128
|
check_uniqueness uniqueness_arg1, uniqueness_arg2, on_error: on_error
|
129
|
+
do_work
|
127
130
|
sleep 0.5 if sleep
|
128
131
|
end
|
132
|
+
def do_work; end
|
129
133
|
end
|
130
134
|
|
131
135
|
class UniqueMultipleService < Services::Base
|
@@ -133,14 +137,18 @@ class UniqueMultipleService < Services::Base
|
|
133
137
|
args.each do |arg|
|
134
138
|
check_uniqueness arg, on_error: on_error
|
135
139
|
end
|
140
|
+
do_work
|
136
141
|
sleep 0.5 if sleep
|
137
142
|
end
|
143
|
+
def do_work; end
|
138
144
|
end
|
139
145
|
|
140
146
|
class NonUniqueService < Services::Base
|
141
147
|
def call(on_error, sleep)
|
148
|
+
do_work
|
142
149
|
sleep 0.5 if sleep
|
143
150
|
end
|
151
|
+
def do_work; end
|
144
152
|
end
|
145
153
|
|
146
154
|
class NestedExceptionService < Services::Base
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: services
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Manuel Meurer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-11-
|
11
|
+
date: 2014-11-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|