upperkut 0.1.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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
  - - ">="