switchman 1.5.21 → 2.1.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.
Files changed (56) hide show
  1. checksums.yaml +5 -5
  2. data/app/models/switchman/shard.rb +757 -11
  3. data/db/migrate/20130328212039_create_switchman_shards.rb +3 -1
  4. data/db/migrate/20130328224244_create_default_shard.rb +4 -2
  5. data/db/migrate/20161206323434_add_back_default_string_limits_switchman.rb +13 -0
  6. data/db/migrate/20180828183945_add_default_shard_index.rb +15 -0
  7. data/db/migrate/20180828192111_add_timestamps_to_shards.rb +17 -0
  8. data/db/migrate/20190114212900_add_unique_name_indexes.rb +9 -0
  9. data/lib/switchman/action_controller/caching.rb +2 -0
  10. data/lib/switchman/active_record/abstract_adapter.rb +14 -4
  11. data/lib/switchman/active_record/association.rb +64 -37
  12. data/lib/switchman/active_record/attribute_methods.rb +54 -22
  13. data/lib/switchman/active_record/base.rb +76 -31
  14. data/lib/switchman/active_record/batches.rb +3 -1
  15. data/lib/switchman/active_record/calculations.rb +17 -22
  16. data/lib/switchman/active_record/connection_handler.rb +88 -78
  17. data/lib/switchman/active_record/connection_pool.rb +28 -23
  18. data/lib/switchman/active_record/finder_methods.rb +37 -28
  19. data/lib/switchman/active_record/log_subscriber.rb +14 -19
  20. data/lib/switchman/active_record/migration.rb +80 -0
  21. data/lib/switchman/active_record/model_schema.rb +3 -1
  22. data/lib/switchman/active_record/persistence.rb +9 -1
  23. data/lib/switchman/active_record/postgresql_adapter.rb +170 -126
  24. data/lib/switchman/active_record/predicate_builder.rb +3 -1
  25. data/lib/switchman/active_record/query_cache.rb +22 -87
  26. data/lib/switchman/active_record/query_methods.rb +139 -125
  27. data/lib/switchman/active_record/reflection.rb +42 -14
  28. data/lib/switchman/active_record/relation.rb +108 -33
  29. data/lib/switchman/active_record/spawn_methods.rb +2 -0
  30. data/lib/switchman/active_record/statement_cache.rb +44 -52
  31. data/lib/switchman/active_record/table_definition.rb +4 -2
  32. data/lib/switchman/active_record/type_caster.rb +2 -0
  33. data/lib/switchman/active_record/where_clause_factory.rb +5 -2
  34. data/lib/switchman/active_support/cache.rb +18 -0
  35. data/lib/switchman/arel.rb +8 -25
  36. data/lib/switchman/call_super.rb +19 -0
  37. data/lib/switchman/connection_pool_proxy.rb +70 -24
  38. data/lib/switchman/database_server.rb +69 -59
  39. data/lib/switchman/default_shard.rb +3 -0
  40. data/lib/switchman/engine.rb +44 -41
  41. data/lib/switchman/environment.rb +2 -0
  42. data/lib/switchman/errors.rb +2 -0
  43. data/lib/switchman/{shackles → guard_rail}/relation.rb +7 -5
  44. data/lib/switchman/{shackles.rb → guard_rail.rb} +6 -4
  45. data/lib/switchman/open4.rb +2 -0
  46. data/lib/switchman/r_spec_helper.rb +14 -8
  47. data/lib/switchman/rails.rb +2 -0
  48. data/lib/switchman/schema_cache.rb +17 -0
  49. data/lib/switchman/sharded_instrumenter.rb +4 -2
  50. data/lib/switchman/standard_error.rb +4 -2
  51. data/lib/switchman/test_helper.rb +7 -10
  52. data/lib/switchman/version.rb +3 -1
  53. data/lib/switchman.rb +5 -1
  54. data/lib/tasks/switchman.rake +53 -72
  55. metadata +84 -38
  56. data/app/models/switchman/shard_internal.rb +0 -692
@@ -1,8 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+
1
5
  module Switchman
