switchman 3.5.13 → 3.5.14

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
  SHA256:
3
- metadata.gz: c0ab0712ea0711e482d32ddf5ce6ec740b399b6dc8074ea86eaaa092c13bbdd1
4
- data.tar.gz: a3e43929c88727da33b2954966f7cf357bd2b8793eed33563f57908839e2d97d
3
+ metadata.gz: 243c4f20483016069c4c18efd47a64a8b2078596f254c1152f4f2a70555054f0
4
+ data.tar.gz: 332d8e5a2d5e119d1599e259f27fa1ba25e9f3139cc7504bd159aebcb730e724
5
5
  SHA512:
6
- metadata.gz: 67a005a7e156773576077ade7723e1500d291e782cdfb4af9662cef6ba2a217b2e0743aaea8a5e8bffc1777295baf465f9d7678f6d8fa6c681215b1ad9c5817a
7
- data.tar.gz: 7e72531b558a3fc0d447d63efeec37b5a0480ebe873ff2e76fb6334927aee10d5342a92b0be8336bd324da57c2df6ff154b5efb2d88b5cc0e9e7b16f60bc6c78
6
+ metadata.gz: 53604b1eedb8bd5ca453d617eb7e4c83ad5080bad4ccb2dff0cced4be2baaeccbe139d7b5936face689e2ccbd94cac338d39f7ecf2ace8a25f61f81fe47ed0c8
7
+ data.tar.gz: 5f9b8523159c6eeec8275838a825c9c911ad136ef33d32d362e80ce63008f130e7789f1863b851d9527c68591bcbcfa6aa95183f2b6cca5eef7e55096194abb4
@@ -57,7 +57,12 @@ module Switchman
57
57
  end
58
58
 
59
59
  def clear_query_caches_for_current_thread
60
- ::ActiveRecord::Base.connection_handler.connection_pool_list(:all).each do |pool|
60
+ pools = if ::Rails.version < "7.1"
61
+ ::ActiveRecord::Base.connection_handler.connection_pool_list
62
+ else
63
+ ::ActiveRecord::Base.connection_handler.connection_pool_list(:all)
64
+ end
65
+ pools.each do |pool|
61
66
  pool.connection(switch_shard: false).clear_query_cache if pool.active_connection?
62
67
  end
63
68
  end
@@ -10,6 +10,10 @@ module Switchman
10
10
  attr_accessor :creating_new_shard
11
11
  attr_reader :all_roles
12
12
 
13
+ include Enumerable
14
+
15
+ delegate :each, to: :all
16
+
13
17
  def all
14
18
  database_servers.values
15
19
  end
@@ -50,6 +54,10 @@ module Switchman
50
54
  all.each { |db| db.guard! if db.config[:prefer_secondary] }
51
55
  end
52
56
 
57
+ def regions
58
+ @regions ||= all.filter_map(&:region).uniq.sort
59
+ end
60
+
53
61
  private
54
62
 
55
63
  def reference_role(role)
@@ -140,6 +148,29 @@ module Switchman
140
148
  end
141
149
  end
142
150
 
151
+ def region
152
+ config[:region]
153
+ end
154
+
155
+ # @param region [String, Array<String>] the region(s) to check against
156
+ # @return true if the database server doesn't have a region, or it
157
+ # matches the specified region
158
+ def in_region?(region)
159
+ !self.region || (region.is_a?(Array) ? region.include?(self.region) : self.region == region)
160
+ end
161
+
162
+ # @return true if the database server doesn't have a region, Switchman is
163
+ # not configured with a region, or the database server's region matches
164
+ # Switchman's current region
165
+ def in_current_region?
166
+ unless instance_variable_defined?(:@in_current_region)
167
+ @in_current_region = !region ||
168
+ !Switchman.region ||
169
+ region == Switchman.region
170
+ end
171
+ @in_current_region
172
+ end
173
+
143
174
  # locks this db to a specific environment, except for
144
175
  # when doing writes (then it falls back to the current
145
176
  # value of GuardRail.environment)
@@ -6,6 +6,7 @@ module Switchman
6
6
  "default"
