switchman 3.0.2 → 3.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.
- checksums.yaml +4 -4
- data/Rakefile +1 -1
- data/db/migrate/20180828183945_add_default_shard_index.rb +1 -1
- data/db/migrate/20180828192111_add_timestamps_to_shards.rb +1 -1
- data/lib/switchman/action_controller/caching.rb +2 -2
- data/lib/switchman/active_record/abstract_adapter.rb +2 -13
- data/lib/switchman/active_record/associations.rb +223 -0
- data/lib/switchman/active_record/attribute_methods.rb +144 -63
- data/lib/switchman/active_record/base.rb +100 -43
- data/lib/switchman/active_record/calculations.rb +12 -5
- data/lib/switchman/active_record/connection_pool.rb +9 -31
- data/lib/switchman/active_record/database_configurations.rb +18 -2
- data/lib/switchman/active_record/finder_methods.rb +2 -2
- data/lib/switchman/active_record/migration.rb +7 -4
- data/lib/switchman/active_record/model_schema.rb +1 -1
- data/lib/switchman/active_record/persistence.rb +7 -2
- data/lib/switchman/active_record/postgresql_adapter.rb +6 -2
- data/lib/switchman/active_record/predicate_builder.rb +1 -1
- data/lib/switchman/active_record/query_methods.rb +27 -14
- data/lib/switchman/active_record/reflection.rb +1 -1
- data/lib/switchman/active_record/relation.rb +25 -24
- data/lib/switchman/active_record/statement_cache.rb +2 -2
- data/lib/switchman/active_record/table_definition.rb +1 -1
- data/lib/switchman/active_record/test_fixtures.rb +43 -0
- data/lib/switchman/active_support/cache.rb +16 -0
- data/lib/switchman/arel.rb +28 -6
- data/lib/switchman/database_server.rb +71 -65
- data/lib/switchman/default_shard.rb +0 -2
- data/lib/switchman/engine.rb +67 -125
- data/lib/switchman/errors.rb +4 -2
- data/lib/switchman/guard_rail/relation.rb +6 -9
- data/lib/switchman/guard_rail.rb +5 -0
- data/lib/switchman/parallel.rb +68 -0
- data/lib/switchman/r_spec_helper.rb +5 -17
- data/lib/switchman/rails.rb +1 -4
- data/{app/models → lib}/switchman/shard.rb +61 -188
- data/lib/switchman/sharded_instrumenter.rb +1 -1
- data/lib/switchman/standard_error.rb +11 -12
- data/{app/models → lib}/switchman/unsharded_record.rb +1 -1
- data/lib/switchman/version.rb +1 -1
- data/lib/switchman.rb +22 -2
- data/lib/tasks/switchman.rake +24 -13
- metadata +24 -22
- data/lib/switchman/active_record/association.rb +0 -206
- data/lib/switchman/open4.rb +0 -80
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.0
|
|
4
|
+
version: 3.1.0
|
|
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:
|
|
13
|
+
date: 2022-06-02 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: activerecord
|
|
@@ -18,48 +18,48 @@ dependencies:
|
|
|
18
18
|
requirements:
|
|
19
19
|
- - ">="
|
|
20
20
|
- !ruby/object:Gem::Version
|
|
21
|
-
version:
|
|
21
|
+
version: 6.1.4
|
|
22
22
|
- - "<"
|
|
23
23
|
- !ruby/object:Gem::Version
|
|
24
|
-
version: '
|
|
24
|
+
version: '7.1'
|
|
25
25
|
type: :runtime
|
|
26
26
|
prerelease: false
|
|
27
27
|
version_requirements: !ruby/object:Gem::Requirement
|
|
28
28
|
requirements:
|
|
29
29
|
- - ">="
|
|
30
30
|
- !ruby/object:Gem::Version
|
|
31
|
-
version:
|
|
31
|
+
version: 6.1.4
|
|
32
32
|
- - "<"
|
|
33
33
|
- !ruby/object:Gem::Version
|
|
34
|
-
version: '
|
|
34
|
+
version: '7.1'
|
|
35
35
|
- !ruby/object:Gem::Dependency
|
|
36
36
|
name: guardrail
|
|
37
37
|
requirement: !ruby/object:Gem::Requirement
|
|
38
38
|
requirements:
|
|
39
39
|
- - "~>"
|
|
40
40
|
- !ruby/object:Gem::Version
|
|
41
|
-
version: 3.0.
|
|
41
|
+
version: 3.0.1
|
|
42
42
|
type: :runtime
|
|
43
43
|
prerelease: false
|
|
44
44
|
version_requirements: !ruby/object:Gem::Requirement
|
|
45
45
|
requirements:
|
|
46
46
|
- - "~>"
|
|
47
47
|
- !ruby/object:Gem::Version
|
|
48
|
-
version: 3.0.
|
|
48
|
+
version: 3.0.1
|
|
49
49
|
- !ruby/object:Gem::Dependency
|
|
50
|
-
name:
|
|
50
|
+
name: parallel
|
|
51
51
|
requirement: !ruby/object:Gem::Requirement
|
|
52
52
|
requirements:
|
|
53
53
|
- - "~>"
|
|
54
54
|
- !ruby/object:Gem::Version
|
|
55
|
-
version: 1.
|
|
55
|
+
version: '1.22'
|
|
56
56
|
type: :runtime
|
|
57
57
|
prerelease: false
|
|
58
58
|
version_requirements: !ruby/object:Gem::Requirement
|
|
59
59
|
requirements:
|
|
60
60
|
- - "~>"
|
|
61
61
|
- !ruby/object:Gem::Version
|
|
62
|
-
version: 1.
|
|
62
|
+
version: '1.22'
|
|
63
63
|
- !ruby/object:Gem::Dependency
|
|
64
64
|
name: railties
|
|
65
65
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -69,7 +69,7 @@ dependencies:
|
|
|
69
69
|
version: '6.1'
|
|
70
70
|
- - "<"
|
|
71
71
|
- !ruby/object:Gem::Version
|
|
72
|
-
version: '
|
|
72
|
+
version: '7.1'
|
|
73
73
|
type: :runtime
|
|
74
74
|
prerelease: false
|
|
75
75
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -79,21 +79,21 @@ dependencies:
|
|
|
79
79
|
version: '6.1'
|
|
80
80
|
- - "<"
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
|
-
version: '
|
|
82
|
+
version: '7.1'
|
|
83
83
|
- !ruby/object:Gem::Dependency
|
|
84
84
|
name: appraisal
|
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
|
86
86
|
requirements:
|
|
87
87
|
- - "~>"
|
|
88
88
|
- !ruby/object:Gem::Version
|
|
89
|
-
version:
|
|
89
|
+
version: 2.3.0
|
|
90
90
|
type: :development
|
|
91
91
|
prerelease: false
|
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
93
|
requirements:
|
|
94
94
|
- - "~>"
|
|
95
95
|
- !ruby/object:Gem::Version
|
|
96
|
-
version:
|
|
96
|
+
version: 2.3.0
|
|
97
97
|
- !ruby/object:Gem::Dependency
|
|
98
98
|
name: byebug
|
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -242,8 +242,6 @@ extensions: []
|
|
|
242
242
|
extra_rdoc_files: []
|
|
243
243
|
files:
|
|
244
244
|
- Rakefile
|
|
245
|
-
- app/models/switchman/shard.rb
|
|
246
|
-
- app/models/switchman/unsharded_record.rb
|
|
247
245
|
- db/migrate/20130328212039_create_switchman_shards.rb
|
|
248
246
|
- db/migrate/20130328224244_create_default_shard.rb
|
|
249
247
|
- db/migrate/20161206323434_add_back_default_string_limits_switchman.rb
|
|
@@ -253,7 +251,7 @@ files:
|
|
|
253
251
|
- lib/switchman.rb
|
|
254
252
|
- lib/switchman/action_controller/caching.rb
|
|
255
253
|
- lib/switchman/active_record/abstract_adapter.rb
|
|
256
|
-
- lib/switchman/active_record/
|
|
254
|
+
- lib/switchman/active_record/associations.rb
|
|
257
255
|
- lib/switchman/active_record/attribute_methods.rb
|
|
258
256
|
- lib/switchman/active_record/base.rb
|
|
259
257
|
- lib/switchman/active_record/calculations.rb
|
|
@@ -275,6 +273,7 @@ files:
|
|
|
275
273
|
- lib/switchman/active_record/statement_cache.rb
|
|
276
274
|
- lib/switchman/active_record/table_definition.rb
|
|
277
275
|
- lib/switchman/active_record/tasks/database_tasks.rb
|
|
276
|
+
- lib/switchman/active_record/test_fixtures.rb
|
|
278
277
|
- lib/switchman/active_record/type_caster.rb
|
|
279
278
|
- lib/switchman/active_support/cache.rb
|
|
280
279
|
- lib/switchman/arel.rb
|
|
@@ -286,18 +285,21 @@ files:
|
|
|
286
285
|
- lib/switchman/errors.rb
|
|
287
286
|
- lib/switchman/guard_rail.rb
|
|
288
287
|
- lib/switchman/guard_rail/relation.rb
|
|
289
|
-
- lib/switchman/
|
|
288
|
+
- lib/switchman/parallel.rb
|
|
290
289
|
- lib/switchman/r_spec_helper.rb
|
|
291
290
|
- lib/switchman/rails.rb
|
|
291
|
+
- lib/switchman/shard.rb
|
|
292
292
|
- lib/switchman/sharded_instrumenter.rb
|
|
293
293
|
- lib/switchman/standard_error.rb
|
|
294
294
|
- lib/switchman/test_helper.rb
|
|
295
|
+
- lib/switchman/unsharded_record.rb
|
|
295
296
|
- lib/switchman/version.rb
|
|
296
297
|
- lib/tasks/switchman.rake
|
|
297
298
|
homepage: http://www.instructure.com/
|
|
298
299
|
licenses:
|
|
299
300
|
- MIT
|
|
300
|
-
metadata:
|
|
301
|
+
metadata:
|
|
302
|
+
rubygems_mfa_required: 'true'
|
|
301
303
|
post_install_message:
|
|
302
304
|
rdoc_options: []
|
|
303
305
|
require_paths:
|
|
@@ -306,14 +308,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
306
308
|
requirements:
|
|
307
309
|
- - ">="
|
|
308
310
|
- !ruby/object:Gem::Version
|
|
309
|
-
version: '2.
|
|
311
|
+
version: '2.7'
|
|
310
312
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
311
313
|
requirements:
|
|
312
314
|
- - ">="
|
|
313
315
|
- !ruby/object:Gem::Version
|
|
314
316
|
version: '0'
|
|
315
317
|
requirements: []
|
|
316
|
-
rubygems_version: 3.
|
|
318
|
+
rubygems_version: 3.1.6
|
|
317
319
|
signing_key:
|
|
318
320
|
specification_version: 4
|
|
319
321
|
summary: Rails sharding magic
|
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Switchman
|
|
4
|
-
module ActiveRecord
|
|
5
|
-
module Association
|
|
6
|
-
def shard
|
|
7
|
-
reflection.shard(owner)
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def build_record(*args)
|
|
11
|
-
shard.activate { super }
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def load_target
|
|
15
|
-
shard.activate { super }
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def scope
|
|
19
|
-
shard_value = @reflection.options[:multishard] ? @owner : shard
|
|
20
|
-
@owner.shard.activate { super.shard(shard_value, :association) }
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
module CollectionAssociation
|
|
25
|
-
def find_target
|
|
26
|
-
shards = reflection.options[:multishard] && owner.respond_to?(:associated_shards) ? owner.associated_shards : [shard]
|
|
27
|
-
# activate both the owner and the target's shard category, so that Reflection#join_id_for,
|
|
28
|
-
# when called for the owner, will be returned relative to shard the query will execute on
|
|
29
|
-
Shard.with_each_shard(shards, [klass.connection_classes, owner.class.connection_classes].uniq) do
|
|
30
|
-
super
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def _create_record(*)
|
|
35
|
-
shard.activate { super }
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
module BelongsToAssociation
|
|
40
|
-
def replace_keys(record, force: false)
|
|
41
|
-
if record&.class&.sharded_column?(reflection.association_primary_key(record.class))
|
|
42
|
-
foreign_id = record[reflection.association_primary_key(record.class)]
|
|
43
|
-
owner[reflection.foreign_key] = Shard.relative_id_for(foreign_id, record.shard, owner.shard)
|
|
44
|
-
else
|
|
45
|
-
super
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def shard
|
|
50
|
-
if @owner.class.sharded_column?(@reflection.foreign_key) &&
|
|
51
|
-
(foreign_id = @owner[@reflection.foreign_key])
|
|
52
|
-
Shard.shard_for(foreign_id, @owner.shard)
|
|
53
|
-
else
|
|
54
|
-
super
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
module ForeignAssociation
|
|
60
|
-
# significant change:
|
|
61
|
-
# * transpose the key to the correct shard
|
|
62
|
-
def set_owner_attributes(record) # rubocop:disable Naming/AccessorMethodName
|
|
63
|
-
return if options[:through]
|
|
64
|
-
|
|
65
|
-
key = owner._read_attribute(reflection.join_foreign_key)
|
|
66
|
-
key = Shard.relative_id_for(key, owner.shard, shard)
|
|
67
|
-
record._write_attribute(reflection.join_primary_key, key)
|
|
68
|
-
|
|
69
|
-
record._write_attribute(reflection.type, owner.class.polymorphic_name) if reflection.type
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
module Extension
|
|
74
|
-
def self.build(_model, _reflection); end
|
|
75
|
-
|
|
76
|
-
def self.valid_options
|
|
77
|
-
[:multishard]
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
::ActiveRecord::Associations::Builder::Association.extensions << Extension
|
|
82
|
-
|
|
83
|
-
module Preloader
|
|
84
|
-
module Association
|
|
85
|
-
# Copypasta from Activerecord but with added global_id_for goodness.
|
|
86
|
-
def records_for(ids)
|
|
87
|
-
scope.where(association_key_name => ids).load do |record|
|
|
88
|
-
global_key = if model.connection_classes == UnshardedRecord
|
|
89
|
-
convert_key(record[association_key_name])
|
|
90
|
-
else
|
|
91
|
-
Shard.global_id_for(record[association_key_name], record.shard)
|
|
92
|
-
end
|
|
93
|
-
owner = owners_by_key[convert_key(global_key)].first
|
|
94
|
-
association = owner.association(reflection.name)
|
|
95
|
-
association.set_inverse_instance(record)
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
# significant changes:
|
|
100
|
-
# * partition_by_shard the records_for call
|
|
101
|
-
# * re-globalize the fetched owner id before looking up in the map
|
|
102
|
-
def load_records
|
|
103
|
-
# owners can be duplicated when a relation has a collection association join
|
|
104
|
-
# #compare_by_identity makes such owners different hash keys
|
|
105
|
-
@records_by_owner = {}.compare_by_identity
|
|
106
|
-
|
|
107
|
-
if owner_keys.empty?
|
|
108
|
-
raw_records = []
|
|
109
|
-
else
|
|
110
|
-
# determine the shard to search for each owner
|
|
111
|
-
if reflection.macro == :belongs_to
|
|
112
|
-
# for belongs_to, it's the shard of the foreign_key
|
|
113
|
-
partition_proc = lambda do |owner|
|
|
114
|
-
if owner.class.sharded_column?(owner_key_name)
|
|
115
|
-
Shard.shard_for(owner[owner_key_name], owner.shard)
|
|
116
|
-
else
|
|
117
|
-
Shard.current
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
elsif !reflection.options[:multishard]
|
|
121
|
-
# for non-multishard associations, it's *just* the owner's shard
|
|
122
|
-
partition_proc = ->(owner) { owner.shard }
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
raw_records = Shard.partition_by_shard(owners, partition_proc) do |partitioned_owners|
|
|
126
|
-
relative_owner_keys = partitioned_owners.map do |owner|
|
|
127
|
-
key = owner[owner_key_name]
|
|
128
|
-
if key && owner.class.sharded_column?(owner_key_name)
|
|
129
|
-
key = Shard.relative_id_for(key, owner.shard,
|
|
130
|
-
Shard.current(klass.connection_classes))
|
|
131
|
-
end
|
|
132
|
-
convert_key(key)
|
|
133
|
-
end
|
|
134
|
-
relative_owner_keys.compact!
|
|
135
|
-
relative_owner_keys.uniq!
|
|
136
|
-
records_for(relative_owner_keys)
|
|
137
|
-
end
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
@preloaded_records = raw_records.select do |record|
|
|
141
|
-
assignments = false
|
|
142
|
-
|
|
143
|
-
owner_key = record[association_key_name]
|
|
144
|
-
if owner_key && record.class.sharded_column?(association_key_name)
|
|
145
|
-
owner_key = Shard.global_id_for(owner_key,
|
|
146
|
-
record.shard)
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
owners_by_key[convert_key(owner_key)].each do |owner|
|
|
150
|
-
entries = (@records_by_owner[owner] ||= [])
|
|
151
|
-
|
|
152
|
-
if reflection.collection? || entries.empty?
|
|
153
|
-
entries << record
|
|
154
|
-
assignments = true
|
|
155
|
-
end
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
assignments
|
|
159
|
-
end
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
# significant change: globalize keys on sharded columns
|
|
163
|
-
def owners_by_key
|
|
164
|
-
@owners_by_key ||= owners.each_with_object({}) do |owner, result|
|
|
165
|
-
key = owner[owner_key_name]
|
|
166
|
-
key = Shard.global_id_for(key, owner.shard) if key && owner.class.sharded_column?(owner_key_name)
|
|
167
|
-
key = convert_key(key)
|
|
168
|
-
(result[key] ||= []) << owner if key
|
|
169
|
-
end
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
# significant change: don't cache scope (since it could be for different shards)
|
|
173
|
-
def scope
|
|
174
|
-
build_scope
|
|
175
|
-
end
|
|
176
|
-
end
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
module CollectionProxy
|
|
180
|
-
def initialize(*args)
|
|
181
|
-
super
|
|
182
|
-
self.shard_value = scope.shard_value
|
|
183
|
-
self.shard_source_value = :association
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
def shard(*args)
|
|
187
|
-
scope.shard(*args)
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
module AutosaveAssociation
|
|
192
|
-
def record_changed?(reflection, record, key)
|
|
193
|
-
record.new_record? ||
|
|
194
|
-
(record.has_attribute?(reflection.foreign_key) && record.send(reflection.foreign_key) != key) || # have to use send instead of [] because sharding
|
|
195
|
-
record.attribute_changed?(reflection.foreign_key)
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
def save_belongs_to_association(reflection)
|
|
199
|
-
# this seems counter-intuitive, but the autosave code will assign to attribute bypassing switchman,
|
|
200
|
-
# after reading the id attribute _without_ bypassing switchman. So we need Shard.current for the
|
|
201
|
-
# category of the associated record to match Shard.current for the category of self
|
|
202
|
-
shard.activate(connection_classes_for_reflection(reflection)) { super }
|
|
203
|
-
end
|
|
204
|
-
end
|
|
205
|
-
end
|
|
206
|
-
end
|
data/lib/switchman/open4.rb
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'open4'
|
|
4
|
-
|
|
5
|
-
# This fixes a bug with exception handling,
|
|
6
|
-
# see https://github.com/ahoward/open4/pull/30
|
|
7
|
-
module Open4
|
|
8
|
-
def self.do_popen(b = nil, exception_propagation_at = nil, closefds=false, &cmd)
|
|
9
|
-
pw, pr, pe, ps = IO.pipe, IO.pipe, IO.pipe, IO.pipe
|
|
10
|
-
|
|
11
|
-
verbose = $VERBOSE
|
|
12
|
-
begin
|
|
13
|
-
$VERBOSE = nil
|
|
14
|
-
|
|
15
|
-
cid = fork {
|
|
16
|
-
if closefds
|
|
17
|
-
exlist = [0, 1, 2] | [pw,pr,pe,ps].map{|p| [p.first.fileno, p.last.fileno] }.flatten
|
|
18
|
-
ObjectSpace.each_object(IO){|io|
|
|
19
|
-
io.close if (not io.closed?) and (not exlist.include? io.fileno) rescue nil
|
|
20
|
-
}
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
pw.last.close
|
|
24
|
-
STDIN.reopen pw.first
|
|
25
|
-
pw.first.close
|
|
26
|
-
|
|
27
|
-
pr.first.close
|
|
28
|
-
STDOUT.reopen pr.last
|
|
29
|
-
pr.last.close
|
|
30
|
-
|
|
31
|
-
pe.first.close
|
|
32
|
-
STDERR.reopen pe.last
|
|
33
|
-
pe.last.close
|
|
34
|
-
|
|
35
|
-
STDOUT.sync = STDERR.sync = true
|
|
36
|
-
|
|
37
|
-
begin
|
|
38
|
-
cmd.call(ps)
|
|
39
|
-
rescue Exception => e
|
|
40
|
-
begin
|
|
41
|
-
Marshal.dump(e, ps.last)
|
|
42
|
-
ps.last.flush
|
|
43
|
-
rescue Errno::EPIPE
|
|
44
|
-
raise e
|
|
45
|
-
end
|
|
46
|
-
ensure
|
|
47
|
-
ps.last.close unless ps.last.closed?
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
exit!
|
|
51
|
-
}
|
|
52
|
-
ensure
|
|
53
|
-
$VERBOSE = verbose
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
[ pw.first, pr.last, pe.last, ps.last ].each { |fd| fd.close }
|
|
57
|
-
|
|
58
|
-
Open4.propagate_exception cid, ps.first if exception_propagation_at == :init
|
|
59
|
-
|
|
60
|
-
pw.last.sync = true
|
|
61
|
-
|
|
62
|
-
pi = [ pw.last, pr.first, pe.first ]
|
|
63
|
-
|
|
64
|
-
begin
|
|
65
|
-
return [cid, *pi] unless b
|
|
66
|
-
|
|
67
|
-
begin
|
|
68
|
-
b.call(cid, *pi)
|
|
69
|
-
ensure
|
|
70
|
-
pi.each { |fd| fd.close unless fd.closed? }
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
Open4.propagate_exception cid, ps.first if exception_propagation_at == :block
|
|
74
|
-
|
|
75
|
-
Process.waitpid2(cid).last
|
|
76
|
-
ensure
|
|
77
|
-
ps.first.close unless ps.first.closed?
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
end
|