smith 0.5.13.1 → 0.6.1

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.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/bin/agency +19 -7
  3. data/bin/pry-smith +11 -0
  4. data/bin/smithctl +19 -21
  5. data/lib/smith/acl_compiler.rb +101 -56
  6. data/lib/smith/acl_parser.rb +75 -0
  7. data/lib/smith/agent.rb +28 -43
  8. data/lib/smith/agent_cache.rb +43 -17
  9. data/lib/smith/agent_monitoring.rb +1 -1
  10. data/lib/smith/agent_process.rb +148 -53
  11. data/lib/smith/application/agency.rb +44 -54
  12. data/lib/smith/bootstrap.rb +31 -17
  13. data/lib/smith/cache.rb +4 -0
  14. data/lib/smith/command.rb +1 -3
  15. data/lib/smith/commands/agency/kill.rb +22 -5
  16. data/lib/smith/commands/agency/list.rb +9 -4
  17. data/lib/smith/commands/agency/logger.rb +25 -12
  18. data/lib/smith/commands/agency/object_count.rb +19 -8
  19. data/lib/smith/commands/agency/start.rb +7 -10
  20. data/lib/smith/commands/agency/stop.rb +30 -12
  21. data/lib/smith/commands/common.rb +1 -1
  22. data/lib/smith/commands/smithctl/acl.rb +6 -3
  23. data/lib/smith/commands/smithctl/dump.rb +79 -0
  24. data/lib/smith/commands/smithctl/firehose.rb +2 -1
  25. data/lib/smith/commands/smithctl/push.rb +27 -12
  26. data/lib/smith/commands/smithctl/status.rb +27 -0
  27. data/lib/smith/config.rb +140 -28
  28. data/lib/smith/daemon.rb +16 -3
  29. data/lib/smith/exceptions.rb +6 -3
  30. data/lib/smith/logger.rb +12 -24
  31. data/lib/smith/messaging/acl/agent_keepalive.proto +2 -2
  32. data/lib/smith/messaging/acl/agent_lifecycle.proto +15 -9
  33. data/lib/smith/messaging/acl/agent_stats.proto +6 -5
  34. data/lib/smith/messaging/acl/default.rb +2 -7
  35. data/lib/smith/messaging/acl_type_cache.rb +77 -0
  36. data/lib/smith/messaging/factory.rb +29 -0
  37. data/lib/smith/messaging/queue.rb +12 -10
  38. data/lib/smith/messaging/queue_definition.rb +21 -4
  39. data/lib/smith/messaging/receiver.rb +55 -62
  40. data/lib/smith/messaging/requeue.rb +1 -5
  41. data/lib/smith/messaging/sender.rb +48 -43
  42. data/lib/smith/messaging/util.rb +0 -10
  43. data/lib/smith/queue_definitions.rb +7 -4
  44. data/lib/smith/version.rb +1 -1
  45. data/lib/smith.rb +57 -56
  46. metadata +77 -128
  47. data/lib/smith/messaging/payload.rb +0 -100
@@ -7,20 +7,19 @@ module Smith
7
7
  include Logger
8
8
  include Util
9
9
 
10
- attr_accessor :queue_name
10
+ def initialize(queue_def, opts={}, &blk)
11
11
 
12
- def initialize(queue_definition, opts={}, &blk)
12
+ # This is for backward compatibility.
13
+ @queue_def = queue_def.is_a?(QueueDefinition) ? queue_def : QueueDefinition.new(queue_def, opts)
13
14
 
14
- @queue_name, opts = get_queue_name_and_options(queue_definition, opts)
15
+ @acl_type_cache = AclTypeCache.instance
15
16
 
16
17
  @reply_container = {}
17
18
 
18
- normalised_queue_name = normalise(@queue_name)
19
+ prefetch = option_or_default(@queue_def.options, :prefetch, Smith.config.agent.prefetch)
19
20
 
20
- prefetch = option_or_default(opts, :prefetch, Smith.config.agent.prefetch)
21
-
22
- @options = AmqpOptions.new(opts)
23
- @options.routing_key = normalised_queue_name
21
+ @options = AmqpOptions.new(@queue_def.options)
22
+ @options.routing_key = @queue_def.normalise
24
23
 
25
24
  @message_counts = Hash.new(0)
26
25
 
@@ -30,15 +29,15 @@ module Smith
30
29
 
31
30
  open_channel(:prefetch => prefetch) do |channel|
32
31
  @channel_completion.succeed(channel)
33
- channel.direct(normalised_queue_name, @options.exchange) do |exchange|
32
+ channel.direct(@queue_def.normalise, @options.exchange) do |exchange|
34
33
 
35
34
  exchange.on_return do |basic_return,metadata,payload|
36
- logger.error { "#{ACL::Payload.decode(payload.clone, metadata.type)} returned! Exchange: #{reply_code.exchange}, reply_code = #{basic_return.reply_code}, reply_text = #{basic_return.reply_text}" }
35
+ logger.error { "#{@acl_type_cache[metadata.type].new.parse_from_string} returned! Exchange: #{reply_code.exchange}, reply_code: #{basic_return.reply_code}, reply_text: #{basic_return.reply_text}" }
37
36
  logger.error { "Properties: #{metadata.properties}" }
38
37
  end
39
38
 
40
- channel.queue(normalised_queue_name, @options.queue) do |queue|
41
- queue.bind(exchange, :routing_key => normalised_queue_name)
39
+ channel.queue(@queue_def.normalise, @options.queue) do |queue|
40
+ queue.bind(exchange, :routing_key => @queue_def.normalise)
42
41
 
43
42
  @queue_completion.succeed(queue)
44
43
  @exchange_completion.succeed(exchange)
@@ -64,47 +63,46 @@ module Smith
64
63
  message_id = random
65
64
  logger.verbose { "message_id: #{message_id}" }
66
65
 
67
- #### !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ####
68
- #### TODO if there is a timeout delete ####
69
- #the proc from the @reply_container. ####
70
- #### !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ####
66
+ #### !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ####
67
+ #### TODO if there is a timeout delete ####
68
+ #### the proc from the @reply_container. ####
69
+ #### !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ####
71
70
  @reply_container[message_id] = {:reply_proc => @reply_proc, :timeout => @timeout.clone.tap {|t| t.set_timeout(message_id) }}
72
- _publish(ACL::Payload.new(payload), @options.publish(opts, {:reply_to => reply_queue.queue_name, :message_id => message_id}))
71
+ _publish(payload, @options.publish(opts, {:reply_to => reply_queue.queue_name, :message_id => message_id}))
73
72
  end
74
73
  else
75
- _publish(ACL::Payload.new(payload), @options.publish(opts), &blk)
74
+ _publish(payload, @options.publish(opts), &blk)
76
75
  end
77
76
  end
78
77
 
79
- def on_timeout(timeout, &blk)
80
- @timeout = Timeout.new(timeout, &blk)
78
+ def on_timeout(timeout=nil, &blk)
79
+ @timeout = Timeout.new(timeout || Smith.config.smith.timeout, &blk)
81
80
  end
82
81
 
83
82
  # Set up a listener that will receive replies from the published
84
83
  # messages. You must publish with intent to reply -- tee he.
85
84
  #
86
85
  # If you pass in a queue_name the same queue name will get used for every
87
- # reply. This means that there are no create and teardown costs for every
88
- # for every message. If no queue_name is given a random one will be
89
- # assigned.
86
+ # reply. This means that there are no create and teardown costs for each
87
+ # message. If no queue_name is given a random one will be assigned.
90
88
  def on_reply(opts={}, &blk)
91
89
  @reply_proc = blk
92
- @timeout ||= Timeout.new(Smith.config.agency.timeout, :queue_name => @queue_name)
93
- reply_queue_name = opts.delete(:reply_queue_name) || random
94
90
 
95
- options = {:auto_delete => false, :auto_ack => false}.merge(opts)
91
+ @timeout ||= Timeout.new(Smith.config.smith.timeout, :queue_name => @queue_def.denormalise)
96
92
 
97
- logger.debug { "reply queue: #{reply_queue_name}" }
93
+ queue_def = QueueDefinition.new(opts.delete(:reply_queue_name) || "#{@queue_def.denormalise}.reply", opts.merge(:auto_delete => true, :durable => false))
94
+ logger.debug { "reply queue: #{queue_def.denormalise}" }
98
95
 
99
96
  @reply_queue_completion ||= EM::Completion.new.tap do |completion|
100
- Receiver.new(reply_queue_name, options) do |queue|
97
+ Receiver.new(queue_def) do |queue|
101
98
  queue.subscribe do |payload, receiver|
102
99
  @reply_container.delete(receiver.correlation_id).tap do |reply|
103
100
  if reply
104
101
  reply[:timeout].cancel_timeout
105
102
  reply[:reply_proc].call(payload, receiver)
106
103
  else
107
- logger.error { "Reply message has no correlation_id: #{reply.inspect}" }
104
+ receiver.ack if opts[:auto_ack]
105
+ logger.error { "No reply block for correlation_id: #{receiver.correlation_id}. This is probably a timed out message. Message: #{payload.to_json}" }
108
106
  end
109
107
  end
110
108
  end
@@ -156,7 +154,7 @@ module Smith
156
154
  end
157
155
 
158
156
  def counter
159
- @message_counts[@queue_name]
157
+ @message_counts[@queue_def.denormalise]
160
158
  end
161
159
 
162
160
  # Define a channel error handler.
@@ -166,30 +164,33 @@ module Smith
166
164
  end
167
165
  end
168
166
 
167
+ def queue_name
168
+ @queue_def.denormalise
169
+ end
170
+
169
171
  private
170
172
 
171
173
  def _publish(message, opts, &blk)
172
- logger.verbose { "Publishing to: [queue]: #{@queue_name}. [options]: #{opts}" }
173
- logger.verbose { "Payload content: [queue]: #{@queue_name}, [metadata type]: #{message._type}, [message]: #{message.inspect}" }
174
- if message.initialized?
175
- increment_counter
176
- type = (message.respond_to?(:_type)) ? message._type : message.type
177
- @exchange_completion.completion do |exchange|
178
- exchange.publish(message.encode, opts.merge(:type => type), &blk)
179
- end
180
- else
181
- raise IncompletePayload, "Message is incomplete: #{message.to_s}"
174
+ logger.verbose { "Publishing to: [queue]: #{@queue_def.denormalise}. [options]: #{opts}" }
175
+ logger.verbose { "ACL content: [queue]: #{@queue_def.denormalise}, [metadata type]: #{message.class}, [message]: #{message.inspect}" }
176
+
177
+ increment_counter
178
+
179
+ type = @acl_type_cache.get_by_type(message.class)
180
+
181
+ @exchange_completion.completion do |exchange|
182
+ exchange.publish(message.to_s, opts.merge(:type => type), &blk)
182
183
  end
183
184
  end
184
185
 
185
186
  def increment_counter(value=1)
186
- @message_counts[@queue_name] += value
187
+ @message_counts[@queue_def.denormalise] += value
187
188
  end
188
189
  end
189
190
 
190
191
  class Timeout
191
192
  def initialize(timeout, opts={}, &blk)
192
- @timeout_proc = blk || proc { |message_id| raise ACLTimeoutError, "Message not received within the timeout period#{(message_id) ? ": #{message_id}" : ""}" }
193
+ @timeout_proc = blk || proc { |message_id| raise MessageTimeoutError, "Message not received within the timeout period#{(message_id) ? ": #{message_id}" : ""}" }
193
194
  @timeout_duration = timeout
194
195
  end
195
196
 
@@ -198,7 +199,7 @@ module Smith
198
199
  cancel_timeout
199
200
  if @timeout_duration
200
201
  @timeout = EventMachine::Timer.new(@timeout_duration) do
201
- @timeout_proc.call(message_id)
202
+ @timeout_proc.call(message_id, @timeout_duration)
202
203
  end
203
204
  else
204
205
  raise ArgumentError, "on_timeout not set."
@@ -212,6 +213,10 @@ module Smith
212
213
  def cancel_timeout
213
214
  @timeout.cancel if @timeout
214
215
  end
216
+
217
+ def to_s
218
+ "<Smith::Timeout: #{@timeout_duration}>"
219
+ end
215
220
  end
216
221
  end
217
222
  end
@@ -41,20 +41,10 @@ module Smith
41
41
  end
42
42
  end
43
43
 
44
- def normalise(name)
45
- "#{Smith.config.smith.namespace}.#{name}"
46
- end
47
-
48
44
  def random(prefix = '', suffix = '')
49
45
  "#{prefix}#{SecureRandom.hex(8)}#{suffix}"
50
46
  end
51
47
 
52
- # Return the queue name and options based on whether the
53
- # queue_definition is of type QueueDefinition.
54
- def get_queue_name_and_options(queue_definition, opts)
55
- (queue_definition.is_a?(QueueDefinition)) ? queue_definition.to_a : [queue_definition, opts]
56
- end
57
-
58
48
  def option_or_default(options, key, default, &blk)
59
49
  if options.is_a?(Hash)
60
50
  if options.key?(key)
@@ -2,10 +2,13 @@
2
2
 
3
3
  module Smith
4
4
  module QueueDefinitions
5
- Agency_control = QueueDefinition.new("#{Smith.hostname}.agency.control", :auto_delete => false, :durable => false, :persistent => false, :strict => true)
6
- Agent_keepalive = QueueDefinition.new("#{Smith.hostname}.agent.keepalive", :auto_delete => false, :durable => false)
7
- Agent_lifecycle = QueueDefinition.new("#{Smith.hostname}.agent.lifecycle", :auto_delete => false, :durable => false)
5
+ Agency_control = QueueDefinition.new("#{Smith.hostname}.agency.control", :auto_ack => false, :durable => true, :persistent => false, :strict => true)
6
+ Agent_keepalive = QueueDefinition.new("#{Smith.hostname}.agent.keepalive", :auto_delete => false, :durable => true)
7
+ Agent_lifecycle = QueueDefinition.new("#{Smith.hostname}.agent.lifecycle", :auto_delete => false, :durable => true)
8
8
 
9
- Agent_stats = QueueDefinition.new('agent.stats', :durable => false, :auto_delete => false)
9
+ Agent_stats = QueueDefinition.new('agent.stats', :durable => true, :auto_delete => true)
10
+
11
+ # Something tells me that I've crossed line with this.
12
+ Agent_control = ->(uuid) { QueueDefinition.new("agent.control.#{uuid}", :durable => false, :auto_delete => true) }
10
13
  end
11
14
  end
data/lib/smith/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Smith
2
- VERSION = "0.5.13.1"
2
+ VERSION = "0.6.1"
3
3
  end
data/lib/smith.rb CHANGED
@@ -4,11 +4,9 @@ require 'tmpdir'
4
4
  require "socket"
5
5
  require 'logging'
6
6
  require 'pathname'
7
+ require 'protobuf'
7
8
  require 'fileutils'
8
- require 'optimism'
9
- require 'dm-core'
10
9
  require 'securerandom'
11
- require 'dm-yaml-adapter'
12
10
  require 'extlib/string'
13
11
  require 'extlib/inflection'
14
12
  require 'daemons/pidfile'
@@ -35,8 +33,12 @@ module Smith
35
33
  Config.get
36
34
  end
37
35
 
36
+ def config_path
37
+ Smith.config.path
38
+ end
39
+
38
40
  def root_path
39
- Pathname.new(File.dirname(__FILE__) + '/..').expand_path
41
+ Pathname.new(__FILE__).dirname.parent.expand_path
40
42
  end
41
43
 
42
44
  def agent_paths
@@ -52,19 +54,14 @@ module Smith
52
54
  path_to_pathnames(config.agency.acl_path)
53
55
  end
54
56
 
55
- # Return the acl cache path. If it's not specified in the config
56
- # generate a temporary path.
57
+ def cache_path
58
+ Pathname.new(config.agency.cache_path).expand_path
59
+ end
60
+
61
+ # Return the acl cache path.
57
62
  def acl_cache_path
58
- @acl_cache_path ||= if Smith.config.agency._has_key?(:acl_cache_path)
59
- Pathname.new(Smith.config.agency.acl_cache_path).tap { |path| check_path(path) }
60
- else
61
- cache_dir = Pathname.new(ENV['HOME']).join('.smith').join('acl')
62
- if cache_dir.exist?
63
- cache_dir
64
- else
65
- FileUtils.mkdir_p(cache_dir)
66
- cache_dir
67
- end
63
+ @acl_cache_path = Pathname.new(Smith.config.agency.acl_cache_path).tap do |path|
64
+ check_path(path, true)
68
65
  end
69
66
  end
70
67
 
@@ -73,26 +70,24 @@ module Smith
73
70
  @compiler.compile
74
71
  end
75
72
 
76
- # Load all acls. This fucking horrible but for the time
77
- # being it's how it's going to be. This will really start
78
- # to be a problem when there are a lot of acls.
79
- def load_acls
80
- Pathname.glob(Smith.acl_cache_path.join("*.pb.rb"))do |acl_file|
81
- logger.verbose { "Loading acl file: #{acl_file}" }
82
- require acl_file
83
- end
84
- end
85
-
86
73
  def running?
87
74
  EM.reactor_running?
88
75
  end
89
76
 
90
77
  def start(opts={}, &block)
91
- EM.epoll if EM.epoll?
92
- EM.kqueue if EM.kqueue?
93
- EM.set_descriptor_table_size(opts[:fdsize] || 1024)
78
+ if EM.epoll? && Smith.config.eventmachine.epoll
79
+ logger.debug { "Using epoll for I/O event notification." }
80
+ EM.epoll
81
+ end
82
+
83
+ if EM.kqueue? && Smith.config.eventmachine.kqueue
84
+ logger.debug { "Using kqueue for I/O event notification." }
85
+ EM.kqueue
86
+ end
87
+
88
+ EM.set_descriptor_table_size(Smith.config.eventmachine.file_descriptors)
94
89
 
95
- connection_settings = config.amqp.broker.to_hash.merge(
90
+ connection_settings = config.amqp.broker.merge(
96
91
  :on_tcp_connection_failure => method(:tcp_connection_failure_handler),
97
92
  :on_possible_authentication_failure => method(:authentication_failure_handler))
98
93
 
@@ -102,7 +97,7 @@ module Smith
102
97
  connection.on_connection do
103
98
  broker = connection.broker.properties
104
99
  endpoint = connection.broker_endpoint
105
- logger.debug { "Connected to: AMQP Broker: #{endpoint}, (#{broker['product']}/v#{broker['version']})" } unless opts[:quiet]
100
+ logger.info { "Connected to: AMQP Broker: #{endpoint}, (#{broker['product']}/v#{broker['version']})" } unless opts[:quiet]
106
101
  end
107
102
 
108
103
  connection.on_tcp_connection_loss do |connection, settings|
@@ -195,32 +190,38 @@ module Smith
195
190
  end
196
191
  end
197
192
 
198
- def check_path(path)
199
- logger.error("Path does not exist: #{path}") unless path.exist?
193
+ def check_path(path, create=false)
194
+ unless path.exist?
195
+ error_message = "Path does not exist: #{path}"
196
+ if create
197
+ logger.info { "Path does not exist: #{path}. Creating" }
198
+ path.mkpath
199
+ else
200
+ logger.warn { "Path does not exist: #{path}" }
201
+ end
202
+ end
200
203
  end
204
+
201
205
  end
202
206
  end
203
207
 
204
- require_relative 'smith/amqp_errors'
205
- require_relative 'smith/object_count'
206
- require_relative 'smith/cache'
207
- require_relative 'smith/agent_cache'
208
- require_relative 'smith/agent_process'
209
- require_relative 'smith/agent_monitoring'
210
- require_relative 'smith/command'
211
- require_relative 'smith/command_base'
212
- require_relative 'smith/exceptions'
213
- require_relative 'smith/object_count'
214
- require_relative 'smith/version'
215
-
216
- require_relative 'smith/messaging/queue_definition'
217
- require_relative 'smith/messaging/amqp_options'
218
- require_relative 'smith/messaging/queue_factory'
219
- require_relative 'smith/messaging/acl/default'
220
- require_relative 'smith/messaging/payload'
221
- require_relative 'smith/messaging/util'
222
- require_relative 'smith/messaging/responder'
223
- require_relative 'smith/messaging/receiver'
224
- require_relative 'smith/messaging/sender'
225
-
226
- require_relative 'smith/queue_definitions'
208
+ require 'smith/amqp_errors'
209
+ require 'smith/object_count'
210
+ require 'smith/cache'
211
+ require 'smith/exceptions'
212
+ require 'smith/object_count'
213
+ require 'smith/version'
214
+ require 'smith/acl_parser'
215
+
216
+ require 'smith/messaging/acl_type_cache'
217
+ require 'smith/messaging/queue_definition'
218
+ require 'smith/messaging/amqp_options'
219
+ require 'smith/messaging/queue_factory'
220
+ require 'smith/messaging/factory'
221
+ require 'smith/messaging/acl/default'
222
+ require 'smith/messaging/util'
223
+ require 'smith/messaging/responder'
224
+ require 'smith/messaging/receiver'
225
+ require 'smith/messaging/sender'
226
+
227
+ require 'smith/queue_definitions'