2
6
  class DatabaseServer
3
7
  attr_accessor :id
4
8
 
5
9
  class << self
10
+ attr_accessor :creating_new_shard
11
+
6
12
  def all
7
13
  database_servers.values
8
14
  end
@@ -36,8 +42,14 @@ module Switchman
36
42
  def database_servers
37
43
  unless @database_servers
38
44
  @database_servers = {}.with_indifferent_access
39
- ::ActiveRecord::Base.configurations.each do |(id, config)|
40
- @database_servers[id] = DatabaseServer.new(id, config)
45
+ if ::Rails.version >= '6.0'
46
+ ::ActiveRecord::Base.configurations.configurations.each do |config|
47
+ @database_servers[config.env_name] = DatabaseServer.new(config.env_name, config.config)
48
+ end
49
+ else
50
+ ::ActiveRecord::Base.configurations.each do |(id, config)|
51
+ @database_servers[id] = DatabaseServer.new(id, config)
52
+ end
41
53
  end
42
54
  end
43
55
  @database_servers
@@ -59,12 +71,12 @@ module Switchman
59
71
  @fake
60
72
  end
61
73
 
62
- def config(environment = :master)
74
+ def config(environment = :primary)
63
75
  @configs[environment] ||= begin
64
76
  if @config[environment].is_a?(Array)
65
77
  @config[environment].map do |config|
66
78
  config = @config.merge((config || {}).symbolize_keys)
67
- # make sure Shackles doesn't get any brilliant ideas about choosing the first possible server
79
+ # make sure GuardRail doesn't get any brilliant ideas about choosing the first possible server
68
80
  config.delete(environment)
69
81
  config
70
82
  end
@@ -76,33 +88,33 @@ module Switchman
76
88
  end
77
89
  end
78
90
 
79
- def shackles_environment
80
- @shackles_environment || ::Shackles.environment
91
+ def guard_rail_environment
92
+ @guard_rail_environment || ::GuardRail.environment
81
93
  end
82
94
 
83
95
  # locks this db to a specific environment, except for
84
96
  # when doing writes (then it falls back to the current
85
- # value of Shackles.environment)
86
- def shackle!(environment = :slave)
87
- @shackles_environment = environment
97
+ # value of GuardRail.environment)
98
+ def guard!(environment = :secondary)
99
+ @guard_rail_environment = environment
88
100
  end
89
101
 
90
- def unshackle!
91
- @shackles_environment = nil
102
+ def unguard!
103
+ @guard_rail_environment = nil
92
104
  end
93
105
 
94
- def unshackle
95
- old_env = @shackles_environment
96
- unshackle!
106
+ def unguard
107
+ old_env = @guard_rail_environment
108
+ unguard!
97
109
  yield
98
110
  ensure
99
- shackle!(old_env)
111
+ guard!(old_env)
100
112
  end
101
113
 
102
114
  def shareable?
103
115
  @shareable_environment_key ||= []
104
- environment = shackles_environment
105
- explicit_user = ::Shackles.global_config[:username]
116
+ environment = guard_rail_environment
117
+ explicit_user = ::GuardRail.global_config[:username]
106
118
  return @shareable if @shareable_environment_key == [environment, explicit_user]
107
119
  @shareable_environment_key = [environment, explicit_user]
108
120
  if explicit_user
@@ -112,7 +124,7 @@ module Switchman
112
124
  config = config.first if config.is_a?(Array)
113
125
  username = config[:username]
114
126
  end
115
- @shareable = self.config[:adapter] != 'sqlite3' && username !~ /%?\{[a-zA-Z0-9_]+\}/
127
+ @shareable = username !~ /%?\{[a-zA-Z0-9_]+\}/
116
128
  end
117
129
 
118
130
  def shards
@@ -134,31 +146,16 @@ module Switchman
134
146
  create_schema = options[:schema]
135
147
  # look for another shard associated with this db
136
148
  other_shard = self.shards.where("name<>':memory:' OR name IS NULL").order(:id).first
137
- temp_name = other_shard.try(:name) unless id == ::Rails.env
138
- temp_name = Shard.default.name if id == ::Rails.env
139
149
 
