switchman 3.6.4 → 3.6.6

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: fe8b34e257391bacd4982df3b99510262ebd3ed88d9f00c943a018fdec7d015e
4
- data.tar.gz: d55e7363f9595127992f2f874f39e1c017ac9d6e757d8b6b8e0a30fd4bd778b0
3
+ metadata.gz: 8c9938f088ef1bcce6ba0647f9e46db87d777a8b654a832bc82dbb24be2bea0a
4
+ data.tar.gz: 22087e848ed37bf0551999ea9923248688a5194501be7d02d07e2a2448fe701d
5
5
  SHA512:
6
- metadata.gz: 61ef5a93ffd16bf1cca7ac8cd064230e62257c7d12567fb52f0f2e5e7ceac13ab2b34b61077cea8d62cb2390f4224229a3444183a52b877fca883cdfc50e5b10
7
- data.tar.gz: 17c1326e8daf6ad3febbaacc48a6e8642befe6c0a0847de83e55cbc40e687ee8cb9850ebd885ace4e5847993171e28a236bfed9b9690ada52b23c2e0d7687f4e
6
+ metadata.gz: 21931a6cedf1dfb74539e19657a9df58becb94e098a94423a059c3961ee042f36a66b547c40bc62ca66b17498235330743f12f8bc3d75a1c6f5172b25df15d37
7
+ data.tar.gz: 0220b63454657b7cddd89100a2d1ebe940369d6c0749345153912676741eba50f48d6ec1ae5e68f1eed08c46f4fa4e6c77032a0b0839f46645e7a4b376ef9ac0
@@ -22,13 +22,59 @@ module Switchman
22
22
  @integral_id
23
23
  end
24
24
 
25
- %w[transaction insert_all upsert_all].each do |method|
25
+ def transaction(**)
26
+ if self != ::ActiveRecord::Base && current_scope
27
+ current_scope.activate do
28
+ db = Shard.current(connection_class_for_self).database_server
29
+ db.unguard { super }
30
+ end
31
+ else
32
+ db = Shard.current(connection_class_for_self).database_server
33
+ db.unguard { super }
34
+ end
35
+ end
36
+
37
+ # NOTE: `returning` values are _not_ transposed back to the current shard
38
+ %w[insert_all upsert_all].each do |method|
26
39
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
27
- def #{method}(*, **)
28
- if self != ::ActiveRecord::Base && current_scope
29
- current_scope.activate do
40
+ def #{method}(attributes, returning: nil, **)
41
+ scope = self != ::ActiveRecord::Base && current_scope
42
+ if (target_shard = scope&.primary_shard) == (current_shard = Shard.current(connection_class_for_self))
43
+ scope = nil
44
+ end
45
+ if scope
46
+ dupped = false
47
+ attributes.each_with_index do |hash, i|
48
+ if dupped || hash.any? { |k, v| sharded_column?(k) }
49
+ unless dupped
50
+ attributes = attributes.dup
51
+ dupped = true
52
+ end
53
+ attributes[i] = hash.to_h do |k, v|
54
+ if sharded_column?(k)
55
+ [k, Shard.relative_id_for(v, current_shard, target_shard)]
56
+ else
57
+ [k, v]
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ if scope
65
+ scope.activate do
30
66
  db = Shard.current(connection_class_for_self).database_server
31
- db.unguard { super }
67
+ result = db.unguard { super }
68
+ if result&.columns&.any? { |c| sharded_column?(c) }
69
+ transposed_rows = result.rows.map do |row|
70
+ row.map.with_index do |value, i|
71
+ sharded_column?(result.columns[i]) ? Shard.relative_id_for(value, target_shard, current_shard) : value
72
+ end
73
+ end
74
+ result = ::ActiveRecord::Result.new(result.columns, transposed_rows, result.column_types)
75
+ end
76
+
77
+ result
32
78
  end
33
79
  else
34
80
  db = Shard.current(connection_class_for_self).database_server
@@ -50,16 +50,16 @@ module Switchman
50
50
  end
51
51
  end
52
52
 
53
- class PrefixingIO
53
+ class TransformingIO
54
54
  delegate_missing_to :@original_io
55
55
 
56
- def initialize(prefix, original_io)
57
- @prefix = prefix
56
+ def initialize(transformer, original_io)
57
+ @transformer = transformer
58
58
  @original_io = original_io
59
59
  end
60
60
 
61
61
  def puts(*args)
62
- args.flatten.each { |arg| @original_io.puts "#{@prefix}: #{arg}" }
62
+ args.flatten.each { |arg| @original_io.puts @transformer.call(arg) }
63
63
  end
64
64
  end
65
65
  end
@@ -165,7 +165,7 @@ module Switchman
165
165
  # forking.
166
166
  # exception: - :ignore, :raise, :defer (wait until the end and raise the first
167
167
  # error), or a proc
