symphony 0.12.3 → 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|