switchman 3.5.13 → 3.5.14

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
  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