140
150
  case config[:adapter]
141
151
  when 'postgresql'
142
- temp_name ||= 'public'
143
152
  create_statement = lambda { "CREATE SCHEMA #{name}" }
144
153
  password = " PASSWORD #{::ActiveRecord::Base.connection.quote(config[:password])}" if config[:password]
145
- when 'sqlite3'
146
- if name
147
- # Try to create a db on-disk even if the only shards for sqlite are in-memory
148
- temp_name = nil if temp_name == ':memory:'
149
- # Put it in the db directory if there are no other sqlite shards
150
- temp_name ||= 'db/dummy'
151
- temp_name = File.join(File.dirname(temp_name), "#{name}.sqlite3")
152
- # If they really asked for :memory:, give them :memory:
153
- temp_name = name if name == ':memory:'
154
- name = temp_name
155
- end
156
154
  else
157
- temp_name ||= self.config[:database] % self.config
158
155
  create_statement = lambda { "CREATE DATABASE #{name}" }
159
156
  end
160
157
  sharding_config = Switchman.config
161
- config_create_statement = sharding_config[config[:adapter]].try(:[], :create_statement)
158
+ config_create_statement = sharding_config[config[:adapter]]&.[](:create_statement)
162
159
  config_create_statement ||= sharding_config[:create_statement]
163
160
  if config_create_statement
164
161
  create_commands = Array(config_create_statement).dup
@@ -168,42 +165,49 @@ module Switchman
168
165
  end
169
166
 
170
167
  create_shard = lambda do
171
- shard = Shard.create!(:name => temp_name,
172
- :database_server => self) do |shard|
173
- shard.id = options[:id] if options[:id]
168
+ shard_id = options.fetch(:id) do
169
+ case config[:adapter]
170
+ when 'postgresql'
171
+ id_seq = Shard.connection.quote(Shard.connection.quote_table_name('switchman_shards_id_seq'))
172
+ next_id = Shard.connection.select_value("SELECT nextval(#{id_seq})")
173
+ next_id.to_i
174
+ else
175
+ nil
176
+ end
177
+ end
178
+
179
+ if name.nil?
180
+ base_name = self.config[:database].to_s % self.config
181
+ base_name = nil if base_name == ':memory:'
182
+ base_name << '_' if base_name
183
+ base_id = shard_id || SecureRandom.uuid
184
+ name = "#{base_name}shard_#{base_id}"
174
185
  end
186
+
187
+ shard = Shard.create!(:id => shard_id,
188
+ :name => name,
189
+ :database_server => self)
190
+ schema_already_existed = false
191
+
175
192
  begin
176
- if name.nil?
177
- base_name = self.config[:database] % self.config
178
- base_name = $1 if base_name =~ /(?:.*\/)(.+)_shard_\d+(?:\.sqlite3)?$/
179
- base_name = nil if base_name == ':memory:'
180
- base_name << '_' if base_name
181
- name = "#{base_name}shard_#{shard.id}"
182
- if config[:adapter] == 'sqlite3'
183
- # Try to create a db on-disk even if the only shards for sqlite are in-memory
184
- temp_name = nil if temp_name == ':memory:'
185
- # Put it in the db directory if there are no other sqlite shards
186
- temp_name ||= 'db/dummy'
187
- name = File.join(File.dirname(temp_name), "#{name}.sqlite3")
188
- shard.name = name
189
- end
190
- end
193
+ self.class.creating_new_shard = true
191
194
  shard.activate(*Shard.categories) do
192
- ::Shackles.activate(:deploy) do
195
+ ::GuardRail.activate(:deploy) do
193
196
  begin
194
197
  if create_statement
198
+ if (::ActiveRecord::Base.connection.select_value("SELECT 1 FROM pg_namespace WHERE nspname=#{::ActiveRecord::Base.connection.quote(name)}"))
199
+ schema_already_existed = true
200
+ raise "This schema already exists; cannot overwrite"
201
+ end
195
202
  Array(create_statement.call).each do |stmt|
