deploy_pin 1.3.0 → 1.3.1

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
  SHA256:
3
- metadata.gz: a760ef5554d4821d4e57eca80bd547a24f5456c7e2bd27768fc2d62db3bad25e
4
- data.tar.gz: 33e218e88444b4362b29d7f506fcdc455f07c07b4c6760228043b5f403687948
3
+ metadata.gz: f3006ccc3913b4cc7ee0b5d68b316ffb70ecef2bbfeae27c7efec65422246cdb
4
+ data.tar.gz: d055a8554b590bc039915d3a2866f23aa8541db9ffe8c2c933294f54c2013785
5
5
  SHA512:
6
- metadata.gz: fbae2f1401711aabf1e88e3e01daeb2b8a1e07a4f3072fb8d54eb9f0f40f0bb74a4eaaed7859cd4119574544a2814393a44dcb7297808db6f89dd7e659d03220
7
- data.tar.gz: cb8203f11fefb55c2e525fb2b6a2ca82e0d8791b69c64717d3f830bf2c5a3cf91977cb3e3842d780aa6084171795154eb548546a610798a60c53938b35f7173b
6
+ metadata.gz: 70d4054142995c62609666711a87d4d4e42783bf4631e61f3a43e3407a9cd68654de65b0925655ca416fb65e41bccd3a2246125e446f5f4d8a9cb48f70e7ffd4
7
+ data.tar.gz: 654cbb997e23bf9a2cc9d8579e71646f68fd669dfd92235da2a1d15b8b3d9775b01db714ce99e79e23e6389282a907c2f9ae5b573c348bd4b0d3f43105989903
@@ -19,7 +19,11 @@ module DeployPin
19
19
  yield(index, _tasks.count, task, executable)
20
20
 
21
21
  # run if executable
22
- task.run if executable
22
+ if executable
23
+ run_with_timeout(!task.explicit_timeout? && !task.parallel?) do
24
+ task.run
25
+ end
26
+ end
23
27
 
24
28
  # mark each task as done
25
29
  task.mark unless task.done?
@@ -46,23 +50,30 @@ module DeployPin
46
50
  end
47
51
 
48
52
  private
53
+ # :reek:UtilityFunction
54
+ def files
55
+ Dir["#{DeployPin.tasks_path}/*.rb"]
56
+ end
49
57
 
50
- def files
51
- Dir["#{DeployPin.tasks_path}/*.rb"]
52
- end
58
+ def tasks
59
+ files.map do |file|
60
+ task = DeployPin::Task.new(file)
61
+ task.parse_file
53
62
 
54
- def tasks
55
- files.map do |file|
56
- task = DeployPin::Task.new(file)
57
- task.parse_file
63
+ # check if task is suitable
64
+ task if task_criteria.suitable?(task)
65
+ end.compact.sort # sort by group position in config
66
+ end
58
67
 
59
- # check if task is suitable
60
- task if task_criteria.suitable?(task)
61
- end.compact.sort # sort by group position in config
62
- end
68
+ def task_criteria
69
+ @task_criteria ||= DeployPin::TaskCriteria.new(identifiers: identifiers)
70
+ end
63
71
 
64
- def task_criteria
65
- @task_criteria ||= DeployPin::TaskCriteria.new(identifiers: identifiers)
66
- end
72
+ # :reek:UtilityFunction and :reek:ControlParameter
73
+ def run_with_timeout(under_timeout, &block)
74
+ return yield unless under_timeout
75
+
76
+ DeployPin::Database.execute_with_timeout(DeployPin.statement_timeout, **{}, &block)
77
+ end
67
78
  end
68
79
  end
@@ -2,6 +2,9 @@
2
2
 
3
3
  module DeployPin
4
4
  module Database
5
+ PG_TIMEOUT_STATEMENT = 'SET statement_timeout TO %s'
6
+ MYSQL_TIMEOUT_STATEMENT = 'SET max_execution_time = %s'
7
+
5
8
  extend self
6
9
 
7
10
  # Run a block under a sql maximum timeout.
@@ -46,7 +49,7 @@ module DeployPin
46
49
 
47
50
  return call_block_under_timeout(timeout, &blk) unless params.key? :connected_to
48
51
 
49
- klass = params[:connected_to].key? :database ? ActiveRecord::Base : ::ApplicationRecord
52
+ klass = params[:connected_to].key?(:database) ? ActiveRecord::Base : ::ApplicationRecord
50
53
  klass.connected_to(**params[:connected_to]) do
51
54
  call_block_under_timeout(timeout, &blk)
52
55
  end
@@ -65,32 +68,24 @@ module DeployPin
65
68
 
66
69
  timeout_statement =
67
70
  if postgresql?
68
- postgresql_timeout_statement
71
+ PG_TIMEOUT_STATEMENT
69
72
  elsif mysql?
70
- mysql_timeout_statement
73
+ MYSQL_TIMEOUT_STATEMENT
71
74
  end
72
75
 
73
- connection.select_all "#{timeout_statement} #{connection.quote(timeout_in_milliseconds)}"
76
+ connection.execute timeout_statement % connection.quote(timeout_in_milliseconds)
74
77
  end
75
78
 
76
79
  def postgresql?
77
80
  connection.adapter_name =~ /postg/i
78
81
  end
79
82
 
80
- def postgresql_timeout_statement
81
- "SET statement_timeout TO"
82
- end
83
-
84
83
  def mysql?
85
84
  connection.adapter_name =~ /mysql/i
86
85
  end
87
86
 
88
- def mysql_timeout_statement
89
- "SET max_execution_time ="
90
- end
91
-
92
87
  def connection
93
88
  ActiveRecord::Base.connection
94
89
  end
95
90
  end
96
- end
91
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeployPin
4
+ # Parallel wrapper to run parallel tasks using database statement timeout.
5
+ # This wrapper keeps parallel interface, but running the processes under db statement timeout.
6
+ #
7
+ # In order to use this wrapper, just use call parallel methods with `parallel_`. Ex.:
8
+ # parallel_each(1..2, in_processes: 2, timeout: 0.3.seconds) do |i|
9
+ # puts "Item: #{i}, Worker: #{Parallel.worker_number}"
10
+ # ActiveRecord::Base.connection.execute("<some db query>")
11
+ # end
12
+ #
13
+ # In order to pass more `timeout` options, it requires to pass an array, like:
14
+ # parallel_each(1..2, in_processes: 2, timeout: [0.3.seconds, { connected_to: { role: :reading } }]) do |i|
15
+ # puts "Item: #{i}, Worker: #{Parallel.worker_number}"
16
+ # ActiveRecord::Base.connection.execute("<some db query>")
17
+ # end
18
+ module ParallelWrapper
19
+ PARALLEL_PREFIX = 'parallel_'
20
+
21
+ # :reek:TooManyInstanceVariables
22
+ class ParallelRunner
23
+ def initialize(method_name, *args, &db_block)
24
+ @method_name = method_name
25
+ @db_block = db_block
26
+
27
+ if args.last.is_a?(Hash) && args.last.key?(:timeout)
28
+ @timeout_args = args.pop
29
+ prepare_timeout_args
30
+ end
31
+
32
+ @parallel_args = args
33
+ end
34
+
35
+ def run
36
+ raise 'You must provide at least one argument for parallel methods' if parallel_args.empty?
37
+
38
+ Parallel.send(parallel_method_name, *parallel_args) do |*block_args|
39
+ ActiveRecord::Base.connection_pool.with_connection do
40
+ DeployPin::Database.execute_with_timeout(timeout, **timeout_params) do
41
+ db_block.call(*block_args)
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ attr_accessor :method_name, :parallel_args, :db_block
50
+
51
+ def prepare_timeout_args
52
+ timeout = @timeout_args[:timeout]
53
+ if timeout.is_a?(Array)
54
+ @timeout = timeout.shift
55
+ @timeout_params = timeout.first
56
+ else
57
+ @timeout = timeout
58
+ end
59
+ end
60
+
61
+ def timeout
62
+ @timeout ||= DeployPin.statement_timeout
63
+ end
64
+
65
+ def timeout_params
66
+ @timeout_params ||= {}
67
+ end
68
+
69
+ def parallel_method_name
70
+ @parallel_method_name ||= method_name.to_s.gsub(PARALLEL_PREFIX, '').to_sym
71
+ end
72
+ end
73
+
74
+ def method_missing(name, *args, &block)
75
+ return super unless respond_to_missing?(name)
76
+
77
+ ParallelRunner.new(name, *args, &block).run
78
+ end
79
+
80
+ # :reek:ManualDispatch and :reek:BooleanParameter
81
+ def respond_to_missing?(method_name, include_private = false)
82
+ return super unless parallel_prefix_pattern.match? method_name
83
+
84
+ parallel_method_name = method_name.to_s.gsub(PARALLEL_PREFIX, '').to_sym
85
+
86
+ Parallel.respond_to?(parallel_method_name) || super
87
+ end
88
+
89
+ private
90
+
91
+ def parallel_prefix_pattern
92
+ @parallel_prefix_pattern ||= /\A#{PARALLEL_PREFIX}/
93
+ end
94
+ end
95
+ end
@@ -3,11 +3,15 @@
3
3
  # Task wrapper
4
4
  module DeployPin
5
5
  class Task
6
+ extend ::DeployPin::ParallelWrapper
7
+ include ::DeployPin::ParallelWrapper
8
+
6
9
  attr_reader :file,
7
10
  :uuid,
8
11
  :group,
9
12
  :title,
10
- :script
13
+ :script,
14
+ :explicit_timeout
11
15
 
12
16
  def initialize(file)
13
17
  @file = file
@@ -15,6 +19,8 @@ module DeployPin
15
19
  @group = nil
16
20
  @title = ''
17
21
  @script = ''
22
+ @explicit_timeout = false
23
+ @parallel = false
18
24
  end
19
25
 
20
26
  def run
@@ -31,6 +37,14 @@ module DeployPin
31
37
  DeployPin::Record.where(uuid: uuid).exists?
32
38
  end
33
39
 
40
+ def explicit_timeout?
41
+ @explicit_timeout
42
+ end
43
+
44
+ def parallel?
45
+ @parallel
46
+ end
47
+
34
48
  def parse_file
35
49
  File.foreach(file) do |line|
36
50
  case line.strip
@@ -41,6 +55,9 @@ module DeployPin
41
55
  @title = Regexp.last_match(1).strip
42
56
  when /\A[^#].*/
43
57
  @script += line
58
+
59
+ @explicit_timeout = true if line =~ /Database.execute_with_timeout.*/
60
+ @parallel = true if line =~ /[Pp]arallel.*/
44
61
  end
45
62
  end
46
63
  end
@@ -60,13 +77,13 @@ module DeployPin
60
77
 
61
78
  protected
62
79
 
63
- # for sorting
64
- def <=>(other)
65
- group_index <=> other.group_index
66
- end
80
+ # for sorting
81
+ def <=>(other)
82
+ group_index <=> other.group_index
83
+ end
67
84
 
68
- def group_index
69
- DeployPin.groups.index(group)
70
- end
85
+ def group_index
86
+ DeployPin.groups.index(group)
87
+ end
71
88
  end
72
89
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DeployPin
4
- VERSION = '1.3.0'
4
+ VERSION = '1.3.1'
5
5
  end
data/lib/deploy_pin.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'deploy_pin/runner'
4
4
  require 'deploy_pin/collector'
5
+ require 'deploy_pin/parallel_wrapper'
5
6
  require 'deploy_pin/task'
6
7
  require 'deploy_pin/task_criteria'
7
8
  require 'deploy_pin/engine'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deploy_pin
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Viktor Sych
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-16 00:00:00.000000000 Z
11
+ date: 2022-08-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -166,6 +166,7 @@ files:
166
166
  - lib/deploy_pin/collector.rb
167
167
  - lib/deploy_pin/database.rb
168
168
  - lib/deploy_pin/engine.rb
169
+ - lib/deploy_pin/parallel_wrapper.rb
169
170
  - lib/deploy_pin/runner.rb
170
171
  - lib/deploy_pin/task.rb
171
172
  - lib/deploy_pin/task_criteria.rb