upperkut 0.1.0 → 0.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 91c4f06d58263b93f03a4c21abb41cb855a2ec79
4
- data.tar.gz: e69b6d2c5fc335e34a49814af7bfa6e4225cd985
3
+ metadata.gz: 7ec2e461543486232e5fef688a00cbcef1a605fa
4
+ data.tar.gz: 6a4dbf806dcf5f8e9833b2e8062dcb39b8d6f184
5
5
  SHA512:
6
- metadata.gz: 503f1a51933df504062ae31e285e1471c857d3f52b61cb0310abfe43f60e54c4eee2f6cc670677d652cd49c868db503ba79f4b2c849b31dc1d5c896b76a9e81c
7
- data.tar.gz: f20ef3064c8d3d861abba7de081399f67d91613e7c33b7ed6aad658dfc5155784a52bbfd33f8f2a2253ff3aa753885ddd293b763ce806a111a10b64b86fec143
6
+ metadata.gz: c67e94633c0f33071724af9f939bc0b0ba3db29262c97526a386daba2cb66a0c89101c34ab42f284ee9bacd7b43a6664b55ffa5fbf79e91fbc7fe112e1a0d196
7
+ data.tar.gz: 06ceffd02b4bb83bdf1e2dfad936def7dbad1d6d7b2cab2c6d7faf965c119f7bda3e1bc7d7b4957ed732049f0b7851a887a281b8b2ee90683d40def39b64ba3a
data/.gitignore CHANGED
@@ -9,3 +9,4 @@
9
9
 
10
10
  # rspec failure tracking
11
11
  .rspec_status
12
+ *.gem
data/Gemfile.lock CHANGED
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- upperkut (0.1.0)
5
- redis
4
+ upperkut (0.1.1)
5
+ redis (>= 3.3.5, < 5)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -1,8 +1,6 @@
1
1
  # Upperkut
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/upperkut`. To experiment with that code, run `bin/console` for an interactive prompt.
4
-
5
- TODO: Delete this and the text above, and describe your gem
3
+ Batch background processing tool.
6
4
 
7
5
  ## Installation
8
6
 
@@ -21,8 +19,50 @@ Or install it yourself as:
21
19
  $ gem install upperkut
22
20
 
23
21
  ## Usage
24
-
25
- TODO: Write usage instructions here
22
+ Examples:
23
+
24
+ 1) Create a Worker class and the define how to process the batch;
25
+ ```ruby
26
+ class MyWorker
27
+ include Upperkut::Worker
28
+
29
+ # This is optional
30
+
31
+ setup_upperkut do |s|
32
+ # Define which redis instance you want to use
33
+ s.redis = Redis.new(url: ENV['ANOTHER_REDIS_INSTANCE_URL'])
34
+
35
+ # Define the amount of items must be accumulated
36
+ s.batch_size = 2_000 # The default value is 1_000
37
+
38
+ # How frequent the Processor should hit redis looking for elegible
39
+ # batch. The default value is 5. You can also set the env
40
+ # UPPERKUT_POLLING_INTERVAL.
41
+ s.polling_interval = 4
42
+
43
+ # How long the Processor should wait to process batch even though
44
+ # the amount of items did not reached the batch_size.
45
+ s.max_wait = 300
46
+ end
47
+
48
+ def perform(batch_items)
49
+ SidekiqJobA.perform_async(batch_items)
50
+ SidekiqJobB.perform_async(batch_items)
51
+
52
+ process_metrics(batch_items)
53
+ end
54
+ end
55
+ ```
56
+
57
+ 2) Start pushings items;
58
+ ```ruby
59
+ Myworker.push([{'id' => SecureRandom.uuid}, 'name' => 'Robert C Hall', 'action' => 'EMAIL_OPENNED'])
60
+ ```
61
+
62
+ 3) Start Upperkut;
63
+ ```bash
64
+ $ bundle exec upperkut --worker MyWorker --concurrency 10
65
+ ```
26
66
 
27
67
  ## Development
28
68
 
data/bin/upperkut CHANGED
@@ -1,13 +1,11 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require_relative '../lib/upperkut/cli'
4
- require 'pry'
5
4
 
6
5
  begin
7
6
  cli = Upperkut::CLI.new(ARGV)
8
7
  cli.start
9
8
  rescue => e
10
- raise e
11
9
  STDERR.puts e.message
12
10
  STDERR.puts e.backtrace.join('\n')
13
11
  exit 1
data/lib/upperkut/cli.rb CHANGED
@@ -7,6 +7,8 @@ module Upperkut
7
7
  def initialize(args = ARGV)
8
8
  @options = {}
9
9
  parse_options(args)
10
+
11
+ STDOUT.puts @options.inspect
10
12
  end
11
13
 
12
14
  def start
@@ -20,12 +22,13 @@ module Upperkut
20
22
  signals = %w(INT TERM)
21
23
 
22
24
  signals.each do |signal|
23
- w.puts(signal)
25
+ trap signal do
26
+ w.puts(signal)
27
+ end
24
28
  end
25
29
 
26
30
  begin
27
31
  manager.run
28
-
29
32
  while readable_io = IO.select([r])
30
33
  signal = readable_io.first[0].gets.strip
31
34
  handle_signal(signal)
@@ -33,21 +36,22 @@ module Upperkut
33
36
  rescue Interrupt
34
37
  puts 'Shutting down'
35
38
  manager.stop
39
+ sleep(5)
40
+ manager.kill
36
41
  exit(0)
37
42
  end
38
43
  end
39
44
 
40
45
  private
41
46
 
42
- def handle_signal(sig)
43
- Upperkut.logger.debug "Got #{sig} signal"
47
+ def handle_signal(sig)
44
48
  case sig
45
49
  when 'INT'
46
50
  raise Interrupt
47
51
  when 'TERM'
48
52
  raise Interrupt
49
53
  end
50
- end
54
+ end
51
55
 
52
56
  def parse_options(args)
53
57
  OptionParser.new do |o|
@@ -57,6 +61,9 @@ module Upperkut
57
61
  o.on('-r', '--require FILE', 'Indicate a file to be required') do |arg|
58
62
  @options[:file] = arg
59
63
  end
64
+ o.on('-c', '--concurrency INT', 'Numbers of threads to spawn') do |arg|
65
+ @options[:concurrency] = Integer(arg)
66
+ end
60
67
 
61
68
  end.parse!(args)
62
69
  end
@@ -10,16 +10,25 @@ module Upperkut
10
10
  def initialize(opts = {})
11
11
  self.worker = opts.fetch(:worker).constantize
12
12
  self.redis = worker.setup.redis
13
-
13
+ @concurrency = opts.fetch(:concurrency, 25)
14
14
  @stopped = false
15
+ @processors = []
15
16
  end
16
17
 
17
18
  def run
18
- Processor.new(self).process
19
+ @concurrency.times do
20
+ @processors << Processor.new(self).run
21
+ end
19
22
  end
20
23
 
21
24
  def stop
22
25
  @stopped = true
23
26
  end
27
+
28
+ def kill
29
+ @processors.each do |processor|
30
+ processor.kill
31
+ end
32
+ end
24
33
  end
25
34
  end
@@ -7,6 +7,18 @@ module Upperkut
7
7
  @sleeping_time = 0
8
8
  end
9
9
 
10
+ def run
11
+ @thread ||= Thread.new do
12
+ process
13
+ end
14
+ end
15
+ def kill
16
+ return if !@thread
17
+ @thread.raise Upperkut::Shutdown
18
+ end
19
+
20
+ private
21
+
10
22
  def process
11
23
  loop do
12
24
  if should_process?
@@ -15,18 +27,18 @@ module Upperkut
15
27
  next
16
28
  end
17
29
 
18
- puts "sleeping for #{@worker.setup.polling_interval} seconds"
19
30
  @sleeping_time += sleep(@worker.setup.polling_interval)
20
31
  end
21
32
  end
22
33
 
23
- private
24
-
25
34
  def should_process?
35
+ buffer_size = @worker.size
36
+
26
37
  return false if @manager.stopped
38
+ return false if buffer_size == 0
27
39
 
28
40
  # TODO: rename #setup by config
29
- @worker.size >= @worker.setup.batch_size ||
41
+ buffer_size >= @worker.setup.batch_size ||
30
42
  @sleeping_time >= @worker.setup.max_wait
31
43
  end
32
44
 
@@ -32,6 +32,15 @@ module Upperkut
32
32
  redis.llen(key)
33
33
  end
34
34
 
35
+ def latency
36
+ item = redis.lrange(key, -1, -1)
37
+ item = decode_json_items(item).first
38
+ return 0 unless item
39
+ now = Time.now.to_f
40
+ lat = now - item.fetch('enqueued_at', Time.now).to_f
41
+ lat
42
+ end
43
+
35
44
  private
36
45
 
37
46
  def key
data/lib/upperkut/util.rb CHANGED
@@ -12,12 +12,17 @@ module Upperkut
12
12
  klass_name
13
13
  end
14
14
 
15
- def decode_json_items(items)
16
- items.collect {|i| JSON.parse(i) }
15
+ def encode_json_items(items)
16
+ items = items.collect do |i|
17
+ JSON.generate(
18
+ 'enqueued_at' => Time.now.to_i,
19
+ 'body' => i
20
+ )
21
+ end
17
22
  end
18
23
 
19
- def encode_json_items(items)
20
- items.collect {|i| JSON.generate(i) }
24
+ def decode_json_items(items)
25
+ items.collect {|i| JSON.parse(i) }
21
26
  end
22
27
  end
23
28
  end
@@ -1,3 +1,3 @@
1
1
  module Upperkut
2
- VERSION = "0.1.0"
2
+ VERSION = '0.1.2'
3
3
  end
@@ -11,7 +11,9 @@ module Upperkut
11
11
  end
12
12
 
13
13
  def process
14
- items = self.class.fetch_items
14
+ items = self.class.fetch_items.collect! do |item|
15
+ item['body']
16
+ end
15
17
  perform(items)
16
18
  end
17
19
 
@@ -19,7 +21,7 @@ module Upperkut
19
21
  extend Forwardable
20
22
 
21
23
  def_delegators :setup, :strategy
22
- def_delegators :strategy, :push_items, :fetch_items, :size
24
+ def_delegators :strategy, :push_items, :size, :latency
23
25
 
24
26
  def push_items(items)
25
27
  strategy.push_items(items)
data/lib/upperkut.rb CHANGED
@@ -1,7 +1,52 @@
1
- require_relative "upperkut/version"
1
+ require_relative 'upperkut/version'
2
2
  require_relative 'upperkut/worker'
3
3
  require 'redis'
4
4
 
5
+ # Public: Upperkut is a batch background processing tool for Ruby.
6
+ #
7
+ # Examples:
8
+ #
9
+ # 1) Create a Worker class and the define how to process the batch;
10
+ #
11
+ # class MyWorker
12
+ # include Upperkut::Worker
13
+ #
14
+ # # This is optional
15
+ #
16
+ # setup_upperkut do |s|
17
+ # # Define which redis instance you want to use
18
+ # s.redis = Redis.new(url: ENV['ANOTHER_REDIS_INSTANCE_URL'])
19
+ #
20
+ # # Define the amount of items must be accumulated
21
+ # s.batch_size = 2_000 # The default value is 1_000
22
+ #
23
+ # # How frequent the Processor should hit redis looking for elegible
24
+ # # batch. The default value is 5. You can also set the env
25
+ # # UPPERKUT_POLLING_INTERVAL.
26
+ # s.polling_interval = 4
27
+ #
28
+ # # How long the Processor should wait to process batch even though
29
+ # # the amount of items did not reached the batch_size.
30
+ # s.max_wait = 300
31
+ # end
32
+ #
33
+ # def perform(batch_items)
34
+ # SidekiqJobA.perform_async(batch_items)
35
+ # SidekiqJobB.perform_async(batch_items)
36
+ #
37
+ # process_metrics(batch_items)
38
+ # end
39
+ # end
40
+ #
41
+ # 2) Start pushings items;
42
+ #
43
+ # Myworker.push([{'id' => SecureRandom.uuid}, 'name' => 'Robert C Hall', 'action' => 'EMAIL_OPENNED'])
44
+ #
45
+ # 3) Start Upperkut;
46
+ #
47
+ # $ bundle exec upperkut -worker MyWorker --concurrency 10
48
+ #
49
+ # 4) That's it :)
5
50
  module Upperkut
6
51
  class Configuration
7
52
  attr_accessor :batch_size, :redis, :strategy, :max_wait, :polling_interval
@@ -15,4 +60,6 @@ module Upperkut
15
60
  end
16
61
  end
17
62
  end
63
+
64
+ class Shutdown < StandardError ; end
18
65
  end
data/upperkut.gemspec CHANGED
@@ -1,30 +1,29 @@
1
1
 
2
- lib = File.expand_path("../lib", __FILE__)
2
+ lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "upperkut/version"
4
+ require 'upperkut/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "upperkut"
7
+ spec.name = 'upperkut'
8
8
  spec.version = Upperkut::VERSION
9
- spec.authors = ["Nando Sousa"]
10
- spec.email = ["nandosousafr@gmail.com"]
9
+ spec.authors = ['Nando Sousa']
10
+ spec.email = ['nandosousafr@gmail.com']
11
11
 
12
12
  spec.summary = %q{Batch background processing tool}
13
13
  spec.description = %q{Batch background processing tool}
14
- spec.homepage = "http://shipit.resultadosdigitais.com.br/open-source/"
15
- spec.license = "MIT"
16
-
17
- #spec.metadata["allowed_push_host"] = "all"
14
+ spec.homepage = 'http://shipit.resultadosdigitais.com.br/open-source/'
15
+ spec.license = 'MIT'
18
16
 
19
17
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
20
18
  f.match(%r{^(test|spec|features)/})
21
19
  end
22
- spec.bindir = "exe"
23
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
- spec.require_paths = ["lib"]
20
+ spec.executables = ['upperkut']
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.required_ruby_version = '>= 2.2.2'
25
24
 
26
- spec.add_dependency 'redis'
27
- spec.add_development_dependency "bundler", "~> 1.16"
28
- spec.add_development_dependency "rake", "~> 10.0"
29
- spec.add_development_dependency "rspec", "~> 3.0"
25
+ spec.add_dependency 'redis', '>= 3.3.5', '< 5'
26
+ spec.add_development_dependency 'bundler', '~> 1.16'
27
+ spec.add_development_dependency 'rake', '~> 10.0'
28
+ spec.add_development_dependency 'rspec', '~> 3.0'
30
29
  end
metadata CHANGED
@@ -1,12 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: upperkut
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nando Sousa
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
11
  date: 2018-06-24 00:00:00.000000000 Z
12
12
  dependencies:
@@ -16,14 +16,20 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 3.3.5
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '5'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
27
  - - ">="
25
28
  - !ruby/object:Gem::Version
26
- version: '0'
29
+ version: 3.3.5
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '5'
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: bundler
29
35
  requirement: !ruby/object:Gem::Requirement
@@ -69,7 +75,8 @@ dependencies:
69
75
  description: Batch background processing tool
70
76
  email:
71
77
  - nandosousafr@gmail.com
72
- executables: []
78
+ executables:
79
+ - upperkut
73
80
  extensions: []
74
81
  extra_rdoc_files: []
75
82
  files:
@@ -106,7 +113,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
106
113
  requirements:
107
114
  - - ">="
108
115
  - !ruby/object:Gem::Version
109
- version: '0'
116
+ version: 2.2.2
110
117
  required_rubygems_version: !ruby/object:Gem::Requirement
111
118
  requirements:
112
119
  - - ">="