196
203
  ::ActiveRecord::Base.connection.execute(stmt)
197
204
  end
198
205
  # have to disconnect and reconnect to the correct db
199
- shard.name = name
200
206
  if self.shareable? && other_shard
201
207
  other_shard.activate { ::ActiveRecord::Base.connection }
202
208
  else
203
209
  ::ActiveRecord::Base.connection_pool.current_pool.disconnect!
204
210
  end
205
- else
206
- shard.name = name
207
211
  end
208
212
  old_proc = ::ActiveRecord::Base.connection.raw_connection.set_notice_processor {} if config[:adapter] == 'postgresql'
209
213
  old_verbose = ::ActiveRecord::Migration.verbose
@@ -211,7 +215,10 @@ module Switchman
211
215
 
212
216
  unless create_schema == false
213
217
  reset_column_information
214
- migrate = -> { ::ActiveRecord::Migrator.migrate(::ActiveRecord::Migrator.migrations_paths) }
218
+
219
+ migrate = ::Rails.version >= '5.2' ?
220
+ -> { ::ActiveRecord::Base.connection.migration_context.migrate } :
221
+ -> { ::ActiveRecord::Migrator.migrate(::ActiveRecord::Migrator.migrations_paths) }
215
222
  if ::ActiveRecord::Base.connection.supports_ddl_transactions?
216
223
  ::ActiveRecord::Base.connection.transaction(requires_new: true, &migrate)
217
224
  else
@@ -226,13 +233,16 @@ module Switchman
226
233
  end
227
234
  end
228
235
  end
229
- shard.save!
230
236
  shard
231
237
  rescue
232
238
  shard.destroy
233
- shard.drop_database if shard.name == name rescue nil
239
+ unless schema_already_existed
240
+ shard.drop_database rescue nil
241
+ end
234
242
  reset_column_information unless create_schema == false rescue nil
235
243
  raise
244
+ ensure
245
+ self.class.creating_new_shard = false
236
246
  end
237
247
  end
238
248
 
@@ -1,8 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'switchman/database_server'
2
4
 
3
5
  module Switchman
4
6
  class DefaultShard
5
7
  def id; 'default'; end
8
+ alias cache_key id
6
9
  def activate(*categories); yield; end
7
10
  def activate!(*categories); end
8
11
  def default?; true; end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman
2
4
  class Engine < ::Rails::Engine
3
5
  isolate_namespace Switchman
4
6
 
5
- config.autoload_once_paths << File.expand_path(File.join(__FILE__, "../../../app/models"))
7
+ config.autoload_once_paths << File.expand_path("app/models", config.paths.path)
6
8
 
7
9
  def self.lookup_stores(cache_store_config)
8
10
  result = {}
@@ -72,6 +74,7 @@ module Switchman
72
74
  require "switchman/active_record/connection_pool"
73
75
  require "switchman/active_record/finder_methods"
74
76
  require "switchman/active_record/log_subscriber"
77
+ require "switchman/active_record/migration"
75
78
  require "switchman/active_record/model_schema"
76
79
  require "switchman/active_record/persistence"
77
80
  require "switchman/active_record/predicate_builder"
@@ -80,84 +83,78 @@ module Switchman
80
83
  require "switchman/active_record/reflection"
81
84
  require "switchman/active_record/relation"
82
85
  require "switchman/active_record/spawn_methods"
83
- require "switchman/active_record/where_clause_factory"
86
+ require "switchman/active_record/statement_cache"
84
87
  require "switchman/active_record/type_caster"
88
+ require "switchman/active_record/where_clause_factory"
85
89
  require "switchman/arel"
90
+ require "switchman/call_super"
86
91
  require "switchman/rails"
87
- require "switchman/shackles/relation"
88
- require "switchman/shard_internal"
92
+ require "switchman/guard_rail/relation"
93
+ require_dependency "switchman/shard"
89
94
  require "switchman/standard_error"
90
95
 
91
96
  ::StandardError.include(StandardError)
92
97
 
93
- include ActiveRecord::Base
98
+ prepend ActiveRecord::Base
94
99
  include ActiveRecord::AttributeMethods
95
100
  include ActiveRecord::Persistence
96
101
  singleton_class.prepend ActiveRecord::ModelSchema::ClassMethods
97
102
 
98
- if ::Rails.version > '4.2'
99
- require "switchman/active_record/statement_cache"
100
- ::ActiveRecord::StatementCache.prepend(ActiveRecord::StatementCache)
101
- ::ActiveRecord::StatementCache.singleton_class.prepend(ActiveRecord::StatementCache::ClassMethods)
102
- ::ActiveRecord::StatementCache::BindMap.prepend(ActiveRecord::StatementCache::BindMap)
103
- ::ActiveRecord::StatementCache::Substitute.send(:attr_accessor, :primary, :sharded)
103
+ ::ActiveRecord::StatementCache.prepend(ActiveRecord::StatementCache)
104
+ ::ActiveRecord::StatementCache.singleton_class.prepend(ActiveRecord::StatementCache::ClassMethods)
105
+ ::ActiveRecord::StatementCache::BindMap.prepend(ActiveRecord::StatementCache::BindMap)
106
+ ::ActiveRecord::StatementCache::Substitute.send(:attr_accessor, :primary, :sharded)
104
107
 
105
- ::ActiveRecord::Associations::CollectionAssociation.prepend(ActiveRecord::CollectionAssociation)
106
- end
108
+ ::ActiveRecord::Associations::CollectionAssociation.prepend(ActiveRecord::CollectionAssociation)
107
109
 
108
- if ::Rails.version >= '4.1.15'
109
- ::ActiveRecord::PredicateBuilder.singleton_class.prepend(ActiveRecord::PredicateBuilder)
110
- end
110
+ ::ActiveRecord::PredicateBuilder.singleton_class.prepend(ActiveRecord::PredicateBuilder)
111
111
 
112
- if ::Rails.version > '4.1'
113
- prepend(ActiveRecord::AutosaveAssociation)
114
- end
112
+ prepend(ActiveRecord::AutosaveAssociation)
115
113
 
116
114
  ::ActiveRecord::Associations::Association.prepend(ActiveRecord::Association)
117
115
  ::ActiveRecord::Associations::BelongsToAssociation.prepend(ActiveRecord::BelongsToAssociation)
118
116
  ::ActiveRecord::Associations::CollectionProxy.include(ActiveRecord::CollectionProxy)
119
- if ::Rails.version < '5'
120
- ::ActiveRecord::Associations::Builder::CollectionAssociation.include(ActiveRecord::Builder::CollectionAssociation)
121
- end
122
117
 
123
118
  ::ActiveRecord::Associations::Preloader::Association.prepend(ActiveRecord::Preloader::Association)
124
119
  ::ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(ActiveRecord::AbstractAdapter)
125
120
  ::ActiveRecord::ConnectionAdapters::ConnectionHandler.prepend(ActiveRecord::ConnectionHandler)
126
121
  ::ActiveRecord::ConnectionAdapters::ConnectionPool.prepend(ActiveRecord::ConnectionPool)
127
122
  ::ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(ActiveRecord::QueryCache)
128
- # when we call super in Switchman::ActiveRecord::QueryCache#select_all,
129
- # we want it to find the definition from
130
- # ActiveRecord::ConnectionAdapters::DatabaseStatements, not
131
- # ActiveRecord::ConnectionAdapters::QueryCache
132
- ::ActiveRecord::ConnectionAdapters::QueryCache.send(:remove_method, :select_all)
133
123
 
134
124
  ::ActiveRecord::LogSubscriber.prepend(ActiveRecord::LogSubscriber)
125
+ ::ActiveRecord::Migration.prepend(ActiveRecord::Migration)
126
+ ::ActiveRecord::Migration::Compatibility::V5_0.prepend(ActiveRecord::Migration::Compatibility::V5_0)
127
+ ::ActiveRecord::MigrationContext.prepend(ActiveRecord::MigrationContext) if ::Rails.version >= '5.2'
128
+ ::ActiveRecord::Migrator.prepend(ActiveRecord::Migrator)
129
+
130
+ ::ActiveRecord::Reflection::AbstractReflection.include(ActiveRecord::Reflection::AbstractReflection)
131
+ ::ActiveRecord::Reflection::AssociationReflection.prepend(ActiveRecord::Reflection::AssociationScopeCache)
132
+ ::ActiveRecord::Reflection::ThroughReflection.prepend(ActiveRecord::Reflection::AssociationScopeCache)
135
133
  ::ActiveRecord::Reflection::AssociationReflection.prepend(ActiveRecord::Reflection::AssociationReflection)
136
134
  ::ActiveRecord::Relation.prepend(ActiveRecord::Batches)
137
135
  ::ActiveRecord::Relation.prepend(ActiveRecord::Calculations)
138
136
  ::ActiveRecord::Relation.include(ActiveRecord::FinderMethods)
139
137
  ::ActiveRecord::Relation.include(ActiveRecord::QueryMethods)
138
+ ::ActiveRecord::Relation.prepend(GuardRail::Relation)
140
139
  ::ActiveRecord::Relation.prepend(ActiveRecord::Relation)
141
140
  ::ActiveRecord::Relation.include(ActiveRecord::SpawnMethods)
142
- ::ActiveRecord::Relation.prepend(Shackles::Relation)
141
+ ::ActiveRecord::Relation.include(CallSuper)
143
142
 
144
- if ::Rails.version >= '5'
145
- ::ActiveRecord::Relation::WhereClauseFactory.prepend(ActiveRecord::WhereClauseFactory)
146
- ::ActiveRecord::PredicateBuilder::AssociationQueryValue.prepend(ActiveRecord::PredicateBuilder::AssociationQueryValue)
147
- ::ActiveRecord::TypeCaster::Map.include(ActiveRecord::TypeCaster::Map)
148
- ::ActiveRecord::TypeCaster::Connection.include(ActiveRecord::TypeCaster::Connection)
149
- end
143
+ ::ActiveRecord::Relation::WhereClauseFactory.prepend(ActiveRecord::WhereClauseFactory)
144
+ ::ActiveRecord::PredicateBuilder::AssociationQueryValue.prepend(ActiveRecord::PredicateBuilder::AssociationQueryValue)
145
+ ::ActiveRecord::PredicateBuilder::PolymorphicArrayValue.prepend(ActiveRecord::PredicateBuilder::AssociationQueryValue)
146
+ ::ActiveRecord::TypeCaster::Map.include(ActiveRecord::TypeCaster::Map)
147
+ ::ActiveRecord::TypeCaster::Connection.include(ActiveRecord::TypeCaster::Connection)
150
148
 
151
149
  ::Rails.singleton_class.prepend(Rails::ClassMethods)
152
150
 
153
151
  ::Arel::Table.prepend(Arel::Table)
154
152
  ::Arel::Visitors::ToSql.prepend(Arel::Visitors::ToSql)
155
- ::Arel::Visitors::PostgreSQL.include(Arel::Visitors::PostgreSQL) if ::Rails.version < '4.2'
156
153
  end
157
154
  end
158
155
 
159
- def self.foreign_key_check(name, type, options)
160
- if name.to_s =~ /_id\z/ && type.to_s == 'integer' && options[:limit].to_i < 8
156
+ def self.foreign_key_check(name, type, limit: nil)
157
+ if name.to_s =~ /_id\z/ && type.to_s == 'integer' && limit.to_i < 8
161
158
  puts "WARNING: All foreign keys need to be 8-byte integers. #{name} looks like a foreign key. If so, please add the option: `:limit => 8`"
162
159
  end
163
160
  end
@@ -175,6 +172,12 @@ module Switchman
175
172
  require "switchman/active_record/postgresql_adapter"
176
173
  ::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(ActiveRecord::PostgreSQLAdapter)
177
174
  end
175
+
176
+ # If Switchman::Shard wasn't loaded as of when ActiveRecord::Base initialized
177
+ # establish a connection here instead
178
+ if !Shard.instance_variable_get(:@default)
179
+ ::ActiveRecord::Base.establish_connection
180
+ end
178
181
  end
179
182
  end
180
183
 
@@ -185,15 +188,15 @@ module Switchman
185
188
  end
186
189
  end
187
190
 
188
- initializer 'switchman.extend_shackles', :before => "switchman.extend_ar" do
191
+ initializer 'switchman.extend_guard_rail', :before => "switchman.extend_ar" do
189
192
  ::ActiveSupport.on_load(:active_record) do
190
- require "switchman/shackles"
193
+ require "switchman/guard_rail"
191
194
 
192
- ::Shackles.singleton_class.prepend(Shackles::ClassMethods)
195
+ ::GuardRail.singleton_class.prepend(GuardRail::ClassMethods)
193
196
  end
194
197
  end
195
198
 
196
- initializer 'switchman.extend_controller', :after => "shackles.extend_ar" do
199
+ initializer 'switchman.extend_controller', :after => "guard_rail.extend_ar" do
197
200
  ::ActiveSupport.on_load(:action_controller) do
198
201
  require "switchman/action_controller/caching"
199
202
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'etc'
2
4
 
3
5
  module Switchman
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman
2
4
  class NonExistentShardError < RuntimeError; end
3
5
  class ParallelShardExecError < RuntimeError; end
@@ -1,11 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman
2
- module Shackles
4
+ module GuardRail
3
5
  module Relation
4
6
  def exec_queries(*args)
5
7
  if self.lock_value
6
8
  db = Shard.current(shard_category).database_server
7
- if ::Shackles.environment != db.shackles_environment
8
- return db.unshackle { super }
9
+ if ::GuardRail.environment != db.guard_rail_environment
10
+ return db.unguard { super }
9
11
  end
10
12
  end
11
13
  super
@@ -15,8 +17,8 @@ module Switchman
15
17
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
16
18
  def #{method}(*args)
17
19
  db = Shard.current(shard_category).database_server
18
- if ::Shackles.environment != db.shackles_environment
19
- db.unshackle { super }
20
+ if ::GuardRail.environment != db.guard_rail_environment
21
+ db.unguard { super }
20
22
  else
21
23
  super
22
24
  end
@@ -1,17 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman
2
- module Shackles
4
+ module GuardRail
3
5
  module ClassMethods
4
6
  def self.prepended(klass)
5
7
  klass.send(:remove_method, :ensure_handler)
6
8
  end
7
9
 
8
10
  # drops the save_handler and ensure_handler calls from the vanilla
9
- # Shackles' implementation.
11
+ # GuardRail' implementation.
10
12
  def activate!(environment)
11
- environment ||= :master
13
+ environment ||= :primary
12
14
  activated_environments << environment
13
15
  old_environment = self.environment
14
- @environment = environment
16
+ Thread.current[:guard_rail_environment] = environment
15
17
  old_environment
16
18
  end
17
19
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'open4'
2
4
 
3
5
  # This fixes a bug with exception handling,
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "switchman/test_helper"
2
4
 
3
5
  module Switchman
@@ -36,6 +38,7 @@ module Switchman
36
38
 
37
39
  puts "Setting up sharding for all specs..."
38
40
  Shard.delete_all
41
+ Switchman.cache.delete("default_shard")
39
42
 
40
43
  @@shard1, @@shard2 = TestHelper.recreate_persistent_test_shards
41
44
  @@default_shard = Shard.default
@@ -64,14 +67,15 @@ module Switchman
64
67
  (@@shard3.drop_database if @@shard3) rescue nil
65
68
  @@shard1 = @@shard2 = @@shard3 = nil
66
69
  Shard.delete_all
67
- Shard.default(true)
70
+ Shard.default(reload: true)
68
71
  next
69
72
  end
70
73
  end
71
74
  # we'll re-persist in the group's `before :all`; we don't want them to exist
72
75
  # in the db before then
73
76
  Shard.delete_all
74
- Shard.default(true)
77
+ Switchman.cache.delete("default_shard")
78
+ Shard.default(reload: true)
75
79
  puts "Done!"
76
80
 
77
81
  at_exit do
@@ -99,7 +103,8 @@ module Switchman
99
103
  dup = @@default_shard.dup
100
104
  dup.id = @@default_shard.id
101
105
  dup.save!
102
- Shard.default(true)
106
+ Switchman.cache.delete("default_shard")
107
+ Shard.default(reload: true)
103
108
  dup = @@shard1.dup
104
109
  dup.id = @@shard1.id
105
110
  dup.save!
@@ -118,8 +123,8 @@ module Switchman
118
123
  klass.before do
119
124
  raise "Sharding did not set up correctly" if @@sharding_failed
120
125
  Shard.clear_cache
121
- if use_transactional_fixtures
122
- Shard.default(true)
126
+ if use_transactional_tests
127
+ Shard.default(reload: true)
123
128
  @shard1 = Shard.find(@shard1.id)
124
129
  @shard2 = Shard.find(@shard2.id)
125
130
  shards = [@shard2]
@@ -134,7 +139,7 @@ module Switchman
134
139
 
135
140
  klass.after do
136
141
  next if @@sharding_failed
137
- if use_transactional_fixtures
142
+ if use_transactional_tests
138
143
  shards = [@shard2]
139
144
  shards << @shard1 unless @shard1.database_server == Shard.default.database_server
140
145
  shards.each do |shard|
@@ -146,8 +151,9 @@ module Switchman
146
151
  end
147
152
 
148
153
  klass.after(:all) do
149
- Shard.delete_all
150
- Shard.default(true)
154
+ Shard.connection.update("TRUNCATE #{Shard.quoted_table_name} CASCADE")
155
+ Switchman.cache.delete("default_shard")
156
+ Shard.default(reload: true)
151
157
  end
152
158
  end
153
159
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman::Rails
2
4
  module ClassMethods
3
5
  def self.prepended(klass)
@@ -1,11 +1,28 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman
2
4
  class SchemaCache < ::ActiveRecord::ConnectionAdapters::SchemaCache
3
5
  delegate :connection, to: :pool
4
6
  attr_reader :pool
5
7
 
8
+ SHARED_IVS = %i{@columns @columns_hash @primary_keys @data_sources @indexes}.freeze
9
+
6
10
  def initialize(pool)
7
11
  @pool = pool
8
12
  super(nil)
9
13
  end
14
+
15
+ def copy_values(other_cache)
16
+ SHARED_IVS.each do |iv|
17
+ instance_variable_get(iv).replace(other_cache.instance_variable_get(iv))
18
+ end
19
+ end
20
+
21
+ def copy_references(other_cache)
22
+ # use the same cached values but still fall back to the correct pool
23
+ SHARED_IVS.each do |iv|
24
+ instance_variable_set(iv, other_cache.instance_variable_get(iv))
25
+ end
26
+ end
10
27
  end
11
28
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman
2
4
  class ShardedInstrumenter < ::SimpleDelegator
3
5
  def initialize(instrumenter, shard_host)
@@ -6,7 +8,7 @@ module Switchman
6
8
  end
7
9
 
8
10
  def instrument(name, payload={})
9
- shard = @shard_host.try(:shard)
11
+ shard = @shard_host&.shard
10
12
  # attribute_methods_generated? will be false during a reload -
11
13
  # when we might be doing a query while defining attribute methods,
12
14
  # so just avoid logging then
@@ -14,7 +16,7 @@ module Switchman
14
16
  payload[:shard] = {
15
17
  database_server_id: shard.database_server.id,
16
18
  id: shard.id,
17
- env: shard.database_server.shackles_environment
19
+ env: shard.database_server.guard_rail_environment
18
20
  }
19
21
  end
20
22
  super name, payload
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman
2
4
  module StandardError
3
5
  def initialize(*args)
@@ -5,8 +7,8 @@ module Switchman
5
7
  super
6
8
  end
7
9
 
8
- def current_shard(category = :default)
9
- @active_shards[category] || Shard.default
10
+ def current_shard(category = :primary)
11
+ @active_shards&.[](category) || Shard.default
10
12
  end
11
13
  end
12
14
  end