switchman 1.6.1 → 1.7.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 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