switchman 1.6.1 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fb1dad0f6da4624a6946a25d2afb9a398fa101df
4
- data.tar.gz: 27c1f2e00c09a1b585408144526de94ce438561b
3
+ metadata.gz: 8a8e56791eabefc33f16db7b5f9917c547758b19
4
+ data.tar.gz: 9c5746a17f3b2c08f0a3a5e3d30823966e15017e
5
5
  SHA512:
6
- metadata.gz: fdf18b474711a37ade96a52015f09d736725a030151e97b0ac341741c254f1816409f36d564d19a5d4fbd29cea585d69f7c7f64c7029bd7a99cf026515fd8a07
7
- data.tar.gz: 80708161aeedbb40f42ca1f837ab9333f14a7a94d1d1ca3608c28bd1cbc655cbdc5471960a437f5c82a6a3813ce23008f7034b2374c2cafc16852822b2e20cec
6
+ metadata.gz: f90666ec8cdaea74595ebcdb75c84ae69aae97c4e48997fc7c1779ad3340e3fb0350b24b87e888b0365a27098e64fe0e28ea6895932e62b3f275d18a43abab99
7
+ data.tar.gz: 6d4f656621629771412e95f1facca1ce1b30023f20247b70cf383a995e636763408dd62db9312ac2356b2c633ddca72502079bef05158c3926133411d01405ec
@@ -11,12 +11,12 @@ module Switchman
11
11
  CATEGORIES =
12
12
  {
13
13
  # special cased to mean all other models
14
- :default => nil,
14
+ :primary => nil,
15
15
  # special cased to not allow activating a shard other than the default
16
16
  :unsharded => [Shard]
17
17
  }
18
18
  private_constant :CATEGORIES
19
- @shard_category = :unsharded
19
+ @connection_specification_name = @shard_category = :unsharded
20
20
 
21
21
  if defined?(::ProtectedAttributes)
22
22
  attr_accessible :default, :name, :database_server
@@ -71,7 +71,7 @@ module Switchman
71
71
  @default
72
72
  end
73
73
 
74
- def current(category = :default)
74
+ def current(category = :primary)
75
75
  active_shards[category] || Shard.default
76
76
  end
77
77
 
@@ -682,7 +682,7 @@ module Switchman
682
682
 
683
683
  def hashify_categories(categories)
684
684
  if categories.empty?
685
- { :default => self }
685
+ { :primary => self }
686
686
  else
687
687
  categories.inject({}) { |h, category| h[category] = self; h }
688
688
  end
@@ -65,8 +65,8 @@ module Switchman
65
65
  if reflection
66
66
  if reflection.options[:polymorphic]
67
67
  # a polymorphic association has to be discovered at runtime. This code ends up being something like
68
- # context_type.try(:constantize).try(:shard_category) || :default
69
- "read_attribute(:#{reflection.foreign_type}).try(:constantize).try(:shard_category) || :default"
68
+ # context_type.try(:constantize).try(:shard_category) || :primary
69
+ "read_attribute(:#{reflection.foreign_type}).try(:constantize).try(:shard_category) || :primary"
70
70
  else
71
71
  # otherwise we can just return a symbol for the statically known type of the association
72
72
  reflection.klass.shard_category.inspect
@@ -4,8 +4,14 @@ module Switchman
4
4
  module ClassMethods
5
5
  delegate :shard, to: :all
6
6
 
7
- def shard_category
8
- @shard_category || (self.superclass < ::ActiveRecord::Base && self.superclass.shard_category) || :default
7
+ if ::Rails.version < '5'
8
+ def shard_category
9
+ @shard_category || (self.superclass < ::ActiveRecord::Base && self.superclass.shard_category) || :primary
10
+ end
11
+ else
12
+ def shard_category
13
+ connection_specification_name.to_sym
14
+ end
9
15
  end
10
16
 
11
17
  def shard_category=(category)
@@ -14,11 +20,15 @@ module Switchman
14
20
  categories[shard_category].delete(self)
