chained_job 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7a8aa04f7ae42cb12af32784e969ce63952884ff5db3e6e435943bcf1112f50b
4
+ data.tar.gz: db0cc9308feec6d98a8d8444f7c96da901b204f14d265cc1f6b20df9eab6a72a
5
+ SHA512:
6
+ metadata.gz: b865a3f752707bea1255f0bbc76bbb29ed901ab83b05092ef4c03274651d7b0eadee5307f6f28610a0cbfe374ede0e141a482c837b9677ed2defb52923b03190
7
+ data.tar.gz: 6b3b01a064ed9a76948196c16715c24226382c6fed5925a5fa3f53455a0b6b7d7976a066721de58746b0cb1bf6d3ed28899dd97901bfbf510f28b38f40a033dd
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require 'bundler/setup'
6
+ require 'irb'
7
+
8
+ require 'chained_job'
9
+
10
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'chained_job/config'
4
+ require 'chained_job/middleware'
5
+ require 'chained_job/version'
6
+
7
+ module ChainedJob
8
+ class Error < StandardError; end
9
+ class ConfigurationError < Error; end
10
+
11
+ module_function
12
+
13
+ def redis
14
+ config.redis || raise(ConfigurationError, 'Redis is not configured')
15
+ end
16
+
17
+ def logger
18
+ config.logger
19
+ end
20
+
21
+ def config
22
+ @config ||= ChainedJob::Config.new
23
+ end
24
+
25
+ def configure
26
+ yield(config)
27
+ end
28
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+
5
+ module ChainedJob
6
+ class Config
7
+ DEFAULT_ARGUMENTS_BATCH_SIZE = 1_000
8
+ DEFAULT_ARGUMENTS_QUEUE_EXPIRATION = 7 * 24 * 60 * 60 # 7 days
9
+
10
+ attr_accessor(
11
+ :arguments_batch_size,
12
+ :arguments_queue_expiration,
13
+ :around_start_chains,
14
+ :around_chain_process,
15
+ :debug,
16
+ :logger,
17
+ :redis,
18
+ )
19
+
20
+ def initialize
21
+ self.arguments_batch_size = DEFAULT_ARGUMENTS_BATCH_SIZE
22
+ self.arguments_queue_expiration = DEFAULT_ARGUMENTS_QUEUE_EXPIRATION
23
+
24
+ self.logger = ::Logger.new(STDOUT)
25
+
26
+ self.around_start_chains = ->(_options, &block) { block.call }
27
+ self.around_chain_process = ->(_options, &block) { block.call }
28
+
29
+ self.debug = true
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ChainedJob
4
+ module Helpers
5
+ module_function
6
+
7
+ def redis_key(job_class)
8
+ "chained_job:#{job_class}"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'chained_job/start_chains'
4
+ require 'chained_job/process'
5
+
6
+ module ChainedJob
7
+ module Middleware
8
+ def perform(worker_id = nil)
9
+ if worker_id
10
+ ChainedJob::Process.run(self, worker_id)
11
+ else
12
+ ChainedJob::StartChains.run(self.class, array_of_job_arguments, parallelism)
13
+ end
14
+ end
15
+
16
+ def array_of_job_arguments
17
+ raise NoMethodError, 'undefined method array_of_job_arguments'
18
+ end
19
+
20
+ def parallelism
21
+ raise NoMethodError, 'undefined method parallelism'
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'chained_job/helpers'
4
+
5
+ module ChainedJob
6
+ class Process
7
+ def self.run(job_instance, worker_id)
8
+ new(job_instance, worker_id).run
9
+ end
10
+
11
+ attr_reader :job_instance, :worker_id
12
+
13
+ def initialize(job_instance, worker_id)
14
+ @job_instance = job_instance
15
+ @worker_id = worker_id
16
+ end
17
+
18
+ def run
19
+ with_hooks do
20
+ return log_finished_worker unless argument
21
+
22
+ job_instance.process(argument)
23
+ job_instance.class.perform_later(worker_id)
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def with_hooks
30
+ ChainedJob.config.around_chain_process.call(options) { yield }
31
+ end
32
+
33
+ def options
34
+ { job_class: job_instance.class, worker_id: worker_id }
35
+ end
36
+
37
+ def log_finished_worker
38
+ ChainedJob.logger.info(
39
+ "#{job_instance.class} worker #{worker_id} finished"
40
+ )
41
+ end
42
+
43
+ def argument
44
+ @argument ||= ChainedJob.redis.lpop(redis_key)
45
+ end
46
+
47
+ def redis_key
48
+ Helpers.redis_key(job_instance.class)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'chained_job/helpers'
4
+
5
+ module ChainedJob
6
+ class StartChains
7
+ def self.run(job_class, array_of_job_arguments, parallelism)
8
+ new(job_class, array_of_job_arguments, parallelism).run
9
+ end
10
+
11
+ attr_reader :job_class, :array_of_job_arguments, :parallelism
12
+
13
+ def initialize(job_class, array_of_job_arguments, parallelism)
14
+ @job_class = job_class
15
+ @array_of_job_arguments = array_of_job_arguments
16
+ @parallelism = parallelism
17
+ end
18
+
19
+ def run
20
+ with_hooks do
21
+ redis.del(redis_key)
22
+
23
+ return unless array_of_job_arguments.count.positive?
24
+
25
+ store_job_arguments
26
+
27
+ log_chained_job_start
28
+
29
+ parallelism.times { |worked_id| job_class.perform_later(worked_id) }
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def with_hooks
36
+ ChainedJob.config.around_start_chains.call(options) { yield }
37
+ end
38
+
39
+ def options
40
+ {
41
+ job_class: job_class,
42
+ array_of_job_arguments: array_of_job_arguments,
43
+ parallelism: parallelism,
44
+ }
45
+ end
46
+
47
+ def store_job_arguments
48
+ array_of_job_arguments.each_slice(config.arguments_batch_size) do |sublist|
49
+ redis.rpush(redis_key, sublist)
50
+ end
51
+
52
+ redis.expire(redis_key, config.arguments_queue_expiration)
53
+ end
54
+
55
+ def log_chained_job_start
56
+ ChainedJob.logger.info(
57
+ "#{job_class} starting #{parallelism} workers "\
58
+ "processing #{array_of_job_arguments.count} items"
59
+ )
60
+ end
61
+
62
+ def redis
63
+ ChainedJob.redis
64
+ end
65
+
66
+ def redis_key
67
+ Helpers.redis_key(job_class)
68
+ end
69
+
70
+ def config
71
+ ChainedJob.config
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ChainedJob
4
+ VERSION = '0.1.0'
5
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chained_job
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Mantas Kūjalis
8
+ - Titas Norkūnas
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2020-04-24 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.17'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.17'
28
+ - !ruby/object:Gem::Dependency
29
+ name: minitest
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '5.0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '5.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rake
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '12.0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '12.0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: rubocop-vinted
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '0.3'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '0.3'
70
+ description: Chained job allows you to define an array of queued jobs that should
71
+ be run in sequence after the main job has been executed successfully.
72
+ email:
73
+ - mantas.kujalis@vinted.com
74
+ - titas@vinted.com
75
+ executables:
76
+ - setup
77
+ - console
78
+ extensions: []
79
+ extra_rdoc_files: []
80
+ files:
81
+ - bin/console
82
+ - bin/setup
83
+ - lib/chained_job.rb
84
+ - lib/chained_job/config.rb
85
+ - lib/chained_job/helpers.rb
86
+ - lib/chained_job/middleware.rb
87
+ - lib/chained_job/process.rb
88
+ - lib/chained_job/start_chains.rb
89
+ - lib/chained_job/version.rb
90
+ homepage: https://github.com/vinted/chained_job
91
+ licenses:
92
+ - MIT
93
+ metadata: {}
94
+ post_install_message:
95
+ rdoc_options: []
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ requirements: []
109
+ rubygems_version: 3.0.3
110
+ signing_key:
111
+ specification_version: 4
112
+ summary: Chained job helper
113
+ test_files: []