chained_job 0.1.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.
@@ -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: []