168
- # output: - :simple, :decorated (with database_server_id:shard_name)
168
+ # output: - :simple, :decorated (with database_server_id:shard_name), custom lambda transformer
169
169
  def with_each_shard(*args, parallel: false, exception: :raise, output: :simple)
170
170
  raise ArgumentError, "wrong number of arguments (#{args.length} for 0...2)" if args.length > 2
171
171
 
@@ -181,6 +181,13 @@ module Switchman
181
181
  scope, classes = args
182
182
  end
183
183
 
184
+ output = if output == :decorated
185
+ ->(arg) { "#{Shard.current.description}: #{arg}" }
186
+ elsif output == :simple
187
+ nil
188
+ else
189
+ output
190
+ end
184
191
  parallel = [Environment.cpu_count || 2, 2].min if parallel == true
185
192
  parallel = 0 if parallel == false || parallel.nil?
186
193
 
@@ -224,7 +231,10 @@ module Switchman
224
231
  new_title = [short_parent_name, name].join(" ")
225
232
  Process.setproctitle(new_title)
226
233
  Switchman.config[:on_fork_proc]&.call
227
- with_each_shard(subscope, classes, exception: exception, output: :decorated) do
234
+ with_each_shard(subscope,
235
+ classes,
236
+ exception: exception,
237
+ output: output || :decorated) do
228
238
  last_description = Shard.current.description
229
239
  Parallel::ResultWrapper.new(yield)
230
240
  end
@@ -258,9 +268,9 @@ module Switchman
258
268
  next unless shard.database_server
259
269
 
260
270
  shard.activate(*classes) do
261
- if output == :decorated
262
- $stdout = Parallel::PrefixingIO.new(shard.description, $stdout)
263
- $stderr = Parallel::PrefixingIO.new(shard.description, $stderr)
271
+ if output
272
+ $stdout = Parallel::TransformingIO.new(output, $stdout)
273
+ $stderr = Parallel::TransformingIO.new(output, $stderr)
264
274
  end
265
275
 
266
276
  result.concat Array.wrap(yield)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Switchman
4
- VERSION = "3.6.4"
4
+ VERSION = "3.6.6"
5
5
  end
@@ -71,12 +71,13 @@ module Switchman
71
71
  end
72
72
 
73
73
  def self.options
74
- { parallel: ENV["PARALLEL"].to_i }
74
+ { exception: (ENV["FAIL_FAST"] == "0") ? :defer : :raise, parallel: ENV["PARALLEL"].to_i }
75
75
  end
76
76
 
77
77
  # classes - an array or proc, to activate as the current shard during the
78
78
  # task.
79
79
  def self.shardify_task(task_name, classes: [::ActiveRecord::Base])
80
+ log_format = ENV.fetch("LOG_FORMAT", nil)
80
81
  old_task = ::Rake::Task[task_name]
81
82
  old_actions = old_task.actions.dup
82
83
  old_task.actions.clear
@@ -90,14 +91,56 @@ module Switchman
90
91
  ::GuardRail.activate(:deploy) do
91
92
  Shard.default.database_server.unguard do
92
93
  classes = classes.call if classes.respond_to?(:call)
93
- Shard.with_each_shard(scope, classes, **options) do
94
+
95
+ # We don't want the shard status messages to be wrapped using a custom log transfomer
96
+ original_stderr = $stderr
97
+ original_stdout = $stdout
98
+ output = if log_format == "json"
99
+ lambda { |msg|
100
+ JSON.dump(shard: Shard.current.id,
101
+ database_server: Shard.current.database_server.id,
102
+ type: "log",
103
+ message: msg)
104
+ }
105
+ else
106
+ nil
107
+ end
108
+ Shard.with_each_shard(scope, classes, output: output, **options) do
94
109
  shard = Shard.current
95
- puts "#{shard.id}: #{shard.description}"
110
+
111
+ if log_format == "json"
112
+ original_stdout.puts JSON.dump(
113
+ shard: shard.id,
114
+ database_server: shard.database_server.id,
115
+ type: "started"
116
+ )
117
+ else
118
+ original_stdout.puts "#{shard.id}: #{shard.description}"
119
+ end
96
120
 
97
121
  shard.database_server.unguard do
98
122
  old_actions.each { |action| action.call(*task_args) }
99
123
  end
124
+
125
+ if log_format == "json"
126
+ original_stdout.puts JSON.dump(
127
+ shard: shard.id,
128
+ database_server: shard.database_server.id,
129
+ type: "completed"
130
+ )
131
+ end
100
132
  nil
133
+ rescue => e
134
+ if log_format == "json"
135
+ original_stderr.puts JSON.dump(
136
+ shard: shard.id,
137
+ database_server: shard.database_server.id,
138
+ type: "failed",
139
+ message: e.full_message
140
+ )
141
+ end
142
+
143
+ raise
101
144
  end
102
145
  rescue => e
103
146
  if options[:parallel] != 0
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: switchman
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.6.4
4
+ version: 3.6.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cody Cutrer
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2024-05-10 00:00:00.000000000 Z
13
+ date: 2024-08-20 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord