upperkut 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- metadata +6 -45
- data/.circleci/config.yml +0 -65
- data/.codeclimate.yml +0 -15
- data/.gitignore +0 -12
- data/.rspec +0 -4
- data/CHANGELOG.md +0 -52
- data/CODE_OF_CONDUCT.md +0 -74
- data/Dockerfile +0 -7
- data/Gemfile +0 -11
- data/Gemfile.lock +0 -58
- data/LICENSE.txt +0 -21
- data/Makefile +0 -4
- data/README.md +0 -162
- data/Rakefile +0 -6
- data/docker-compose.yml +0 -18
- data/examples/basic.rb +0 -12
- data/examples/priority_worker.rb +0 -21
- data/examples/scheduled_worker.rb +0 -19
- data/examples/with_middlewares.rb +0 -42
- data/lib/upperkut/cli.rb +0 -100
- data/lib/upperkut/core_ext.rb +0 -18
- data/lib/upperkut/item.rb +0 -22
- data/lib/upperkut/logging.rb +0 -36
- data/lib/upperkut/manager.rb +0 -50
- data/lib/upperkut/middleware.rb +0 -35
- data/lib/upperkut/middlewares/datadog.rb +0 -11
- data/lib/upperkut/middlewares/new_relic.rb +0 -23
- data/lib/upperkut/middlewares/rollbar.rb +0 -25
- data/lib/upperkut/processor.rb +0 -64
- data/lib/upperkut/redis_pool.rb +0 -29
- data/lib/upperkut/strategies/base.rb +0 -56
- data/lib/upperkut/strategies/buffered_queue.rb +0 -218
- data/lib/upperkut/strategies/priority_queue.rb +0 -217
- data/lib/upperkut/strategies/scheduled_queue.rb +0 -162
- data/lib/upperkut/util.rb +0 -73
- data/lib/upperkut/version.rb +0 -3
- data/lib/upperkut/worker.rb +0 -42
- data/lib/upperkut/worker_thread.rb +0 -37
- data/lib/upperkut.rb +0 -103
- data/upperkut.gemspec +0 -29
@@ -1,19 +0,0 @@
|
|
1
|
-
require_relative '../lib/upperkut/worker'
|
2
|
-
require_relative '../lib/upperkut/strategies/scheduled_queue'
|
3
|
-
|
4
|
-
class ScheduledWorker
|
5
|
-
include Upperkut::Worker
|
6
|
-
|
7
|
-
setup_upperkut do |config|
|
8
|
-
config.strategy = Upperkut::Strategies::ScheduledQueue.new(
|
9
|
-
self,
|
10
|
-
batch_size: 200
|
11
|
-
)
|
12
|
-
end
|
13
|
-
|
14
|
-
def perform(items)
|
15
|
-
items.each do |item|
|
16
|
-
puts "event dispatched: #{item.inspect}"
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,42 +0,0 @@
|
|
1
|
-
require_relative '../lib/upperkut/worker'
|
2
|
-
require_relative '../lib/upperkut/logging'
|
3
|
-
|
4
|
-
class ClientMiddleware
|
5
|
-
def call(worker, items)
|
6
|
-
logger = Upperkut::Logging.logger
|
7
|
-
|
8
|
-
logger.info("inserting worker=#{worker} items=#{items.count}")
|
9
|
-
yield
|
10
|
-
logger.info("inserted worker=#{worker} items=#{items.count}")
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
class MyMiddleware
|
15
|
-
def call(worker, items)
|
16
|
-
logger = Upperkut::Logging.logger
|
17
|
-
|
18
|
-
logger.info("performing worker=#{worker} items=#{items.count}")
|
19
|
-
yield
|
20
|
-
logger.info("performed worker=#{worker} items=#{items.count}")
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
class WithMiddlewares
|
25
|
-
include Upperkut::Worker
|
26
|
-
|
27
|
-
setup_upperkut do |config|
|
28
|
-
config.server_middlewares do |chain|
|
29
|
-
chain.add MyMiddleware
|
30
|
-
end
|
31
|
-
|
32
|
-
config.client_middlewares do |chain|
|
33
|
-
chain.add ClientMiddleware
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def perform(_items)
|
38
|
-
puts 'executing.........'
|
39
|
-
exec_time = rand(80..200)
|
40
|
-
sleep (exec_time.to_f / 1000.to_f)
|
41
|
-
end
|
42
|
-
end
|
data/lib/upperkut/cli.rb
DELETED
@@ -1,100 +0,0 @@
|
|
1
|
-
require 'optparse'
|
2
|
-
require_relative '../upperkut'
|
3
|
-
require_relative 'manager'
|
4
|
-
require_relative 'logging'
|
5
|
-
|
6
|
-
module Upperkut
|
7
|
-
class CLI
|
8
|
-
def initialize(args = ARGV)
|
9
|
-
@options = {}
|
10
|
-
@logger = Upperkut::Logging.logger
|
11
|
-
|
12
|
-
parse_options(args)
|
13
|
-
end
|
14
|
-
|
15
|
-
def start
|
16
|
-
if target_required = @options[:require]
|
17
|
-
if File.directory?(target_required)
|
18
|
-
require 'rails'
|
19
|
-
if ::Rails::VERSION::MAJOR == 4
|
20
|
-
require File.expand_path("#{@options[:require]}/config/application.rb")
|
21
|
-
::Rails::Application.initializer 'upperkut.eager_load' do
|
22
|
-
::Rails.application.config.eager_load = true
|
23
|
-
end
|
24
|
-
|
25
|
-
require File.expand_path("#{@options[:require]}/config/environment.rb")
|
26
|
-
else
|
27
|
-
require File.expand_path("#{@options[:require]}/config/environment.rb")
|
28
|
-
end
|
29
|
-
else
|
30
|
-
require target_required
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
if log_level = @options[:log_level]
|
35
|
-
@logger.level = log_level
|
36
|
-
end
|
37
|
-
|
38
|
-
@options[:logger] = @logger
|
39
|
-
|
40
|
-
manager = Manager.new(@options)
|
41
|
-
|
42
|
-
@logger.info(@options)
|
43
|
-
|
44
|
-
r, w = IO.pipe
|
45
|
-
signals = %w[INT TERM]
|
46
|
-
|
47
|
-
signals.each do |signal|
|
48
|
-
trap signal do
|
49
|
-
w.puts(signal)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
begin
|
54
|
-
manager.run
|
55
|
-
while readable_io = IO.select([r])
|
56
|
-
signal = readable_io.first[0].gets.strip
|
57
|
-
handle_signal(signal)
|
58
|
-
end
|
59
|
-
rescue Interrupt
|
60
|
-
timeout = Integer(ENV['UPPERKUT_TIMEOUT'] || 8)
|
61
|
-
@logger.info(
|
62
|
-
"Stopping managers, wait for #{timeout} seconds and them kill processors"
|
63
|
-
)
|
64
|
-
|
65
|
-
manager.stop
|
66
|
-
sleep(timeout)
|
67
|
-
manager.kill
|
68
|
-
exit(0)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
private
|
73
|
-
|
74
|
-
def handle_signal(sig)
|
75
|
-
case sig
|
76
|
-
when 'INT'
|
77
|
-
raise Interrupt
|
78
|
-
when 'TERM'
|
79
|
-
raise Interrupt
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def parse_options(args)
|
84
|
-
OptionParser.new do |o|
|
85
|
-
o.on('-w', '--worker WORKER', 'Define worker to be processed') do |arg|
|
86
|
-
@options[:worker] = arg
|
87
|
-
end
|
88
|
-
o.on('-r', '--require FILE', 'Indicate a file to be required') do |arg|
|
89
|
-
@options[:require] = arg
|
90
|
-
end
|
91
|
-
o.on('-c', '--concurrency INT', 'Numbers of threads to spawn') do |arg|
|
92
|
-
@options[:concurrency] = Integer(arg)
|
93
|
-
end
|
94
|
-
o.on('-l', '--log-level LEVEL', 'Log level') do |arg|
|
95
|
-
@options[:log_level] = arg.to_i
|
96
|
-
end
|
97
|
-
end.parse!(args)
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
data/lib/upperkut/core_ext.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
begin
|
2
|
-
require 'active_support/core_ext/string/inflections'
|
3
|
-
rescue LoadError
|
4
|
-
unless ''.respond_to?(:constantize)
|
5
|
-
class String
|
6
|
-
def constantize
|
7
|
-
names = split('::')
|
8
|
-
names.shift if names.empty? || names.first.empty?
|
9
|
-
|
10
|
-
constant = Object
|
11
|
-
names.each do |name|
|
12
|
-
constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
13
|
-
end
|
14
|
-
constant
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
data/lib/upperkut/item.rb
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
require 'securerandom'
|
2
|
-
|
3
|
-
module Upperkut
|
4
|
-
class Item
|
5
|
-
attr_reader :id, :body, :enqueued_at
|
6
|
-
|
7
|
-
def initialize(id:, body:, enqueued_at: nil)
|
8
|
-
@id = id
|
9
|
-
@body = body
|
10
|
-
@enqueued_at = enqueued_at || Time.now.utc.to_i
|
11
|
-
@nacked = false
|
12
|
-
end
|
13
|
-
|
14
|
-
def nack
|
15
|
-
@nacked = true
|
16
|
-
end
|
17
|
-
|
18
|
-
def nacked?
|
19
|
-
@nacked
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
data/lib/upperkut/logging.rb
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
require 'logger'
|
2
|
-
require 'time'
|
3
|
-
require 'socket'
|
4
|
-
|
5
|
-
module Upperkut
|
6
|
-
module Logging
|
7
|
-
class DefaultFormatter < Logger::Formatter
|
8
|
-
def call(severity, time, _program_name, message)
|
9
|
-
"upperkut: #{time.utc.iso8601(3)} hostname=#{Socket.gethostname} "\
|
10
|
-
"pid=#{::Process.pid} severity=#{severity} #{format_message(message)}\n"
|
11
|
-
end
|
12
|
-
|
13
|
-
private
|
14
|
-
|
15
|
-
def format_message(message)
|
16
|
-
return "msg=#{message} " unless message.is_a?(Hash)
|
17
|
-
|
18
|
-
message.each_with_object('') do |(k, v), memo|
|
19
|
-
memo << "#{k}=#{v}\s"
|
20
|
-
memo
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.initialize_logger
|
26
|
-
logger = Logger.new($stdout)
|
27
|
-
logger.level = Logger::INFO
|
28
|
-
logger.formatter = DefaultFormatter.new
|
29
|
-
logger
|
30
|
-
end
|
31
|
-
|
32
|
-
def self.logger
|
33
|
-
@logger ||= initialize_logger
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
data/lib/upperkut/manager.rb
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
require_relative 'core_ext'
|
2
|
-
require_relative 'worker_thread'
|
3
|
-
require_relative 'logging'
|
4
|
-
require_relative 'worker'
|
5
|
-
|
6
|
-
module Upperkut
|
7
|
-
class Manager
|
8
|
-
attr_accessor :worker
|
9
|
-
attr_reader :stopped, :logger, :concurrency
|
10
|
-
|
11
|
-
def initialize(opts = {})
|
12
|
-
self.worker = opts.fetch(:worker).constantize
|
13
|
-
@concurrency = opts.fetch(:concurrency, 1)
|
14
|
-
@logger = opts.fetch(:logger, Logging.logger)
|
15
|
-
|
16
|
-
@stopped = false
|
17
|
-
@threads = []
|
18
|
-
end
|
19
|
-
|
20
|
-
def run
|
21
|
-
@concurrency.times do
|
22
|
-
spawn_thread
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def stop
|
27
|
-
@stopped = true
|
28
|
-
@threads.each(&:stop)
|
29
|
-
end
|
30
|
-
|
31
|
-
def kill
|
32
|
-
@threads.each(&:kill)
|
33
|
-
end
|
34
|
-
|
35
|
-
def notify_killed_processor(thread)
|
36
|
-
@threads.delete(thread)
|
37
|
-
spawn_thread unless @stopped
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
def spawn_thread
|
43
|
-
processor = Processor.new(worker, logger)
|
44
|
-
|
45
|
-
thread = WorkerThread.new(self, processor)
|
46
|
-
@threads << thread
|
47
|
-
thread.run
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
data/lib/upperkut/middleware.rb
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
module Upperkut
|
2
|
-
module Middleware
|
3
|
-
class Chain
|
4
|
-
attr_reader :items
|
5
|
-
|
6
|
-
def initialize
|
7
|
-
@items = []
|
8
|
-
end
|
9
|
-
|
10
|
-
def add(item)
|
11
|
-
return @items if @items.include?(item)
|
12
|
-
|
13
|
-
@items << item
|
14
|
-
end
|
15
|
-
|
16
|
-
def remove(item)
|
17
|
-
@items.delete(item)
|
18
|
-
end
|
19
|
-
|
20
|
-
def invoke(*args)
|
21
|
-
chain = @items.map(&:new)
|
22
|
-
|
23
|
-
traverse_chain = lambda do
|
24
|
-
if chain.empty?
|
25
|
-
yield
|
26
|
-
else
|
27
|
-
chain.shift.call(*args, &traverse_chain)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
traverse_chain.call
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
module Upperkut
|
2
|
-
module Middlewares
|
3
|
-
class NewRelic
|
4
|
-
include ::NewRelic::Agent::Instrumentation::ControllerInstrumentation
|
5
|
-
|
6
|
-
def call(worker, _items)
|
7
|
-
perform_action_with_newrelic_trace(trace_args(worker)) do
|
8
|
-
yield
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
private
|
13
|
-
|
14
|
-
def trace_args(worker)
|
15
|
-
{
|
16
|
-
name: 'perform',
|
17
|
-
class_name: worker.name,
|
18
|
-
category: 'OtherTransaction/Upperkut'
|
19
|
-
}
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
@@ -1,25 +0,0 @@
|
|
1
|
-
module Upperkut
|
2
|
-
module Middlewares
|
3
|
-
class Rollbar
|
4
|
-
def call(worker, items)
|
5
|
-
::Rollbar.reset_notifier!
|
6
|
-
yield
|
7
|
-
rescue Exception => e
|
8
|
-
handle_exception(e, worker, items)
|
9
|
-
raise e
|
10
|
-
end
|
11
|
-
|
12
|
-
private
|
13
|
-
|
14
|
-
def handle_exception(e, worker, items)
|
15
|
-
scope = {
|
16
|
-
framework: "Upperkut #{::Upperkut::VERSION}",
|
17
|
-
request: { params: { items_size: items.size } },
|
18
|
-
context: worker.name
|
19
|
-
}
|
20
|
-
|
21
|
-
::Rollbar.scope(scope).error(e, use_exception_level_filters: true)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
data/lib/upperkut/processor.rb
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
require_relative 'logging'
|
2
|
-
|
3
|
-
module Upperkut
|
4
|
-
class Processor
|
5
|
-
def initialize(worker, logger = Logging.logger)
|
6
|
-
@worker = worker
|
7
|
-
@strategy = worker.strategy
|
8
|
-
@worker_instance = worker.new
|
9
|
-
@logger = logger
|
10
|
-
end
|
11
|
-
|
12
|
-
def process
|
13
|
-
items = @worker.fetch_items.freeze
|
14
|
-
return unless items.any?
|
15
|
-
|
16
|
-
@worker.server_middlewares.invoke(@worker, items) do
|
17
|
-
@worker_instance.perform(items)
|
18
|
-
end
|
19
|
-
|
20
|
-
nacked_items, pending_ack_items = items.partition(&:nacked?)
|
21
|
-
@strategy.nack(nacked_items) if nacked_items.any?
|
22
|
-
@strategy.ack(pending_ack_items) if pending_ack_items.any?
|
23
|
-
rescue StandardError => error
|
24
|
-
@logger.error(
|
25
|
-
action: :handle_execution_error,
|
26
|
-
ex: error.to_s,
|
27
|
-
backtrace: error.backtrace.join("\n"),
|
28
|
-
item_size: Array(items).size
|
29
|
-
)
|
30
|
-
|
31
|
-
if items
|
32
|
-
if @worker_instance.respond_to?(:handle_error)
|
33
|
-
@worker_instance.handle_error(error, items)
|
34
|
-
return
|
35
|
-
end
|
36
|
-
|
37
|
-
@strategy.nack(items)
|
38
|
-
end
|
39
|
-
|
40
|
-
raise error
|
41
|
-
end
|
42
|
-
|
43
|
-
def blocking_process
|
44
|
-
sleeping_time = 0
|
45
|
-
|
46
|
-
loop do
|
47
|
-
break if @stopped
|
48
|
-
|
49
|
-
if @strategy.process?
|
50
|
-
sleeping_time = 0
|
51
|
-
process
|
52
|
-
next
|
53
|
-
end
|
54
|
-
|
55
|
-
sleeping_time += sleep(@worker.setup.polling_interval)
|
56
|
-
@logger.debug(sleeping_time: sleeping_time)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def stop
|
61
|
-
@stopped = true
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
data/lib/upperkut/redis_pool.rb
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
require 'connection_pool'
|
2
|
-
require 'redis'
|
3
|
-
|
4
|
-
module Upperkut
|
5
|
-
class RedisPool
|
6
|
-
DEFAULT_OPTIONS = {
|
7
|
-
pool_timeout: 1, # pool related option
|
8
|
-
size: 2, # pool related option
|
9
|
-
connect_timeout: 0.2,
|
10
|
-
read_timeout: 5.0,
|
11
|
-
write_timeout: 0.5
|
12
|
-
}.freeze
|
13
|
-
|
14
|
-
def initialize(options)
|
15
|
-
@options = DEFAULT_OPTIONS.merge(url: ENV['REDIS_URL'])
|
16
|
-
.merge(options)
|
17
|
-
|
18
|
-
# Extract pool related options
|
19
|
-
@size = @options.delete(:size)
|
20
|
-
@pool_timeout = @options.delete(:pool_timeout)
|
21
|
-
end
|
22
|
-
|
23
|
-
def create
|
24
|
-
ConnectionPool.new(timeout: @pool_timeout, size: @size) do
|
25
|
-
Redis.new(@options)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
@@ -1,56 +0,0 @@
|
|
1
|
-
module Upperkut
|
2
|
-
module Strategies
|
3
|
-
class Base
|
4
|
-
# Public: Ingests the event into strategy.
|
5
|
-
#
|
6
|
-
# items - The Array of items do be inserted.
|
7
|
-
#
|
8
|
-
# Returns true when success, raise when error.
|
9
|
-
def push_items(_items = [])
|
10
|
-
raise NotImplementedError
|
11
|
-
end
|
12
|
-
|
13
|
-
# Public: Retrieve events from Strategy.
|
14
|
-
#
|
15
|
-
# batch_size: # of items to be retrieved.
|
16
|
-
#
|
17
|
-
# Returns an Array containing events as hash.
|
18
|
-
def fetch_items(_batch_size)
|
19
|
-
raise NotImplementedError
|
20
|
-
end
|
21
|
-
|
22
|
-
# Public: Clear all data related to the strategy.
|
23
|
-
def clear
|
24
|
-
raise NotImplementedError
|
25
|
-
end
|
26
|
-
|
27
|
-
# Public: Confirms that items have been processed successfully.
|
28
|
-
#
|
29
|
-
# items - The Array of items do be confirmed.
|
30
|
-
def ack(_items)
|
31
|
-
raise NotImplementedError
|
32
|
-
end
|
33
|
-
|
34
|
-
# Public: Informs that items have been not processed successfully and therefore must be re-processed.
|
35
|
-
#
|
36
|
-
# items - The Array of items do be unacknowledged.
|
37
|
-
def nack(_items)
|
38
|
-
raise NotImplementedError
|
39
|
-
end
|
40
|
-
|
41
|
-
# Public: Tells when to execute the event processing,
|
42
|
-
# when this condition is met so the events are dispatched to
|
43
|
-
# the worker.
|
44
|
-
def process?
|
45
|
-
raise NotImplementedError
|
46
|
-
end
|
47
|
-
|
48
|
-
# Public: Consolidated strategy metrics.
|
49
|
-
#
|
50
|
-
# Returns hash containing metric name and values.
|
51
|
-
def metrics
|
52
|
-
raise NotImplementedError
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|