15
21
  categories.delete(shard_category) if categories[shard_category].empty?
16
22
  end
17
- connection_handler.uninitialize_ar(self)
18
23
  categories[category] ||= []
19
24
  categories[category] << self
20
- @shard_category = category
21
- connection_handler.initialize_categories(superclass)
25
+ if ::Rails.version < '5'
26
+ connection_handler.uninitialize_ar(self)
27
+ @shard_category = category
28
+ connection_handler.initialize_categories(superclass)
29
+ else
30
+ self.connection_specification_name = category.to_s
31
+ end
22
32
  end
23
33
 
24
34
  def integral_id?
@@ -76,7 +76,7 @@ module Switchman
76
76
  opts = grouped_calculation_options(operation.to_s.downcase, column_name, distinct)
77
77
 
78
78
  relation = build_grouped_calculation_relation(opts)
79
- target_shard = Shard.current(:default)
79
+ target_shard = Shard.current(:primary)
80
80
 
81
81
  rows = relation.activate do |rel, shard|
82
82
  calculated_data = klass.connection.select_all(rel)
@@ -22,8 +22,9 @@ module Switchman
22
22
  end
23
23
  end
24
24
 
25
- def establish_connection(owner, spec)
26
- super
25
+ def establish_connection(*args)
26
+ pool = super
27
+ owner, spec = ::Rails.version < '5' ? args : [nil, args.first]
27
28
 
28
29
  # this is the first place that the adapter would have been required; but now we
29
30
  # need this addition ASAP since it will be called when loading the default shard below
@@ -32,17 +33,6 @@ module Switchman
32
33
  ::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(ActiveRecord::PostgreSQLAdapter)
33
34
  end
34
35
 
35
- # AR3 uses the name, AR4 uses the model
36
- model = case owner
37
- when String
38
- owner.constantize
39
- when Class
40
- owner
41
- else
42
- raise "unknown owner #{owner}"
43
- end
44
- pool = owner_to_pool[owner.name]
45
-
46
36
  first_time = !Shard.instance_variable_get(:@default)
47
37
  if first_time
48
38
  # Have to cache the default shard before we insert sharding, otherwise the first access
@@ -55,13 +45,19 @@ module Switchman
55
45
 
56
46
  ::ActiveRecord::Base.configurations[::Rails.env] = spec.instance_variable_get(:@config).stringify_keys
57
47
  end
48
+
58
49
  @shard_connection_pools ||= { [:master, Shard.default.database_server.shareable? ? ::Rails.env : Shard.default] => pool}
59
50
 
