switchman 3.0.2 → 4.2.5
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 +16 -15
- data/db/migrate/20180828183945_add_default_shard_index.rb +2 -2
- data/db/migrate/20180828192111_add_timestamps_to_shards.rb +1 -1
- data/db/migrate/20190114212900_add_unique_name_indexes.rb +10 -4
- data/lib/switchman/action_controller/caching.rb +2 -2
- data/lib/switchman/active_record/abstract_adapter.rb +11 -18
- data/lib/switchman/active_record/associations.rb +315 -0
- data/lib/switchman/active_record/attribute_methods.rb +191 -79
- data/lib/switchman/active_record/base.rb +204 -50
- data/lib/switchman/active_record/calculations.rb +92 -49
- data/lib/switchman/active_record/connection_handler.rb +18 -0
- data/lib/switchman/active_record/connection_pool.rb +47 -34
- data/lib/switchman/active_record/database_configurations.rb +32 -6
- data/lib/switchman/active_record/finder_methods.rb +22 -16
- data/lib/switchman/active_record/log_subscriber.rb +3 -6
- data/lib/switchman/active_record/migration.rb +42 -14
- data/lib/switchman/active_record/model_schema.rb +1 -1
- data/lib/switchman/active_record/pending_migration_connection.rb +17 -0
- data/lib/switchman/active_record/persistence.rb +37 -2
- data/lib/switchman/active_record/postgresql_adapter.rb +39 -20
- data/lib/switchman/active_record/predicate_builder.rb +2 -2
- data/lib/switchman/active_record/query_cache.rb +26 -17
- data/lib/switchman/active_record/query_methods.rb +251 -140
- data/lib/switchman/active_record/reflection.rb +10 -3
- data/lib/switchman/active_record/relation.rb +110 -35
- data/lib/switchman/active_record/spawn_methods.rb +3 -7
- data/lib/switchman/active_record/statement_cache.rb +13 -9
- data/lib/switchman/active_record/table_definition.rb +1 -1
- data/lib/switchman/active_record/tasks/database_tasks.rb +6 -1
- data/lib/switchman/active_record/test_fixtures.rb +89 -0
- data/lib/switchman/active_support/cache.rb +25 -4
- data/lib/switchman/arel.rb +20 -7
- data/lib/switchman/call_super.rb +2 -2
- data/lib/switchman/database_server.rb +123 -83
- data/lib/switchman/default_shard.rb +14 -5
- data/lib/switchman/engine.rb +85 -131
- data/lib/switchman/environment.rb +2 -2
- data/lib/switchman/errors.rb +17 -2
- data/lib/switchman/guard_rail/relation.rb +7 -10
- data/lib/switchman/guard_rail.rb +5 -0
- data/lib/switchman/parallel.rb +68 -0
- data/lib/switchman/r_spec_helper.rb +17 -28
- data/lib/switchman/rails.rb +1 -4
- data/{app/models → lib}/switchman/shard.rb +229 -246
- data/lib/switchman/sharded_instrumenter.rb +9 -3
- data/lib/switchman/shared_schema_cache.rb +11 -0
- data/lib/switchman/standard_error.rb +15 -12
- data/lib/switchman/test_helper.rb +3 -3
- data/{app/models → lib}/switchman/unsharded_record.rb +1 -1
- data/lib/switchman/version.rb +1 -1
- data/lib/switchman.rb +44 -12
- data/lib/tasks/switchman.rake +101 -54
- metadata +34 -176
- 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:
|
|
4
|
+
version: 4.2.5
|
|
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: 2026-05-28 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: activerecord
|
|
@@ -18,222 +18,74 @@ dependencies:
|
|
|
18
18
|
requirements:
|
|
19
19
|
- - ">="
|
|
20
20
|
- !ruby/object:Gem::Version
|
|
21
|
-
version: '
|
|
21
|
+
version: '7.1'
|
|
22
22
|
- - "<"
|
|
23
23
|
- !ruby/object:Gem::Version
|
|
24
|
-
version: '
|
|
24
|
+
version: '8.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: '7.1'
|
|
32
32
|
- - "<"
|
|
33
33
|
- !ruby/object:Gem::Version
|
|
34
|
-
version: '
|
|
34
|
+
version: '8.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.
|
|
41
|
+
version: 3.1.0
|
|
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.
|
|
48
|
+
version: 3.1.0
|
|
49
49
|
- !ruby/object:Gem::Dependency
|
|
50
|
-
name:
|
|
51
|
-
requirement: !ruby/object:Gem::Requirement
|
|
52
|
-
requirements:
|
|
53
|
-
- - "~>"
|
|
54
|
-
- !ruby/object:Gem::Version
|
|
55
|
-
version: 1.3.0
|
|
56
|
-
type: :runtime
|
|
57
|
-
prerelease: false
|
|
58
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
59
|
-
requirements:
|
|
60
|
-
- - "~>"
|
|
61
|
-
- !ruby/object:Gem::Version
|
|
62
|
-
version: 1.3.0
|
|
63
|
-
- !ruby/object:Gem::Dependency
|
|
64
|
-
name: railties
|
|
50
|
+
name: parallel
|
|
65
51
|
requirement: !ruby/object:Gem::Requirement
|
|
66
52
|
requirements:
|
|
67
53
|
- - ">="
|
|
68
54
|
- !ruby/object:Gem::Version
|
|
69
|
-
version: '
|
|
55
|
+
version: '1.22'
|
|
70
56
|
- - "<"
|
|
71
57
|
- !ruby/object:Gem::Version
|
|
72
|
-
version: '
|
|
58
|
+
version: '3.0'
|
|
73
59
|
type: :runtime
|
|
74
60
|
prerelease: false
|
|
75
61
|
version_requirements: !ruby/object:Gem::Requirement
|
|
76
62
|
requirements:
|
|
77
63
|
- - ">="
|
|
78
64
|
- !ruby/object:Gem::Version
|
|
79
|
-
version: '
|
|
65
|
+
version: '1.22'
|
|
80
66
|
- - "<"
|
|
81
67
|
- !ruby/object:Gem::Version
|
|
82
|
-
version: '
|
|
68
|
+
version: '3.0'
|
|
83
69
|
- !ruby/object:Gem::Dependency
|
|
84
|
-
name:
|
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
|
86
|
-
requirements:
|
|
87
|
-
- - "~>"
|
|
88
|
-
- !ruby/object:Gem::Version
|
|
89
|
-
version: '2.1'
|
|
90
|
-
type: :development
|
|
91
|
-
prerelease: false
|
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
-
requirements:
|
|
94
|
-
- - "~>"
|
|
95
|
-
- !ruby/object:Gem::Version
|
|
96
|
-
version: '2.1'
|
|
97
|
-
- !ruby/object:Gem::Dependency
|
|
98
|
-
name: byebug
|
|
70
|
+
name: railties
|
|
99
71
|
requirement: !ruby/object:Gem::Requirement
|
|
100
72
|
requirements:
|
|
101
73
|
- - ">="
|
|
102
74
|
- !ruby/object:Gem::Version
|
|
103
|
-
version: '
|
|
104
|
-
|
|
105
|
-
prerelease: false
|
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
-
requirements:
|
|
108
|
-
- - ">="
|
|
109
|
-
- !ruby/object:Gem::Version
|
|
110
|
-
version: '0'
|
|
111
|
-
- !ruby/object:Gem::Dependency
|
|
112
|
-
name: pg
|
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
|
114
|
-
requirements:
|
|
115
|
-
- - "~>"
|
|
116
|
-
- !ruby/object:Gem::Version
|
|
117
|
-
version: '1.2'
|
|
118
|
-
type: :development
|
|
119
|
-
prerelease: false
|
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
-
requirements:
|
|
122
|
-
- - "~>"
|
|
123
|
-
- !ruby/object:Gem::Version
|
|
124
|
-
version: '1.2'
|
|
125
|
-
- !ruby/object:Gem::Dependency
|
|
126
|
-
name: pry
|
|
127
|
-
requirement: !ruby/object:Gem::Requirement
|
|
128
|
-
requirements:
|
|
129
|
-
- - ">="
|
|
75
|
+
version: '7.1'
|
|
76
|
+
- - "<"
|
|
130
77
|
- !ruby/object:Gem::Version
|
|
131
|
-
version: '
|
|
132
|
-
type: :
|
|
78
|
+
version: '8.1'
|
|
79
|
+
type: :runtime
|
|
133
80
|
prerelease: false
|
|
134
81
|
version_requirements: !ruby/object:Gem::Requirement
|
|
135
82
|
requirements:
|
|
136
83
|
- - ">="
|
|
137
84
|
- !ruby/object:Gem::Version
|
|
138
|
-
version: '
|
|
139
|
-
-
|
|
140
|
-
name: rake
|
|
141
|
-
requirement: !ruby/object:Gem::Requirement
|
|
142
|
-
requirements:
|
|
143
|
-
- - "~>"
|
|
144
|
-
- !ruby/object:Gem::Version
|
|
145
|
-
version: '13.0'
|
|
146
|
-
type: :development
|
|
147
|
-
prerelease: false
|
|
148
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
149
|
-
requirements:
|
|
150
|
-
- - "~>"
|
|
151
|
-
- !ruby/object:Gem::Version
|
|
152
|
-
version: '13.0'
|
|
153
|
-
- !ruby/object:Gem::Dependency
|
|
154
|
-
name: rspec-mocks
|
|
155
|
-
requirement: !ruby/object:Gem::Requirement
|
|
156
|
-
requirements:
|
|
157
|
-
- - "~>"
|
|
158
|
-
- !ruby/object:Gem::Version
|
|
159
|
-
version: '3.5'
|
|
160
|
-
type: :development
|
|
161
|
-
prerelease: false
|
|
162
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
163
|
-
requirements:
|
|
164
|
-
- - "~>"
|
|
165
|
-
- !ruby/object:Gem::Version
|
|
166
|
-
version: '3.5'
|
|
167
|
-
- !ruby/object:Gem::Dependency
|
|
168
|
-
name: rspec-rails
|
|
169
|
-
requirement: !ruby/object:Gem::Requirement
|
|
170
|
-
requirements:
|
|
171
|
-
- - "~>"
|
|
172
|
-
- !ruby/object:Gem::Version
|
|
173
|
-
version: '4.0'
|
|
174
|
-
type: :development
|
|
175
|
-
prerelease: false
|
|
176
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
177
|
-
requirements:
|
|
178
|
-
- - "~>"
|
|
179
|
-
- !ruby/object:Gem::Version
|
|
180
|
-
version: '4.0'
|
|
181
|
-
- !ruby/object:Gem::Dependency
|
|
182
|
-
name: rubocop
|
|
183
|
-
requirement: !ruby/object:Gem::Requirement
|
|
184
|
-
requirements:
|
|
185
|
-
- - "~>"
|
|
186
|
-
- !ruby/object:Gem::Version
|
|
187
|
-
version: '1.10'
|
|
188
|
-
type: :development
|
|
189
|
-
prerelease: false
|
|
190
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
191
|
-
requirements:
|
|
192
|
-
- - "~>"
|
|
193
|
-
- !ruby/object:Gem::Version
|
|
194
|
-
version: '1.10'
|
|
195
|
-
- !ruby/object:Gem::Dependency
|
|
196
|
-
name: rubocop-rake
|
|
197
|
-
requirement: !ruby/object:Gem::Requirement
|
|
198
|
-
requirements:
|
|
199
|
-
- - "~>"
|
|
200
|
-
- !ruby/object:Gem::Version
|
|
201
|
-
version: '0.5'
|
|
202
|
-
type: :development
|
|
203
|
-
prerelease: false
|
|
204
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
205
|
-
requirements:
|
|
206
|
-
- - "~>"
|
|
207
|
-
- !ruby/object:Gem::Version
|
|
208
|
-
version: '0.5'
|
|
209
|
-
- !ruby/object:Gem::Dependency
|
|
210
|
-
name: rubocop-rspec
|
|
211
|
-
requirement: !ruby/object:Gem::Requirement
|
|
212
|
-
requirements:
|
|
213
|
-
- - "~>"
|
|
214
|
-
- !ruby/object:Gem::Version
|
|
215
|
-
version: '2.2'
|
|
216
|
-
type: :development
|
|
217
|
-
prerelease: false
|
|
218
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
219
|
-
requirements:
|
|
220
|
-
- - "~>"
|
|
221
|
-
- !ruby/object:Gem::Version
|
|
222
|
-
version: '2.2'
|
|
223
|
-
- !ruby/object:Gem::Dependency
|
|
224
|
-
name: simplecov
|
|
225
|
-
requirement: !ruby/object:Gem::Requirement
|
|
226
|
-
requirements:
|
|
227
|
-
- - "~>"
|
|
228
|
-
- !ruby/object:Gem::Version
|
|
229
|
-
version: '0.15'
|
|
230
|
-
type: :development
|
|
231
|
-
prerelease: false
|
|
232
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
233
|
-
requirements:
|
|
234
|
-
- - "~>"
|
|
85
|
+
version: '7.1'
|
|
86
|
+
- - "<"
|
|
235
87
|
- !ruby/object:Gem::Version
|
|
236
|
-
version: '
|
|
88
|
+
version: '8.1'
|
|
237
89
|
description: Sharding
|
|
238
90
|
email:
|
|
239
91
|
- cody@instructure.com
|
|
@@ -242,8 +94,6 @@ extensions: []
|
|
|
242
94
|
extra_rdoc_files: []
|
|
243
95
|
files:
|
|
244
96
|
- Rakefile
|
|
245
|
-
- app/models/switchman/shard.rb
|
|
246
|
-
- app/models/switchman/unsharded_record.rb
|
|
247
97
|
- db/migrate/20130328212039_create_switchman_shards.rb
|
|
248
98
|
- db/migrate/20130328224244_create_default_shard.rb
|
|
249
99
|
- db/migrate/20161206323434_add_back_default_string_limits_switchman.rb
|
|
@@ -253,10 +103,11 @@ files:
|
|
|
253
103
|
- lib/switchman.rb
|
|
254
104
|
- lib/switchman/action_controller/caching.rb
|
|
255
105
|
- lib/switchman/active_record/abstract_adapter.rb
|
|
256
|
-
- lib/switchman/active_record/
|
|
106
|
+
- lib/switchman/active_record/associations.rb
|
|
257
107
|
- lib/switchman/active_record/attribute_methods.rb
|
|
258
108
|
- lib/switchman/active_record/base.rb
|
|
259
109
|
- lib/switchman/active_record/calculations.rb
|
|
110
|
+
- lib/switchman/active_record/connection_handler.rb
|
|
260
111
|
- lib/switchman/active_record/connection_pool.rb
|
|
261
112
|
- lib/switchman/active_record/database_configurations.rb
|
|
262
113
|
- lib/switchman/active_record/database_configurations/database_config.rb
|
|
@@ -264,6 +115,7 @@ files:
|
|
|
264
115
|
- lib/switchman/active_record/log_subscriber.rb
|
|
265
116
|
- lib/switchman/active_record/migration.rb
|
|
266
117
|
- lib/switchman/active_record/model_schema.rb
|
|
118
|
+
- lib/switchman/active_record/pending_migration_connection.rb
|
|
267
119
|
- lib/switchman/active_record/persistence.rb
|
|
268
120
|
- lib/switchman/active_record/postgresql_adapter.rb
|
|
269
121
|
- lib/switchman/active_record/predicate_builder.rb
|
|
@@ -275,6 +127,7 @@ files:
|
|
|
275
127
|
- lib/switchman/active_record/statement_cache.rb
|
|
276
128
|
- lib/switchman/active_record/table_definition.rb
|
|
277
129
|
- lib/switchman/active_record/tasks/database_tasks.rb
|
|
130
|
+
- lib/switchman/active_record/test_fixtures.rb
|
|
278
131
|
- lib/switchman/active_record/type_caster.rb
|
|
279
132
|
- lib/switchman/active_support/cache.rb
|
|
280
133
|
- lib/switchman/arel.rb
|
|
@@ -286,18 +139,23 @@ files:
|
|
|
286
139
|
- lib/switchman/errors.rb
|
|
287
140
|
- lib/switchman/guard_rail.rb
|
|
288
141
|
- lib/switchman/guard_rail/relation.rb
|
|
289
|
-
- lib/switchman/
|
|
142
|
+
- lib/switchman/parallel.rb
|
|
290
143
|
- lib/switchman/r_spec_helper.rb
|
|
291
144
|
- lib/switchman/rails.rb
|
|
145
|
+
- lib/switchman/shard.rb
|
|
292
146
|
- lib/switchman/sharded_instrumenter.rb
|
|
147
|
+
- lib/switchman/shared_schema_cache.rb
|
|
293
148
|
- lib/switchman/standard_error.rb
|
|
294
149
|
- lib/switchman/test_helper.rb
|
|
150
|
+
- lib/switchman/unsharded_record.rb
|
|
295
151
|
- lib/switchman/version.rb
|
|
296
152
|
- lib/tasks/switchman.rake
|
|
297
153
|
homepage: http://www.instructure.com/
|
|
298
154
|
licenses:
|
|
299
155
|
- MIT
|
|
300
|
-
metadata:
|
|
156
|
+
metadata:
|
|
157
|
+
rubygems_mfa_required: 'true'
|
|
158
|
+
source_code_uri: https://github.com/instructure/switchman
|
|
301
159
|
post_install_message:
|
|
302
160
|
rdoc_options: []
|
|
303
161
|
require_paths:
|
|
@@ -306,14 +164,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
306
164
|
requirements:
|
|
307
165
|
- - ">="
|
|
308
166
|
- !ruby/object:Gem::Version
|
|
309
|
-
version: '2
|
|
167
|
+
version: '3.2'
|
|
310
168
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
311
169
|
requirements:
|
|
312
170
|
- - ">="
|
|
313
171
|
- !ruby/object:Gem::Version
|
|
314
172
|
version: '0'
|
|
315
173
|
requirements: []
|
|
316
|
-
rubygems_version: 3.
|
|
174
|
+
rubygems_version: 3.4.19
|
|
317
175
|
signing_key:
|
|
318
176
|
specification_version: 4
|
|
319
177
|
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
|