symphony 0.12.3 → 0.14.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
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/History.rdoc +39 -0
- data/Manifest.txt +1 -2
- data/{README.rdoc → README.md} +28 -14
- data/Rakefile +5 -74
- data/USAGE.rdoc +27 -13
- data/lib/symphony.rb +1 -1
- data/lib/symphony/daemon.rb +2 -2
- data/lib/symphony/metrics.rb +1 -1
- data/lib/symphony/mixins.rb +1 -1
- data/lib/symphony/queue.rb +18 -11
- data/lib/symphony/routing.rb +1 -1
- data/lib/symphony/signal_handling.rb +1 -1
- data/lib/symphony/statistics.rb +1 -1
- data/lib/symphony/task.rb +26 -3
- data/lib/symphony/task_group.rb +1 -1
- data/lib/symphony/task_group/longlived.rb +22 -12
- data/lib/symphony/task_group/oneshot.rb +1 -1
- data/spec/helpers.rb +23 -7
- data/spec/symphony/daemon_spec.rb +2 -2
- data/spec/symphony/mixins_spec.rb +6 -2
- data/spec/symphony/queue_spec.rb +28 -2
- data/spec/symphony/routing_spec.rb +1 -1
- data/spec/symphony/statistics_spec.rb +1 -1
- data/spec/symphony/task_group/longlived_spec.rb +20 -16
- data/spec/symphony/task_group/oneshot_spec.rb +1 -1
- data/spec/symphony/task_group_spec.rb +1 -1
- data/spec/symphony/task_spec.rb +19 -2
- data/spec/symphony_spec.rb +1 -1
- metadata +50 -136
- metadata.gz.sig +0 -0
- data/ChangeLog +0 -1162
data/lib/symphony/statistics.rb
CHANGED
data/lib/symphony/task.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# -*- ruby -*-
|
2
|
-
#
|
2
|
+
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'set'
|
5
5
|
require 'sysexits'
|
@@ -7,7 +7,7 @@ require 'pluggability'
|
|
7
7
|
require 'loggability'
|
8
8
|
|
9
9
|
require 'msgpack'
|
10
|
-
require '
|
10
|
+
require 'json'
|
11
11
|
require 'yaml'
|
12
12
|
|
13
13
|
require 'symphony' unless defined?( Symphony )
|
@@ -47,6 +47,15 @@ class Symphony::Task
|
|
47
47
|
plugin_prefixes 'symphony/tasks'
|
48
48
|
|
49
49
|
|
50
|
+
@queue = nil
|
51
|
+
@acknowledge = true
|
52
|
+
@routing_keys = Set.new
|
53
|
+
@prefetch = 10
|
54
|
+
@persistent = false
|
55
|
+
@always_rebind = false
|
56
|
+
@queue_type = nil
|
57
|
+
|
58
|
+
|
50
59
|
### Create a new Task object and listen for work. Exits with the code returned
|
51
60
|
### by #start when it's done.
|
52
61
|
def self::run( exit_on_idle=false )
|
@@ -79,11 +88,15 @@ class Symphony::Task
|
|
79
88
|
def self::inherited( subclass )
|
80
89
|
super
|
81
90
|
|
91
|
+
subclass.instance_variable_set( :@queue, nil )
|
92
|
+
subclass.instance_variable_set( :@queue_type, nil )
|
93
|
+
subclass.instance_variable_set( :@always_rebind, false )
|
82
94
|
subclass.instance_variable_set( :@routing_keys, Set.new )
|
83
95
|
subclass.instance_variable_set( :@acknowledge, true )
|
84
96
|
subclass.instance_variable_set( :@work_model, :longlived )
|
85
97
|
subclass.instance_variable_set( :@prefetch, 10 )
|
86
98
|
subclass.instance_variable_set( :@timeout_action, :reject )
|
99
|
+
subclass.instance_variable_set( :@timeout, nil )
|
87
100
|
subclass.instance_variable_set( :@persistent, false )
|
88
101
|
subclass.instance_variable_set( :@idle_timeout, DEFAULT_IDLE_TIMEOUT )
|
89
102
|
end
|
@@ -132,6 +145,16 @@ class Symphony::Task
|
|
132
145
|
end
|
133
146
|
|
134
147
|
|
148
|
+
### Specify an x-queue-type for the underlying queue
|
149
|
+
def self::queue_type( type=nil )
|
150
|
+
if type
|
151
|
+
@queue_type = type
|
152
|
+
end
|
153
|
+
|
154
|
+
return @queue_type
|
155
|
+
end
|
156
|
+
|
157
|
+
|
135
158
|
### Set up one or more topic key patterns to use when binding the Task's queue
|
136
159
|
### to the exchange.
|
137
160
|
def self::subscribe_to( *routing_keys )
|
@@ -438,7 +461,7 @@ class Symphony::Task
|
|
438
461
|
when 'application/x-msgpack'
|
439
462
|
MessagePack.unpack( payload )
|
440
463
|
when 'application/json', 'text/javascript'
|
441
|
-
|
464
|
+
JSON.parse( payload )
|
442
465
|
when 'application/x-yaml', 'text/x-yaml'
|
443
466
|
YAML.load( payload )
|
444
467
|
else
|
data/lib/symphony/task_group.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# -*- ruby -*-
|
2
|
-
#
|
2
|
+
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'set'
|
5
5
|
require 'symphony/task_group' unless defined?( Symphony::TaskGroup )
|
@@ -35,8 +35,9 @@ class Symphony::TaskGroup::LongLived < Symphony::TaskGroup
|
|
35
35
|
return [ pid ]
|
36
36
|
end
|
37
37
|
|
38
|
-
|
39
|
-
|
38
|
+
return nil
|
39
|
+
rescue Timeout::Error => err
|
40
|
+
self.log.warn "%p while adjusting workers: %s" % [ err.class, err.message ]
|
40
41
|
return nil
|
41
42
|
end
|
42
43
|
|
@@ -44,11 +45,10 @@ class Symphony::TaskGroup::LongLived < Symphony::TaskGroup
|
|
44
45
|
### Return +true+ if the task group should scale up by one.
|
45
46
|
def needs_a_worker?
|
46
47
|
return true if self.workers.empty?
|
47
|
-
return false
|
48
|
-
|
48
|
+
queue = self.get_message_counting_queue or return false
|
49
49
|
|
50
50
|
# Calculate the number of workers across the whole broker
|
51
|
-
if ( cc =
|
51
|
+
if ( cc = queue.consumer_count ) >= self.max_workers
|
52
52
|
self.log.debug "%p: Already at max workers (%d)" % [ self.task_class, self.max_workers ]
|
53
53
|
return false
|
54
54
|
else
|
@@ -62,9 +62,10 @@ class Symphony::TaskGroup::LongLived < Symphony::TaskGroup
|
|
62
62
|
|
63
63
|
### Add the current number of workers to the samples.
|
64
64
|
def sample_queue_status
|
65
|
-
return
|
65
|
+
return if self.workers.empty?
|
66
66
|
|
67
|
-
|
67
|
+
queue = self.get_message_counting_queue or return
|
68
|
+
count = queue.message_count
|
68
69
|
self.add_sample( count )
|
69
70
|
end
|
70
71
|
|
@@ -82,11 +83,20 @@ class Symphony::TaskGroup::LongLived < Symphony::TaskGroup
|
|
82
83
|
### Get a queue for counting the number of messages in the queue for this
|
83
84
|
### worker.
|
84
85
|
def get_message_counting_queue
|
85
|
-
|
86
|
-
|
86
|
+
@queue ||= begin
|
87
|
+
self.log.debug "Creating the message-counting queue."
|
88
|
+
channel = Symphony::Queue.amqp_channel
|
89
|
+
channel.queue( self.task_class.queue_name, passive: true, prefetch: 0 )
|
90
|
+
end
|
91
|
+
|
92
|
+
unless @queue.channel.open?
|
93
|
+
self.log.info "Message-counting queue's channel was closed: resetting."
|
94
|
+
Symphony::Queue.reset
|
95
|
+
@queue = nil
|
96
|
+
end
|
87
97
|
|
88
|
-
return queue
|
89
|
-
rescue Bunny::NotFound
|
98
|
+
return @queue
|
99
|
+
rescue Bunny::NotFound, Bunny::ChannelAlreadyClosed
|
90
100
|
self.log.info "Child hasn't created the queue yet; deferring"
|
91
101
|
Symphony::Queue.reset
|
92
102
|
|
data/spec/helpers.rb
CHANGED
@@ -48,6 +48,10 @@ module Symphony::SpecHelpers
|
|
48
48
|
def initialize
|
49
49
|
@queue = nil
|
50
50
|
@exchange = nil
|
51
|
+
@open = true
|
52
|
+
end
|
53
|
+
def open?
|
54
|
+
return @open
|
51
55
|
end
|
52
56
|
def queue( name, opts={} )
|
53
57
|
return @queue ||= DummySession::Queue.new( self )
|
@@ -60,7 +64,9 @@ module Symphony::SpecHelpers
|
|
60
64
|
def number
|
61
65
|
return 1
|
62
66
|
end
|
63
|
-
def close
|
67
|
+
def close
|
68
|
+
@open = false
|
69
|
+
end
|
64
70
|
end
|
65
71
|
|
66
72
|
class Exchange
|
@@ -86,14 +92,24 @@ end
|
|
86
92
|
|
87
93
|
### Mock with RSpec
|
88
94
|
RSpec.configure do |config|
|
89
|
-
config.
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
+
config.expect_with :rspec do |expectations|
|
96
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
97
|
+
expectations.syntax = :expect
|
98
|
+
end
|
99
|
+
|
100
|
+
config.mock_with :rspec do |mocks|
|
101
|
+
mocks.verify_partial_doubles = true
|
95
102
|
end
|
96
103
|
|
104
|
+
config.run_all_when_everything_filtered = true
|
105
|
+
config.filter_run_when_matching :focus
|
106
|
+
config.order = :random
|
107
|
+
config.example_status_persistence_file_path = 'spec/.state'
|
108
|
+
config.disable_monkey_patching!
|
109
|
+
config.warnings = true
|
110
|
+
config.profile_examples = 5
|
111
|
+
|
112
|
+
|
97
113
|
config.include( Loggability::SpecHelpers )
|
98
114
|
config.include( Symphony::SpecHelpers )
|
99
115
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# -*- ruby -*-
|
2
|
-
#
|
2
|
+
# frozen_string_literal: true
|
3
3
|
# vim: set noet nosta sw=4 ts=4 :
|
4
4
|
|
5
5
|
|
@@ -19,7 +19,7 @@ class Test3Task < Symphony::Task
|
|
19
19
|
end
|
20
20
|
|
21
21
|
|
22
|
-
describe Symphony::Daemon do
|
22
|
+
RSpec.describe Symphony::Daemon do
|
23
23
|
|
24
24
|
before( :all ) do
|
25
25
|
@pids = ( 200..65534 ).cycle
|
@@ -5,12 +5,16 @@ require_relative '../helpers'
|
|
5
5
|
require 'symphony/mixins'
|
6
6
|
|
7
7
|
|
8
|
-
describe Symphony, 'mixins' do
|
8
|
+
RSpec.describe Symphony, 'mixins' do
|
9
9
|
|
10
10
|
describe Symphony::MethodUtilities, 'used to extend a class' do
|
11
11
|
|
12
12
|
let!( :extended_class ) do
|
13
|
-
klass = Class.new
|
13
|
+
klass = Class.new do
|
14
|
+
def initialize
|
15
|
+
@foo = nil
|
16
|
+
end
|
17
|
+
end
|
14
18
|
klass.extend( Symphony::MethodUtilities )
|
15
19
|
klass
|
16
20
|
end
|
data/spec/symphony/queue_spec.rb
CHANGED
@@ -4,7 +4,7 @@ require_relative '../helpers'
|
|
4
4
|
|
5
5
|
require 'symphony/queue'
|
6
6
|
|
7
|
-
describe Symphony::Queue do
|
7
|
+
RSpec.describe Symphony::Queue do
|
8
8
|
|
9
9
|
|
10
10
|
before( :each ) do
|
@@ -128,7 +128,33 @@ describe Symphony::Queue do
|
|
128
128
|
expect( new_channel ).to receive( :prefetch ).
|
129
129
|
with( Symphony::Queue::DEFAULT_PREFETCH )
|
130
130
|
expect( new_channel ).to receive( :queue ).
|
131
|
-
with( queue.name, auto_delete: true ).
|
131
|
+
with( queue.name, auto_delete: true, arguments: {} ).
|
132
|
+
and_return( amqp_queue )
|
133
|
+
expect( amqp_queue ).to receive( :bind ).
|
134
|
+
with( described_class.amqp_exchange, routing_key: 'floppy.rabbit.#' )
|
135
|
+
|
136
|
+
expect( queue.create_amqp_queue ).to be( amqp_queue )
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
it "can declare the queue's x-queue-type" do
|
141
|
+
testing_task_class.subscribe_to( 'floppy.rabbit.#' )
|
142
|
+
testing_task_class.queue_type( 'classic' )
|
143
|
+
expect( described_class.amqp_channel ).to receive( :queue ).
|
144
|
+
with( queue.name, passive: true ).
|
145
|
+
and_raise( Bunny::NotFound.new("no such queue", described_class.amqp_channel, true) )
|
146
|
+
expect( described_class.amqp_channel ).to receive( :open? ).
|
147
|
+
and_return( false )
|
148
|
+
|
149
|
+
# Channel is reset after queue creation fails
|
150
|
+
new_channel = double( "New AMQP channel" )
|
151
|
+
amqp_queue = double( "AMQP queue" )
|
152
|
+
allow( described_class.amqp_session ).to receive( :create_channel ).
|
153
|
+
and_return( new_channel )
|
154
|
+
expect( new_channel ).to receive( :prefetch ).
|
155
|
+
with( Symphony::Queue::DEFAULT_PREFETCH )
|
156
|
+
expect( new_channel ).to receive( :queue ).
|
157
|
+
with( queue.name, auto_delete: true, arguments: { 'x-queue-type' => 'classic' } ).
|
132
158
|
and_return( amqp_queue )
|
133
159
|
expect( amqp_queue ).to receive( :bind ).
|
134
160
|
with( described_class.amqp_exchange, routing_key: 'floppy.rabbit.#' )
|
@@ -4,7 +4,7 @@ require_relative '../../helpers'
|
|
4
4
|
|
5
5
|
require 'symphony/task_group/longlived'
|
6
6
|
|
7
|
-
describe Symphony::TaskGroup::LongLived do
|
7
|
+
RSpec.describe Symphony::TaskGroup::LongLived do
|
8
8
|
|
9
9
|
FIRST_PID = 414
|
10
10
|
|
@@ -23,6 +23,9 @@ describe Symphony::TaskGroup::LongLived do
|
|
23
23
|
def self::run
|
24
24
|
self.has_run = true
|
25
25
|
end
|
26
|
+
def self::name
|
27
|
+
return "TestTask"
|
28
|
+
end
|
26
29
|
end
|
27
30
|
end
|
28
31
|
|
@@ -75,8 +78,8 @@ describe Symphony::TaskGroup::LongLived do
|
|
75
78
|
|
76
79
|
allow( Process ).to receive( :setpgid )
|
77
80
|
|
78
|
-
channel = double( Bunny::Channel )
|
79
|
-
queue = double( Bunny::Queue )
|
81
|
+
channel = double( Bunny::Channel, open?: true )
|
82
|
+
queue = double( Bunny::Queue, channel: channel )
|
80
83
|
expect( Symphony::Queue ).to receive( :amqp_channel ).
|
81
84
|
and_return( channel )
|
82
85
|
expect( channel ).to receive( :queue ).
|
@@ -89,7 +92,8 @@ describe Symphony::TaskGroup::LongLived do
|
|
89
92
|
expect( queue ).to receive( :message_count ).and_return( *samples )
|
90
93
|
|
91
94
|
start = 1414002605
|
92
|
-
start.upto( start + samples.size
|
95
|
+
start.upto( start + samples.size ) do |time|
|
96
|
+
Loggability.logger.debug "Foom"
|
93
97
|
Timecop.freeze( time ) do
|
94
98
|
task_group.adjust_workers
|
95
99
|
end
|
@@ -106,8 +110,8 @@ describe Symphony::TaskGroup::LongLived do
|
|
106
110
|
|
107
111
|
allow( Process ).to receive( :setpgid )
|
108
112
|
|
109
|
-
channel = double( Bunny::Channel )
|
110
|
-
queue = double( Bunny::Queue )
|
113
|
+
channel = double( Bunny::Channel, open?: true )
|
114
|
+
queue = double( Bunny::Queue, channel: channel )
|
111
115
|
expect( Symphony::Queue ).to receive( :amqp_channel ).
|
112
116
|
and_return( channel )
|
113
117
|
expect( channel ).to receive( :queue ).
|
@@ -120,7 +124,7 @@ describe Symphony::TaskGroup::LongLived do
|
|
120
124
|
expect( queue ).to receive( :message_count ).and_return( *samples )
|
121
125
|
|
122
126
|
start = 1414002605
|
123
|
-
start.upto( start + samples.size
|
127
|
+
start.upto( start + samples.size ) do |time|
|
124
128
|
Timecop.freeze( time ) do
|
125
129
|
task_group.adjust_workers
|
126
130
|
end
|
@@ -136,8 +140,8 @@ describe Symphony::TaskGroup::LongLived do
|
|
136
140
|
|
137
141
|
allow( Process ).to receive( :setpgid )
|
138
142
|
|
139
|
-
channel = double( Bunny::Channel )
|
140
|
-
queue = double( Bunny::Queue )
|
143
|
+
channel = double( Bunny::Channel, open?: true )
|
144
|
+
queue = double( Bunny::Queue, channel: channel )
|
141
145
|
expect( Symphony::Queue ).to receive( :amqp_channel ).
|
142
146
|
and_return( channel )
|
143
147
|
expect( channel ).to receive( :queue ).
|
@@ -150,7 +154,7 @@ describe Symphony::TaskGroup::LongLived do
|
|
150
154
|
expect( queue ).to receive( :message_count ).and_return( *samples )
|
151
155
|
|
152
156
|
start = 1414002605
|
153
|
-
start.upto( start + samples.size
|
157
|
+
start.upto( start + samples.size ) do |time|
|
154
158
|
Timecop.freeze( time ) do
|
155
159
|
task_group.adjust_workers
|
156
160
|
end
|
@@ -166,8 +170,8 @@ describe Symphony::TaskGroup::LongLived do
|
|
166
170
|
|
167
171
|
allow( Process ).to receive( :setpgid )
|
168
172
|
|
169
|
-
channel = double( Bunny::Channel )
|
170
|
-
queue = double( Bunny::Queue )
|
173
|
+
channel = double( Bunny::Channel, open?: true )
|
174
|
+
queue = double( Bunny::Queue, channel: channel )
|
171
175
|
expect( Symphony::Queue ).to receive( :amqp_channel ).
|
172
176
|
and_return( channel )
|
173
177
|
expect( channel ).to receive( :queue ).
|
@@ -178,7 +182,7 @@ describe Symphony::TaskGroup::LongLived do
|
|
178
182
|
expect( queue ).to receive( :message_count ).and_return( *samples )
|
179
183
|
|
180
184
|
start = 1414002605
|
181
|
-
start.upto( start + samples.size
|
185
|
+
start.upto( start + samples.size ) do |time|
|
182
186
|
Timecop.freeze( time ) do
|
183
187
|
task_group.adjust_workers
|
184
188
|
end
|
@@ -194,8 +198,8 @@ describe Symphony::TaskGroup::LongLived do
|
|
194
198
|
|
195
199
|
allow( Process ).to receive( :setpgid )
|
196
200
|
|
197
|
-
channel = double( Bunny::Channel )
|
198
|
-
queue = double( Bunny::Queue )
|
201
|
+
channel = double( Bunny::Channel, open?: true )
|
202
|
+
queue = double( Bunny::Queue, channel: channel )
|
199
203
|
expect( Symphony::Queue ).to receive( :amqp_channel ).
|
200
204
|
and_return( channel )
|
201
205
|
expect( channel ).to receive( :queue ).
|
@@ -208,7 +212,7 @@ describe Symphony::TaskGroup::LongLived do
|
|
208
212
|
expect( queue ).to receive( :message_count ).and_return( *samples )
|
209
213
|
|
210
214
|
start = 1414002605
|
211
|
-
start.upto( start + samples.size
|
215
|
+
start.upto( start + samples.size ) do |time|
|
212
216
|
Timecop.freeze( time ) do
|
213
217
|
task_group.adjust_workers
|
214
218
|
end
|