7
7
  end
8
8
  alias_method :cache_key, :id
9
+
9
10
  def activate(*_classes)
10
11
  yield
11
12
  end
@@ -57,6 +58,16 @@ module Switchman
57
58
  self
58
59
  end
59
60
 
61
+ def region; end
62
+
63
+ def in_region?(_region)
64
+ true
65
+ end
66
+
67
+ def in_current_region?
68
+ true
69
+ end
70
+
60
71
  def _dump(_depth)
61
72
  ""
62
73
  end
@@ -121,7 +121,7 @@ module Switchman
121
121
  next if @@sharding_failed
122
122
 
123
123
  # clean up after specs
124
- DatabaseServer.all.each do |ds|
124
+ DatabaseServer.each do |ds|
125
125
  if ds.fake? && ds != @shard2.database_server
126
126
  ds.shards.delete_all unless use_transactional_tests
127
127
  ds.destroy
@@ -13,6 +13,47 @@ module Switchman
13
13
 
14
14
  scope :primary, -> { where(name: nil).order(:database_server_id, :id).distinct_on(:database_server_id) }
15
15
 
16
+ scope :in_region, (lambda do |region, include_regionless: true|
17
+ next in_current_region if region.nil?
18
+
19
+ dbs_by_region = DatabaseServer.group_by(&:region)
20
+ db_count_in_this_region = dbs_by_region[region]&.length.to_i
21
+ db_count_in_this_region += dbs_by_region[nil]&.length.to_i if include_regionless
22
+ non_existent_database_servers = Shard.send(:non_existent_database_servers)
23
+ db_count_in_other_regions = DatabaseServer.all.length -
24
+ db_count_in_this_region +
25
+ non_existent_database_servers.length
26
+
27
+ dbs_in_this_region = dbs_by_region[region]&.map(&:id) || []
28
+ dbs_in_this_region += dbs_by_region[nil]&.map(&:id) || [] if include_regionless
29
+
30
+ if db_count_in_this_region <= db_count_in_other_regions
31
+ if dbs_in_this_region.include?(Shard.default.database_server.id)
32
+ where("database_server_id IN (?) OR database_server_id IS NULL", dbs_in_this_region)
33
+ else
34
+ where(database_server_id: dbs_in_this_region)
35
+ end
36
+ elsif db_count_in_other_regions.zero?
37
+ all
38
+ else
39
+ dbs_not_in_this_region = DatabaseServer.map(&:id) - dbs_in_this_region + non_existent_database_servers
40
+ if dbs_in_this_region.include?(Shard.default.database_server.id)
41
+ where("database_server_id NOT IN (?) OR database_server_id IS NULL", dbs_not_in_this_region)
42
+ else
43
+ where.not(database_server_id: dbs_not_in_this_region)
44
+ end
45
+ end
46
+ end)
47
+
48
+ scope :in_current_region, (lambda do |include_regionless: true|
49
+ # sharding isn't set up? maybe we're in tests, or a somehow degraded environment
50
+ # either way there's only one shard, and we always want to see it
51
+ return [default] unless default.is_a?(Switchman::Shard)
52
+ return all if !Switchman.region || DatabaseServer.none?(&:region)
53
+
54
+ in_region(Switchman.region, include_regionless: include_regionless)
55
+ end)
56
+
16
57
  class << self
17
58
  def sharded_models
18
59
  @sharded_models ||= [::ActiveRecord::Base, UnshardedRecord].freeze
@@ -395,7 +436,7 @@ module Switchman
395
436
  end
396
437
 
397
438
  def configure_connects_to
398
- full_connects_to_hash = DatabaseServer.all.to_h { |db| [db.id.to_sym, db.connects_to_hash] }
439
+ full_connects_to_hash = DatabaseServer.to_h { |db| [db.id.to_sym, db.connects_to_hash] }
399
440
  sharded_models.each do |klass|
400
441
  connects_to_hash = full_connects_to_hash.deep_dup
401
442
  if klass == UnshardedRecord
@@ -484,8 +525,17 @@ module Switchman
484
525
  argv[0] = File.basename(argv[0])
485
526
  argv.shelljoin
486
527
  end
528
+
529
+ # @return [Array<String>] the list of database servers that are in the
530
+ # config, but don't have any shards on them
531
+ def non_existent_database_servers
532
+ @non_existent_database_servers ||=
533
+ Shard.distinct.pluck(:database_server_id).compact - DatabaseServer.all.map(&:id)
534
+ end
487
535
  end
488
536
 
537
+ delegate :region, :in_region?, :in_current_region?, to: :database_server
538
+
489
539
  def name
490
540
  unless instance_variable_defined?(:@name)
491
541
  # protect against re-entrancy
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Switchman
4
- VERSION = "3.5.13"
4
+ VERSION = "3.5.14"
5
5
  end
data/lib/switchman.rb CHANGED
@@ -20,27 +20,31 @@ loader.setup
20
20
  module Switchman
21
21
  Deprecation = ::ActiveSupport::Deprecation.new("4.0", "Switchman")
22
22
 
23
- def self.config
24
- # TODO: load from yaml
25
- @config ||= {}
26
- end
23
+ class << self
24
+ attr_writer :cache
27
25
 
28
- def self.cache
29
- (@cache.respond_to?(:call) ? @cache.call : @cache) || ::Rails.cache
30
- end
26
+ def config
27
+ # TODO: load from yaml
28
+ @config ||= {}
29
+ end
31
30
 
32
- def self.cache=(cache)
33
- @cache = cache
34
- end
31
+ def cache
32
+ (@cache.respond_to?(:call) ? @cache.call : @cache) || ::Rails.cache
33
+ end
34
+
35
+ def region
36
+ config[:region]
37
+ end
35
38
 
36
- def self.foreign_key_check(name, type, limit: nil)
37
- return unless name.to_s.end_with?("_id") && type.to_s == "integer" && limit.to_i < 8
39
+ def foreign_key_check(name, type, limit: nil)
40
+ return unless name.to_s.end_with?("_id") && type.to_s == "integer" && limit.to_i < 8
38
41
 
39
- puts <<~TEXT.squish
40
- WARNING: All foreign keys need to be 8-byte integers.
41
- #{name} looks like a foreign key.
42
- If so, please add the option: `:limit => 8`
43
- TEXT
42
+ puts <<~TEXT.squish
43
+ WARNING: All foreign keys need to be 8-byte integers.
44
+ #{name} looks like a foreign key.
45
+ If so, please add the option: `:limit => 8`
46
+ TEXT
47
+ end
44
48
  end
45
49
 
46
50
  class OrderOnMultiShardQuery < RuntimeError; end
@@ -35,7 +35,7 @@ module Switchman
35
35
 
36
36
  servers = servers.filter_map { |server| DatabaseServer.find(server) }
37
37
  if open
38
- open_servers = DatabaseServer.all.select { |server| server.config[:open] }
38
+ open_servers = DatabaseServer.select { |server| server.config[:open] }
39
39
  servers.concat(open_servers)
40
40
  servers << DatabaseServer.find(nil) if open_servers.empty?
41
41
  servers.uniq!
@@ -43,6 +43,19 @@ module Switchman
43
43
  servers = DatabaseServer.all - servers if negative
44
44
  end
45
45
 
46
+ ENV["REGION"]&.split(",")&.each do |region|
47
+ method = :select!
48
+ if region[0] == "-"
49
+ method = :reject!
50
+ region = region[1..]
51
+ end
52
+ if region == "self"
53
+ servers.send(method, &:in_current_region?)
54
+ else
55
+ servers.send(method) { |server| server.in_region?(region) }
56
+ end
57
+ end
58
+
46
59
  servers = filter_database_servers_chain.call(servers)
47
60
 
48
61
  scope = base_scope.order(::Arel.sql("database_server_id IS NOT NULL, database_server_id, id"))
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.5.13
4
+ version: 3.5.14
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: 2023-10-05 00:00:00.000000000 Z
13
+ date: 2023-10-12 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord