serf 0.9.0 → 0.10.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.
- data/README.md +106 -305
- data/lib/serf/builder.rb +92 -128
- data/lib/serf/command.rb +36 -73
- data/lib/serf/errors/not_found.rb +8 -0
- data/lib/serf/middleware/girl_friday_async.rb +39 -0
- data/lib/serf/middleware/masherize.rb +25 -0
- data/lib/serf/middleware/uuid_tagger.rb +14 -8
- data/lib/serf/{util → routing}/regexp_matcher.rb +12 -6
- data/lib/serf/routing/route.rb +35 -0
- data/lib/serf/routing/route_set.rb +64 -0
- data/lib/serf/serfer.rb +56 -114
- data/lib/serf/util/error_handling.rb +5 -9
- data/lib/serf/util/options_extraction.rb +16 -5
- data/lib/serf/util/protected_call.rb +3 -2
- data/lib/serf/util/uuidable.rb +28 -4
- data/lib/serf/version.rb +1 -1
- data/serf.gemspec +8 -11
- metadata +35 -38
- data/lib/serf/more/command_worker.rb +0 -45
- data/lib/serf/routing/endpoint.rb +0 -49
- data/lib/serf/routing/registry.rb +0 -66
- data/lib/serf/runners/direct.rb +0 -52
- data/lib/serf/runners/event_machine.rb +0 -69
- data/lib/serf/runners/girl_friday.rb +0 -69
- data/lib/serf/runners/helper.rb +0 -23
- data/lib/serf/util/mash_factory.rb +0 -18
@@ -1,49 +0,0 @@
|
|
1
|
-
require 'serf/util/options_extraction'
|
2
|
-
|
3
|
-
module Serf
|
4
|
-
module Routing
|
5
|
-
|
6
|
-
##
|
7
|
-
# An endpoint is the description of how to build a Unit of Work
|
8
|
-
# for a given matched message. It builds an instance that
|
9
|
-
# responds to the `call` method that will actually execute the work.
|
10
|
-
# Units of work is built on every received message with the request,
|
11
|
-
# given arguments, options (merged with serf infrastructure options)
|
12
|
-
# and given block.
|
13
|
-
#
|
14
|
-
class Endpoint
|
15
|
-
include Serf::Util::OptionsExtraction
|
16
|
-
|
17
|
-
def initialize(connect, handler_factory, *args, &block)
|
18
|
-
# If we want to connect serf options, then we try to extract
|
19
|
-
# any possible options from the args list. If a hash exists at the
|
20
|
-
# end of the args list, then we'll merge into it. Otherwise a new hash
|
21
|
-
# will be added on.
|
22
|
-
extract_options! args if @connect = connect
|
23
|
-
|
24
|
-
@handler_factory= handler_factory
|
25
|
-
@args = args
|
26
|
-
@block = block
|
27
|
-
end
|
28
|
-
|
29
|
-
##
|
30
|
-
# Builds a Unit of Work object.
|
31
|
-
#
|
32
|
-
def build(env, serf_options={})
|
33
|
-
# If we are connecting serf options, then we need to pass these
|
34
|
-
# options on to the builder.
|
35
|
-
if @connect
|
36
|
-
@handler_factory.build(
|
37
|
-
env.dup,
|
38
|
-
*@args,
|
39
|
-
options.merge(serf_options),
|
40
|
-
&@block)
|
41
|
-
else
|
42
|
-
@handler_factory.build env.dup, *@args, &@block
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
end
|
47
|
-
|
48
|
-
end
|
49
|
-
end
|
@@ -1,66 +0,0 @@
|
|
1
|
-
require 'serf/util/regexp_matcher'
|
2
|
-
|
3
|
-
module Serf
|
4
|
-
module Routing
|
5
|
-
|
6
|
-
##
|
7
|
-
# EndpointRegistry returns list of Endpoints to execute that match
|
8
|
-
# criteria based on the Endpoints' associated 'matcher' object
|
9
|
-
# with the input of the ENV Hash (passed to match).
|
10
|
-
#
|
11
|
-
class Registry
|
12
|
-
|
13
|
-
def initialize(options={})
|
14
|
-
@endpoints = {}
|
15
|
-
@matchers = []
|
16
|
-
@regexp_matcher_factory = options.fetch(:regexp_matcher_factory) {
|
17
|
-
::Serf::Util::RegexpMatcher
|
18
|
-
}
|
19
|
-
end
|
20
|
-
|
21
|
-
##
|
22
|
-
# Connects a matcher (String or an Object implementing ===) to endpoints.
|
23
|
-
#
|
24
|
-
def add(matcher, endpoints)
|
25
|
-
# Maybe we have an non-String matcher. Handle the Regexp case.
|
26
|
-
# We only keep track of matchers if it isn't a string because
|
27
|
-
# string matchers are just pulled out of endpoints by key lookup.
|
28
|
-
matcher = @regexp_matcher_factory.build matcher if matcher.kind_of? Regexp
|
29
|
-
@matchers << matcher unless matcher.is_a? String
|
30
|
-
|
31
|
-
# We add the (matcher+endpoint) into our endpoints
|
32
|
-
@endpoints[matcher] ||= []
|
33
|
-
@endpoints[matcher].concat endpoints
|
34
|
-
end
|
35
|
-
|
36
|
-
##
|
37
|
-
# @param [Hash] env The input message environment to match for endpoints.
|
38
|
-
# @return [Array] List of endpoints that matched.
|
39
|
-
#
|
40
|
-
def match(env={})
|
41
|
-
kind = env[:kind]
|
42
|
-
endpoints = []
|
43
|
-
endpoints.concat @endpoints.fetch(kind) { [] }
|
44
|
-
@matchers.each do |matcher|
|
45
|
-
endpoints.concat @endpoints[matcher] if matcher === env
|
46
|
-
end
|
47
|
-
return endpoints
|
48
|
-
end
|
49
|
-
|
50
|
-
##
|
51
|
-
# @return [Integer] Number of matchers this EndpointsMap tracks.
|
52
|
-
#
|
53
|
-
def size
|
54
|
-
return @endpoints.size
|
55
|
-
end
|
56
|
-
|
57
|
-
##
|
58
|
-
# Default factory method.
|
59
|
-
#
|
60
|
-
def self.build(options={})
|
61
|
-
self.new options
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
end
|
66
|
-
end
|
data/lib/serf/runners/direct.rb
DELETED
@@ -1,52 +0,0 @@
|
|
1
|
-
require 'serf/runners/helper'
|
2
|
-
require 'serf/util/error_handling'
|
3
|
-
|
4
|
-
module Serf
|
5
|
-
module Runners
|
6
|
-
|
7
|
-
##
|
8
|
-
# Direct runner drives the execution of a handler for given messages.
|
9
|
-
# This class deals with error handling and pushing handler results
|
10
|
-
# to proper error or results channels.
|
11
|
-
#
|
12
|
-
# NOTES:
|
13
|
-
# * Results returned from handlers are pushed to response channel.
|
14
|
-
# * Errors raised by handlers are pushed to error channel, not response.
|
15
|
-
#
|
16
|
-
class Direct
|
17
|
-
include Serf::Util::ErrorHandling
|
18
|
-
include Serf::Runners::Helper
|
19
|
-
|
20
|
-
def initialize(*args)
|
21
|
-
extract_options! args
|
22
|
-
opts! :response_channel
|
23
|
-
end
|
24
|
-
|
25
|
-
def call(handlers, context)
|
26
|
-
results = []
|
27
|
-
handlers.each do |handler|
|
28
|
-
ok, run_result = with_error_handling(context) do
|
29
|
-
handler.call
|
30
|
-
end
|
31
|
-
run_result = run_result.is_a?(Hash) ? [run_result] : Array(run_result)
|
32
|
-
|
33
|
-
# We only post to the response channel if we didn't catch and error.
|
34
|
-
# But we add both error and success results to the 'results' to
|
35
|
-
# pass back to the caller of the runner. This may be a background
|
36
|
-
# runner, which then the results are ignored. But it may have been
|
37
|
-
# the Serfer, which means all results should go back to the user
|
38
|
-
# as this was a foreground (synchronous) execution.
|
39
|
-
results.concat run_result
|
40
|
-
push_results run_result if ok
|
41
|
-
end
|
42
|
-
return results
|
43
|
-
end
|
44
|
-
|
45
|
-
def self.build(options={})
|
46
|
-
self.new options
|
47
|
-
end
|
48
|
-
|
49
|
-
end
|
50
|
-
|
51
|
-
end
|
52
|
-
end
|
@@ -1,69 +0,0 @@
|
|
1
|
-
require 'eventmachine'
|
2
|
-
|
3
|
-
require 'serf/runners/direct'
|
4
|
-
require 'serf/util/error_handling'
|
5
|
-
|
6
|
-
module Serf
|
7
|
-
module Runners
|
8
|
-
|
9
|
-
##
|
10
|
-
# This runner simply wraps another runner to execute in the
|
11
|
-
# EventMachine deferred threadpool.
|
12
|
-
#
|
13
|
-
# NOTE: Because the Serfer class validates messages before
|
14
|
-
# sending them to runners (and handlers), this class simply
|
15
|
-
# responds to the calling client with an 'MessageAcceptedEvent'
|
16
|
-
# to signal that the message will be processed later.
|
17
|
-
#
|
18
|
-
# Errors caught here will simply be logged. This is because
|
19
|
-
# the wrapped runner *MUST* handle its own errors. If an error
|
20
|
-
# should propagate up here, then it was most likely an error
|
21
|
-
# that occurred in a rescue block... we don't want to complicate
|
22
|
-
# any extra pushing to error channels because that may have
|
23
|
-
# been the cause of the error.
|
24
|
-
#
|
25
|
-
class EventMachine
|
26
|
-
include Serf::Util::ErrorHandling
|
27
|
-
|
28
|
-
def initialize(*args)
|
29
|
-
extract_options! args
|
30
|
-
|
31
|
-
# Manditory: Need a runner because this class is just a wrapper.
|
32
|
-
@runner = opts! :runner
|
33
|
-
|
34
|
-
@evm = opts :event_machine, ::EventMachine
|
35
|
-
@logger = opts :logger, ::Serf::Util::NullObject.new
|
36
|
-
end
|
37
|
-
|
38
|
-
def call(handlers, context)
|
39
|
-
# This queues up each handler to be run separately.
|
40
|
-
handlers.each do |handler|
|
41
|
-
@evm.defer(proc do
|
42
|
-
begin
|
43
|
-
with_error_handling(context) do
|
44
|
-
@runner.call [handler], context
|
45
|
-
end
|
46
|
-
rescue => e
|
47
|
-
@logger.fatal(
|
48
|
-
"EventMachineThread: #{e.inspect}\n\n#{e.backtrace.join("\n")}")
|
49
|
-
end
|
50
|
-
end)
|
51
|
-
end
|
52
|
-
return {
|
53
|
-
kind: 'serf/messages/message_accepted_event',
|
54
|
-
message: context
|
55
|
-
}
|
56
|
-
end
|
57
|
-
|
58
|
-
def self.build(options={})
|
59
|
-
options[:runner] = options.fetch(:runner) {
|
60
|
-
factory = options[:runner_factory] || ::Serf::Runners::Direct
|
61
|
-
factory.build options
|
62
|
-
}
|
63
|
-
self.new options
|
64
|
-
end
|
65
|
-
|
66
|
-
end
|
67
|
-
|
68
|
-
end
|
69
|
-
end
|
@@ -1,69 +0,0 @@
|
|
1
|
-
require 'girl_friday'
|
2
|
-
|
3
|
-
require 'serf/runners/helper'
|
4
|
-
require 'serf/util/error_handling'
|
5
|
-
|
6
|
-
module Serf
|
7
|
-
module Runners
|
8
|
-
|
9
|
-
##
|
10
|
-
#
|
11
|
-
class GirlFriday
|
12
|
-
include Serf::Util::ErrorHandling
|
13
|
-
include Serf::Runners::Helper
|
14
|
-
|
15
|
-
def initialize(*args)
|
16
|
-
extract_options! args
|
17
|
-
|
18
|
-
# Mandatory response channel.
|
19
|
-
opts! :response_channel
|
20
|
-
|
21
|
-
# Create our worker queue that will accept tasks (handler and context).
|
22
|
-
# The worker is just a block that passes on the task to the
|
23
|
-
# actual worker method.
|
24
|
-
@queue = ::GirlFriday::WorkQueue.new(
|
25
|
-
opts(:queue_name, :serf_runner),
|
26
|
-
:size => opts(:queue_size, 1)) do |task|
|
27
|
-
perform task
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def call(handlers, context)
|
32
|
-
# Push each handler into the queue along with a copy of the context.
|
33
|
-
handlers.each do |handler|
|
34
|
-
@queue.push(
|
35
|
-
handler: handler,
|
36
|
-
context: context.dup)
|
37
|
-
end
|
38
|
-
|
39
|
-
# We got here, we succeeded pushing all the works.
|
40
|
-
# Now we return our accepted event.
|
41
|
-
return {
|
42
|
-
kind: 'serf/messages/message_accepted_event',
|
43
|
-
message: context
|
44
|
-
}
|
45
|
-
end
|
46
|
-
|
47
|
-
##
|
48
|
-
# Builder method
|
49
|
-
#
|
50
|
-
def self.build(*args)
|
51
|
-
self.new *args
|
52
|
-
end
|
53
|
-
|
54
|
-
##
|
55
|
-
# Actually drives the execution of individual handlers passed to job
|
56
|
-
# queue.
|
57
|
-
#
|
58
|
-
def perform(task)
|
59
|
-
ok, run_result = with_error_handling(task[:context]) do
|
60
|
-
task[:handler].call
|
61
|
-
end
|
62
|
-
run_result = run_result.is_a?(Hash) ? [run_result] : Array(run_result)
|
63
|
-
push_results run_result if ok
|
64
|
-
end
|
65
|
-
|
66
|
-
end
|
67
|
-
|
68
|
-
end
|
69
|
-
end
|
data/lib/serf/runners/helper.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
module Serf
|
2
|
-
module Runners
|
3
|
-
|
4
|
-
module Helper
|
5
|
-
|
6
|
-
##
|
7
|
-
# Loop over the results and push them to the response channel.
|
8
|
-
# Any error in pushing individual messages will result in
|
9
|
-
# a log event and an error channel event.
|
10
|
-
def push_results(results)
|
11
|
-
response_channel = opts! :response_channel
|
12
|
-
results.each do |message|
|
13
|
-
with_error_handling(message) do
|
14
|
-
response_channel.push message
|
15
|
-
end
|
16
|
-
end
|
17
|
-
return nil
|
18
|
-
end
|
19
|
-
|
20
|
-
end
|
21
|
-
|
22
|
-
end
|
23
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
require 'hashie'
|
2
|
-
|
3
|
-
module Serf
|
4
|
-
module Util
|
5
|
-
|
6
|
-
##
|
7
|
-
# A Request Factory that just coerces a REQUEST ENV hash message
|
8
|
-
# into a Hashie::Mash object for convenience key/value access.
|
9
|
-
#
|
10
|
-
module MashFactory
|
11
|
-
|
12
|
-
def self.build(message)
|
13
|
-
Hashie::Mash.new message
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
end
|
18
|
-
end
|