60
- proxy = ConnectionPoolProxy.new(model.shard_category,
51
+ category = ::Rails.version < '5' ? owner.shard_category : pool.spec.name.to_sym
52
+ proxy = ConnectionPoolProxy.new(category,
61
53
  pool,
62
54
  @shard_connection_pools)
63
- owner_to_pool[owner.name] = proxy
64
- class_to_pool.clear
55
+ if ::Rails.version < '5'
56
+ owner_to_pool[owner.name] = proxy
57
+ class_to_pool.clear
58
+ else
59
+ owner_to_pool[pool.spec.name] = proxy
60
+ end
65
61
 
66
62
  if first_time
67
63
  if Shard.default.database_server.config[:prefer_slave]
@@ -79,7 +75,7 @@ module Switchman
79
75
  # DON'T do it if we're not the current connection handler - that means
80
76
  # we're in the middle of switching environments, and we don't want to
81
77
  # establish a connection with incorrect settings
82
- if (model == ::ActiveRecord::Base || model == Shard) && self == ::ActiveRecord::Base.connection_handler && !first_time
78
+ if [:primary, :unsharded].include?(category) && self == ::ActiveRecord::Base.connection_handler && !first_time
83
79
  Shard.default(reload: true, with_fallback: true)
84
80
  proxy.disconnect!
85
81
  end
@@ -106,50 +102,85 @@ module Switchman
106
102
  proxy
107
103
  end
108
104
 
109
- def remove_connection(model)
110
- uninitialize_ar(model) if owner_to_pool[model.name].is_a?(ConnectionPoolProxy)
111
- result = super
112
- initialize_categories
113
- result
114
- end
115
-
116
- def pool_for(owner)
117
- # copypasted from AR#ConnectionHandler other than proxy handling
105
+ if ::Rails.version < '5'
106
+ def remove_connection(model)
107
+ uninitialize_ar(model) if owner_to_pool[model.name].is_a?(ConnectionPoolProxy)
108
+ result = super
109
+ initialize_categories
110
+ result
111
+ end
118
112
 
119
- owner_to_pool.fetch(owner.name) {
120
- if ancestor_pool = pool_from_any_process_for(owner)
121
- # A connection was established in an ancestor process that must have
122
- # subsequently forked. We can't reuse the connection, but we can copy
123
- # the specification and establish a new connection with it.
124
- if ancestor_pool.is_a?(ConnectionPoolProxy)
125
- establish_connection owner, ancestor_pool.default_pool.spec
113
+ def pool_for(owner)
114
+ # copypasted from AR#ConnectionHandler other than proxy handling
115
+
116
+ owner_to_pool.fetch(owner.name) {
117
+ if ancestor_pool = pool_from_any_process_for(owner)
118
+ # A connection was established in an ancestor process that must have
119
+ # subsequently forked. We can't reuse the connection, but we can copy
120
+ # the specification and establish a new connection with it.
121
+ if ancestor_pool.is_a?(ConnectionPoolProxy)
122
+ establish_connection owner, ancestor_pool.default_pool.spec
123
+ else
124
+ establish_connection owner, ancestor_pool.spec
125
+ end
126
126
  else
127
- establish_connection owner, ancestor_pool.spec
127
+ owner_to_pool[owner.name] = nil
128
128
  end
129
- else
130
- owner_to_pool[owner.name] = nil
131
- end
132
- }
133
- end
129
+ }
130
+ end
134
131
 
135
- def retrieve_connection_pool(klass)
136
- class_to_pool[klass.name] ||= begin
137
- original_klass = klass
138
- until pool = pool_for(klass)
139
- klass = klass.superclass
140
- break unless klass <= Base
141
- end
132
+ def retrieve_connection_pool(klass)
133
+ class_to_pool[klass.name] ||= begin
134
+ original_klass = klass
135
+ until pool = pool_for(klass)
136
+ klass = klass.superclass
137
+ break unless klass <= Base
138
+ end
139
+
140
+ if pool.is_a?(ConnectionPoolProxy) && pool.category != original_klass.shard_category
141
+ default_pool = pool.default_pool
142
+ pool = nil
143
+ class_to_pool.each_value { |p| pool = p if p.is_a?(ConnectionPoolProxy) &&
144
+ p.category == original_klass.shard_category &&
145
+ p.default_pool == default_pool }
146
+ pool ||= ConnectionPoolProxy.new(original_klass.shard_category, default_pool, @shard_connection_pools)
147
+ end
142
148
 
143
- if pool.is_a?(ConnectionPoolProxy) && pool.category != original_klass.shard_category
144
- default_pool = pool.default_pool
145
- pool = nil
146
- class_to_pool.each_value { |p| pool = p if p.is_a?(ConnectionPoolProxy) &&
147
- p.category == original_klass.shard_category &&
148
- p.default_pool == default_pool }
149
- pool ||= ConnectionPoolProxy.new(original_klass.shard_category, default_pool, @shard_connection_pools)
149
+ class_to_pool[original_klass.name] = pool
150
150
  end
151
+ end
152
+ else
153
+ def remove_connection(spec_name)
154
+ pool = owner_to_pool[spec_name]
155
+ owner_to_pool[spec_name] = pool.default_pool if pool.is_a?(ConnectionPoolProxy)
156
+ super
157
+ end
151
158
 
152
- class_to_pool[original_klass.name] = pool
159
+ def retrieve_connection_pool(spec_name)
160
+ owner_to_pool.fetch(spec_name) do
161
+ if ancestor_pool = pool_from_any_process_for(spec_name)
162
+ # A connection was established in an ancestor process that must have
163
+ # subsequently forked. We can't reuse the connection, but we can copy
164
+ # the specification and establish a new connection with it.
165
+ pool = nil
166
+ if ancestor_pool.is_a?(ConnectionPoolProxy)
167
+ pool = establish_connection ancestor_pool.default_pool.spec
168
+ else
169
+ pool = establish_connection ancestor_pool.spec
170
+ end
171
+ pool.instance_variable_set(:@schema_cache, ancestor_pool.schema_cache) if ancestor_pool.schema_cache
172
+ pool
173
+ elsif spec_name != "primary"
174
+ primary_pool = retrieve_connection_pool("primary")
175
+ if primary_pool.is_a?(ConnectionPoolProxy)
176
+ ConnectionPoolProxy.new(spec_name.to_sym, primary_pool.default_pool, @shard_connection_pools)
177
+ else
178
+ primary_pool
179
+ end
180
+ else
181
+ owner_to_pool[spec_name] = nil
182
+ end
183
+ end
153
184
  end
154
185
  end
155
186
 
@@ -159,7 +190,7 @@ module Switchman
159
190
  end
160
191
 
161
192
  def switchman_connection_pool_proxies
162
- class_to_pool.values.uniq
193
+ (::Rails.version < '5' ? class_to_pool : owner_to_pool).values.uniq.select{|p| p.is_a?(ConnectionPoolProxy)}
163
194
  end
164
195
 
165
196
  private
@@ -7,13 +7,13 @@ module Switchman
7
7
 
8
8
  def initialize(*args)
9
9
  super
10
- self.shard_value = Shard.current(klass ? klass.shard_category : :default) unless shard_value
10
+ self.shard_value = Shard.current(klass ? klass.shard_category : :primary) unless shard_value
11
11
  self.shard_source_value = :implicit unless shard_source_value
12
12
  end
13
13
 
14
14
  def clone
15
15
  result = super
16
- result.shard_value = Shard.current(klass ? klass.shard_category : :default) unless shard_value
16
+ result.shard_value = Shard.current(klass ? klass.shard_category : :primary) unless shard_value
17
17
  result
18
18
  end
19
19
 
@@ -47,7 +47,7 @@ module Switchman
47
47
  self.activate { |relation| relation.explain(super_method: true) }
48
48
  end
49
49
 
50
- to_a_method = ::Rails.version > '5.0.0.beta2' ? :records : :to_a
50
+ to_a_method = ::Rails.version >= '5' ? :records : :to_a
51
51
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
52
52
  def #{to_a_method}(super_method: false)
53
53
  return super() if super_method
@@ -79,6 +79,10 @@ module Switchman
79
79
  EOS
80
80
  end
81
81
 
82
+ def automatic_reconnect=(value)
83
+ @connection_pools.values.each { |pool| pool.automatic_reconnect = value }
84
+ end
85
+
82
86
  def clear_idle_connections!(since_when)
83
87
  @connection_pools.values.each { |pool| pool.clear_idle_connections!(since_when) }
84
88
  end
@@ -113,7 +117,9 @@ module Switchman
113
117
  end
114
118
  end
115
119
  end
116
- spec = ::ActiveRecord::ConnectionAdapters::ConnectionSpecification.new(config, "#{config[:adapter]}_connection")
120
+ args = [config, "#{config[:adapter]}_connection"]
121
+ args.unshift(pool_key.join("/")) if ::Rails.version >= '5' # seems as good a name as any
122
+ spec = ::ActiveRecord::ConnectionAdapters::ConnectionSpecification.new(*args)
117
123
  # unfortunately the AR code that does this require logic can't really be
118
124
  # called in isolation
119
125
  require "active_record/connection_adapters/#{config[:adapter]}_adapter"
@@ -11,7 +11,7 @@ module Switchman
11
11
  environment ||= :master
12
12
  activated_environments << environment
13
13
  old_environment = self.environment
14
- @environment = environment
14
+ Thread.current[:shackles_environment] = environment
15
15
  old_environment
16
16
  end
17
17
 
@@ -5,7 +5,7 @@ module Switchman
5
5
  super
6
6
  end
7
7
 
8
- def current_shard(category = :default)
8
+ def current_shard(category = :primary)
9
9
  @active_shards[category] || Shard.default
10
10
  end
11
11
  end
@@ -1,3 +1,3 @@
1
1
  module Switchman
2
- VERSION = "1.6.1"
2
+ VERSION = "1.7.0"
3
3
  end
@@ -48,7 +48,7 @@ module Switchman
48
48
  # task. tasks which modify the schema may want to pass all categories in
49
49
  # so that schema updates for non-default tables happen against all shards.
50
50
  # this is handled automatically for the default migration tasks, below.
51
- def self.shardify_task(task_name, categories: [:default])
51
+ def self.shardify_task(task_name, categories: [:primary])
52
52
  old_task = ::Rake::Task[task_name]
53
53
  old_actions = old_task.actions.dup
54
54
  old_task.actions.clear
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: 1.6.1
4
+ version: 1.7.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: 2016-09-20 00:00:00.000000000 Z
13
+ date: 2016-10-10 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: railties
@@ -21,7 +21,7 @@ dependencies:
21
21
  version: '4.0'
22
22
  - - "<="
23
23
  - !ruby/object:Gem::Version
24
- version: 5.0.0.beta3
24
+ version: '5.1'
25
25
  type: :runtime
26
26
  prerelease: false
27
27
  version_requirements: !ruby/object:Gem::Requirement
@@ -31,7 +31,7 @@ dependencies:
31
31
  version: '4.0'
32
32
  - - "<="
33
33
  - !ruby/object:Gem::Version
34
- version: 5.0.0.beta3
34
+ version: '5.1'
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: activerecord
37
37
  requirement: !ruby/object:Gem::Requirement
@@ -41,7 +41,7 @@ dependencies:
41
41
  version: '4.0'
42
42
  - - "<="
43
43
  - !ruby/object:Gem::Version
44
- version: 5.0.0.beta3
44
+ version: '5.1'
45
45
  type: :runtime
46
46
  prerelease: false
47
47
  version_requirements: !ruby/object:Gem::Requirement
@@ -51,21 +51,21 @@ dependencies:
51
51
  version: '4.0'
52
52
  - - "<="
53
53
  - !ruby/object:Gem::Version
54
- version: 5.0.0.beta3
54
+ version: '5.1'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: shackles
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 1.1.0
61
+ version: 1.3.0
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 1.1.0
68
+ version: 1.3.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: open4
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +80,34 @@ dependencies:
80
80
  - - '='
81
81
  - !ruby/object:Gem::Version
82
82
  version: 1.3.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: appraisal
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 2.1.0
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.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: byebug
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
83
111
  - !ruby/object:Gem::Dependency
84
112
  name: mysql2
85
113
  requirement: !ruby/object:Gem::Requirement
@@ -142,14 +170,14 @@ dependencies:
142
170
  requirements:
143
171
  - - "~>"
144
172
  - !ruby/object:Gem::Version
145
- version: '10.0'
173
+ version: '11.3'
146
174
  type: :development
147
175
  prerelease: false
148
176
  version_requirements: !ruby/object:Gem::Requirement
149
177
  requirements:
150
178
  - - "~>"
151
179
  - !ruby/object:Gem::Version
152
- version: '10.0'
180
+ version: '11.3'
153
181
  description: Sharding
154
182
  email:
155
183
  - cody@instructure.com