switchman 3.0.24 → 4.2.4
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 +4 -4
- data/Rakefile +16 -15
- data/db/migrate/20180828183945_add_default_shard_index.rb +1 -1
- data/db/migrate/20190114212900_add_unique_name_indexes.rb +10 -4
- data/lib/switchman/active_record/abstract_adapter.rb +11 -7
- data/lib/switchman/active_record/associations.rb +157 -50
- data/lib/switchman/active_record/attribute_methods.rb +192 -101
- data/lib/switchman/active_record/base.rb +136 -33
- data/lib/switchman/active_record/calculations.rb +91 -48
- data/lib/switchman/active_record/connection_handler.rb +18 -0
- data/lib/switchman/active_record/connection_pool.rb +41 -6
- data/lib/switchman/active_record/database_configurations.rb +23 -13
- data/lib/switchman/active_record/finder_methods.rb +22 -16
- data/lib/switchman/active_record/log_subscriber.rb +3 -6
- data/lib/switchman/active_record/migration.rb +42 -17
- data/lib/switchman/active_record/model_schema.rb +1 -1
- data/lib/switchman/active_record/pending_migration_connection.rb +17 -0
- data/lib/switchman/active_record/persistence.rb +32 -2
- data/lib/switchman/active_record/postgresql_adapter.rb +37 -22
- data/lib/switchman/active_record/predicate_builder.rb +2 -2
- data/lib/switchman/active_record/query_cache.rb +26 -17
- data/lib/switchman/active_record/query_methods.rb +249 -142
- data/lib/switchman/active_record/reflection.rb +10 -3
- data/lib/switchman/active_record/relation.rb +103 -32
- data/lib/switchman/active_record/spawn_methods.rb +3 -7
- data/lib/switchman/active_record/statement_cache.rb +13 -9
- data/lib/switchman/active_record/table_definition.rb +1 -1
- data/lib/switchman/active_record/tasks/database_tasks.rb +6 -1
- data/lib/switchman/active_record/test_fixtures.rb +71 -25
- data/lib/switchman/active_support/cache.rb +9 -4
- data/lib/switchman/arel.rb +16 -25
- data/lib/switchman/call_super.rb +2 -2
- data/lib/switchman/database_server.rb +68 -34
- data/lib/switchman/default_shard.rb +14 -3
- data/lib/switchman/engine.rb +36 -19
- data/lib/switchman/environment.rb +2 -2
- data/lib/switchman/errors.rb +13 -0
- data/lib/switchman/guard_rail/relation.rb +3 -3
- data/lib/switchman/parallel.rb +6 -6
- data/lib/switchman/r_spec_helper.rb +12 -11
- data/lib/switchman/shard.rb +182 -72
- data/lib/switchman/sharded_instrumenter.rb +9 -3
- data/lib/switchman/shared_schema_cache.rb +11 -0
- data/lib/switchman/standard_error.rb +4 -0
- data/lib/switchman/test_helper.rb +3 -3
- data/lib/switchman/version.rb +1 -1
- data/lib/switchman.rb +27 -15
- data/lib/tasks/switchman.rake +96 -60
- metadata +18 -168
|
@@ -7,12 +7,12 @@ module Switchman
|
|
|
7
7
|
# since all should point to the same data, even if multiple are writable
|
|
8
8
|
# (Picks 'primary' since it is guaranteed to exist and switchman handles activating
|
|
9
9
|
# deploy through other means)
|
|
10
|
-
def configs_for(
|
|
10
|
+
def configs_for(include_hidden: false, name: nil, **)
|
|
11
11
|
res = super
|
|
12
|
-
if name && !
|
|
13
|
-
return nil unless name.end_with?(
|
|
14
|
-
elsif !
|
|
15
|
-
return res.select { |config| config.name.end_with?(
|
|
12
|
+
if name && !include_hidden
|
|
13
|
+
return nil unless name.end_with?("primary")
|
|
14
|
+
elsif !include_hidden
|
|
15
|
+
return res.select { |config| config.name.end_with?("primary") }
|
|
16
16
|
end
|
|
17
17
|
res
|
|
18
18
|
end
|
|
@@ -27,21 +27,31 @@ module Switchman
|
|
|
27
27
|
return configs if configs.is_a?(Array)
|
|
28
28
|
|
|
29
29
|
db_configs = configs.flat_map do |env_name, config|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
if config.is_a?(Hash)
|
|
31
|
+
# It would be nice to do the auto-fallback that we want here, but we haven't
|
|
32
|
+
# actually done that for years (or maybe ever) and it will be a big lift to get working
|
|
33
|
+
roles = config.keys.select do |k|
|
|
34
|
+
config[k].is_a?(Hash) || (config[k].is_a?(Array) && config[k].all?(Hash))
|
|
35
|
+
end
|
|
36
|
+
base_config = config.except(*roles)
|
|
37
|
+
else
|
|
38
|
+
base_config = config
|
|
39
|
+
roles = []
|
|
40
|
+
end
|
|
34
41
|
|
|
35
42
|
name = "#{env_name}/primary"
|
|
36
|
-
name =
|
|
43
|
+
name = "primary" if env_name == default_env
|
|
37
44
|
base_db = build_db_config_from_raw_config(env_name, name, base_config)
|
|
38
45
|
[base_db] + roles.map do |role|
|
|
39
|
-
build_db_config_from_raw_config(
|
|
40
|
-
|
|
46
|
+
build_db_config_from_raw_config(
|
|
47
|
+
env_name,
|
|
48
|
+
"#{env_name}/#{role}",
|
|
49
|
+
base_config.merge(config[role].is_a?(Array) ? config[role].first : config[role])
|
|
50
|
+
)
|
|
41
51
|
end
|
|
42
52
|
end
|
|
43
53
|
|
|
44
|
-
db_configs << environment_url_config(default_env,
|
|
54
|
+
db_configs << environment_url_config(default_env, "primary", {}) unless db_configs.find(&:for_current_env?)
|
|
45
55
|
|
|
46
56
|
merge_db_environment_variables(default_env, db_configs.compact)
|
|
47
57
|
end
|
|
@@ -4,10 +4,10 @@ module Switchman
|
|
|
4
4
|
module ActiveRecord
|
|
5
5
|
module FinderMethods
|
|
6
6
|
def find_one(id)
|
|
7
|
-
return super
|
|
7
|
+
return super unless klass.integral_id?
|
|
8
8
|
|
|
9
9
|
if shard_source_value != :implicit
|
|
10
|
-
current_shard = Shard.current(klass.
|
|
10
|
+
current_shard = Shard.current(klass.connection_class_for_self)
|
|
11
11
|
result = activate do |relation, shard|
|
|
12
12
|
current_id = Shard.relative_id_for(id, current_shard, shard)
|
|
13
13
|
# current_id will be nil for non-integral id
|
|
@@ -28,14 +28,14 @@ module Switchman
|
|
|
28
28
|
if shard
|
|
29
29
|
shard.activate { super(local_id) }
|
|
30
30
|
else
|
|
31
|
-
super
|
|
31
|
+
super
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
def find_some_ordered(ids)
|
|
36
|
-
current_shard = Shard.current(klass.
|
|
36
|
+
current_shard = Shard.current(klass.connection_class_for_self)
|
|
37
37
|
ids = ids.map { |id| Shard.relative_id_for(id, current_shard, current_shard) }
|
|
38
|
-
super
|
|
38
|
+
super
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
def find_or_instantiator_by_attributes(match, attributes, *args)
|
|
@@ -43,23 +43,29 @@ module Switchman
|
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
def exists?(conditions = :none)
|
|
46
|
-
|
|
47
|
-
return false unless conditions
|
|
46
|
+
return false if @none
|
|
48
47
|
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
if Base === conditions
|
|
49
|
+
raise ArgumentError, <<-TEXT.squish
|
|
50
|
+
You are passing an instance of ActiveRecord::Base to `exists?`.
|
|
51
|
+
Please pass the id of the object by calling `.id`.
|
|
52
|
+
TEXT
|
|
53
|
+
end
|
|
51
54
|
|
|
52
|
-
|
|
55
|
+
return false if !conditions || limit_value == 0 # rubocop:disable Style/NumericPredicate
|
|
53
56
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
else
|
|
58
|
-
relation = relation.where(table[primary_key].eq(conditions)) if conditions != :none
|
|
57
|
+
if eager_loading?
|
|
58
|
+
relation = apply_join_dependency(eager_loading: false)
|
|
59
|
+
return relation.exists?(conditions)
|
|
59
60
|
end
|
|
60
61
|
|
|
62
|
+
relation = construct_relation_for_exists(conditions)
|
|
63
|
+
return false if relation.where_clause.contradiction?
|
|
64
|
+
|
|
61
65
|
relation.activate do |shard_rel|
|
|
62
|
-
return true if
|
|
66
|
+
return true if skip_query_cache_if_necessary do
|
|
67
|
+
connection.select_rows(shard_rel.arel, "#{name} Exists?").size == 1
|
|
68
|
+
end
|
|
63
69
|
end
|
|
64
70
|
false
|
|
65
71
|
end
|
|
@@ -5,29 +5,26 @@ module Switchman
|
|
|
5
5
|
module LogSubscriber
|
|
6
6
|
# sadly, have to completely replace this
|
|
7
7
|
def sql(event)
|
|
8
|
-
self.class.runtime += event.duration
|
|
9
|
-
return unless logger.debug?
|
|
10
|
-
|
|
11
8
|
payload = event.payload
|
|
12
9
|
|
|
13
10
|
return if ::ActiveRecord::LogSubscriber::IGNORE_PAYLOAD_NAMES.include?(payload[:name])
|
|
14
11
|
|
|
15
12
|
name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
|
|
16
13
|
name = "CACHE #{name}" if payload[:cached]
|
|
17
|
-
sql = payload[:sql].squeeze(
|
|
14
|
+
sql = payload[:sql].squeeze(" ")
|
|
18
15
|
binds = nil
|
|
19
16
|
shard = payload[:shard]
|
|
20
17
|
shard = " [#{shard[:database_server_id]}:#{shard[:id]} #{shard[:env]}]" if shard
|
|
21
18
|
|
|
22
19
|
unless (payload[:binds] || []).empty?
|
|
23
20
|
casted_params = type_casted_binds(payload[:type_casted_binds])
|
|
24
|
-
binds =
|
|
21
|
+
binds = " " + payload[:binds].zip(casted_params).map do |attr, value|
|
|
25
22
|
render_bind(attr, value)
|
|
26
23
|
end.inspect
|
|
27
24
|
end
|
|
28
25
|
|
|
29
26
|
name = colorize_payload_name(name, payload[:name])
|
|
30
|
-
sql = color(sql, sql_color(sql), true)
|
|
27
|
+
sql = color(sql, sql_color(sql), bold: true)
|
|
31
28
|
|
|
32
29
|
debug " #{name} #{sql}#{binds}#{shard}"
|
|
33
30
|
end
|
|
@@ -14,41 +14,66 @@ module Switchman
|
|
|
14
14
|
|
|
15
15
|
def connection
|
|
16
16
|
conn = super
|
|
17
|
-
|
|
17
|
+
if conn.shard != ::ActiveRecord::Base.current_switchman_shard
|
|
18
|
+
::ActiveRecord::Base.connection_pool.switch_database(conn)
|
|
19
|
+
end
|
|
18
20
|
conn
|
|
19
21
|
end
|
|
20
22
|
end
|
|
21
23
|
|
|
22
24
|
module Migrator
|
|
23
|
-
# significant change:
|
|
24
|
-
#
|
|
25
|
-
# name you're accessing may not be consistent
|
|
26
|
-
# to run migrations against multiple shards in the same database
|
|
27
|
-
# concurrently
|
|
25
|
+
# significant change: use the shard name instead of the database name
|
|
26
|
+
# in the lock id. Especially if you're going through pgbouncer, the
|
|
27
|
+
# database name you're accessing may not be consistent
|
|
28
28
|
def generate_migrator_advisory_lock_id
|
|
29
|
-
|
|
29
|
+
db_name_hash = Zlib.crc32(Shard.current.name)
|
|
30
|
+
shard_name_hash = ::ActiveRecord::Migrator::MIGRATOR_SALT * db_name_hash
|
|
31
|
+
# Store in internalmetadata to allow other tools to be able to lock out migrations
|
|
32
|
+
@internal_metadata[:migrator_advisory_lock_id] = shard_name_hash.to_s
|
|
33
|
+
shard_name_hash
|
|
30
34
|
end
|
|
31
35
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
pool = ::ActiveRecord::ConnectionAdapters::ConnectionHandler.new.establish_connection(
|
|
35
|
-
::ActiveRecord::Base.connection_db_config.configuration_hash.except(:prefer_secondary)
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
pool.with_connection { |connection| yield(connection) } # rubocop:disable Style/ExplicitBlockArgument
|
|
39
|
-
ensure
|
|
40
|
-
pool&.disconnect!
|
|
36
|
+
def use_advisory_lock?
|
|
37
|
+
super && pending_migrations.any?
|
|
41
38
|
end
|
|
42
39
|
end
|
|
43
40
|
|
|
44
41
|
module MigrationContext
|
|
42
|
+
def migrate(...)
|
|
43
|
+
schema_cache_holder = ::ActiveRecord::Base.connection_pool
|
|
44
|
+
schema_cache_holder = schema_cache_holder.schema_reflection
|
|
45
|
+
previous_schema_cache = schema_cache_holder.instance_variable_get(:@cache)
|
|
46
|
+
|
|
47
|
+
schema_cache_holder.instance_variable_get(:@cache)
|
|
48
|
+
|
|
49
|
+
reset_column_information
|
|
50
|
+
schema_cache_holder.clear!
|
|
51
|
+
|
|
52
|
+
begin
|
|
53
|
+
super
|
|
54
|
+
ensure
|
|
55
|
+
if ::Rails.version < "7.2"
|
|
56
|
+
schema_cache_holder.set_schema_cache(previous_schema_cache)
|
|
57
|
+
else
|
|
58
|
+
schema_cache_holder.instance_variable_set(:@cache, previous_schema_cache)
|
|
59
|
+
end
|
|
60
|
+
reset_column_information
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
45
64
|
def migrations
|
|
46
65
|
return @migrations if instance_variable_defined?(:@migrations)
|
|
47
66
|
|
|
48
67
|
migrations_cache = Thread.current[:migrations_cache] ||= {}
|
|
49
|
-
key = Digest::MD5.hexdigest(migration_files.sort.join(
|
|
68
|
+
key = Digest::MD5.hexdigest(migration_files.sort.join(","))
|
|
50
69
|
@migrations = migrations_cache[key] ||= super
|
|
51
70
|
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def reset_column_information
|
|
75
|
+
::ActiveRecord::Base.descendants.reject { |m| m <= UnshardedRecord }.each(&:reset_column_information)
|
|
76
|
+
end
|
|
52
77
|
end
|
|
53
78
|
end
|
|
54
79
|
end
|
|
@@ -6,7 +6,7 @@ module Switchman
|
|
|
6
6
|
module ClassMethods
|
|
7
7
|
def quoted_table_name
|
|
8
8
|
@quoted_table_name ||= {}
|
|
9
|
-
@quoted_table_name[Shard.current(
|
|
9
|
+
@quoted_table_name[Shard.current(connection_class_for_self).id] ||= connection.quote_table_name(table_name)
|
|
10
10
|
end
|
|
11
11
|
end
|
|
12
12
|
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Switchman
|
|
4
|
+
module ActiveRecord
|
|
5
|
+
module PendingMigrationConnection
|
|
6
|
+
module ClassMethods
|
|
7
|
+
def current_role
|
|
8
|
+
::ActiveRecord::Base.current_role
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def current_switchman_shard
|
|
12
|
+
::ActiveRecord::Base.current_switchman_shard
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -5,17 +5,47 @@ module Switchman
|
|
|
5
5
|
module Persistence
|
|
6
6
|
# touch reads the id attribute directly, so it's not relative to the current shard
|
|
7
7
|
def touch(*, **)
|
|
8
|
-
|
|
8
|
+
writable_shadow_record_warning
|
|
9
|
+
shard.activate(self.class.connection_class_for_self) { super }
|
|
9
10
|
end
|
|
10
11
|
|
|
11
12
|
def update_columns(*)
|
|
12
|
-
|
|
13
|
+
writable_shadow_record_warning
|
|
14
|
+
shard.activate(self.class.connection_class_for_self) { super }
|
|
13
15
|
end
|
|
14
16
|
|
|
15
17
|
def delete
|
|
16
18
|
db = shard.database_server
|
|
17
19
|
db.unguard { super }
|
|
18
20
|
end
|
|
21
|
+
|
|
22
|
+
def destroy
|
|
23
|
+
writable_shadow_record_warning
|
|
24
|
+
super
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def create_or_update(**, &)
|
|
28
|
+
writable_shadow_record_warning
|
|
29
|
+
super
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def reload(*)
|
|
33
|
+
res = super
|
|
34
|
+
# When a shadow record is reloaded the real record is returned. So
|
|
35
|
+
# we need to ensure the loaded_from_shard is set correctly after a reload.
|
|
36
|
+
@loaded_from_shard = @shard
|
|
37
|
+
if @readonly_from_shadow
|
|
38
|
+
@readonly_from_shadow = false
|
|
39
|
+
@readonly = false
|
|
40
|
+
end
|
|
41
|
+
res
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def writable_shadow_record_warning
|
|
45
|
+
return unless shadow_record? && Switchman.config[:writable_shadow_records]
|
|
46
|
+
|
|
47
|
+
Switchman::Deprecation.warn("writing to shadow records is not supported")
|
|
48
|
+
end
|
|
19
49
|
end
|
|
20
50
|
end
|
|
21
51
|
end
|
|
@@ -5,9 +5,9 @@ module Switchman
|
|
|
5
5
|
module PostgreSQLAdapter
|
|
6
6
|
# copy/paste; use quote_local_table_name
|
|
7
7
|
def create_database(name, options = {})
|
|
8
|
-
options = { encoding:
|
|
8
|
+
options = { encoding: "utf8" }.merge!(options.symbolize_keys)
|
|
9
9
|
|
|
10
|
-
option_string = options.sum do |key, value|
|
|
10
|
+
option_string = options.sum("") do |key, value|
|
|
11
11
|
case key
|
|
12
12
|
when :owner
|
|
13
13
|
" OWNER = \"#{value}\""
|
|
@@ -24,7 +24,7 @@ module Switchman
|
|
|
24
24
|
when :connection_limit
|
|
25
25
|
" CONNECTION LIMIT = #{value}"
|
|
26
26
|
else
|
|
27
|
-
|
|
27
|
+
""
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
30
|
|
|
@@ -37,7 +37,7 @@ module Switchman
|
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
def current_schemas
|
|
40
|
-
select_values(
|
|
40
|
+
select_values("SELECT * FROM unnest(current_schemas(false))")
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
def extract_schema_qualified_name(string)
|
|
@@ -49,13 +49,13 @@ module Switchman
|
|
|
49
49
|
# significant change: use the shard name if no explicit schema
|
|
50
50
|
def quoted_scope(name = nil, type: nil)
|
|
51
51
|
schema, name = extract_schema_qualified_name(name)
|
|
52
|
-
type =
|
|
52
|
+
type =
|
|
53
53
|
case type # rubocop:disable Style/HashLikeCase
|
|
54
|
-
when
|
|
54
|
+
when "BASE TABLE"
|
|
55
55
|
"'r','p'"
|
|
56
|
-
when
|
|
56
|
+
when "VIEW"
|
|
57
57
|
"'v','m'"
|
|
58
|
-
when
|
|
58
|
+
when "FOREIGN TABLE"
|
|
59
59
|
"'f'"
|
|
60
60
|
end
|
|
61
61
|
scope = {}
|
|
@@ -67,28 +67,43 @@ module Switchman
|
|
|
67
67
|
|
|
68
68
|
def foreign_keys(table_name)
|
|
69
69
|
super.each do |fk|
|
|
70
|
-
to_table_qualified_name =
|
|
70
|
+
to_table_qualified_name =
|
|
71
|
+
::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(fk.to_table)
|
|
71
72
|
fk.to_table = to_table_qualified_name.identifier if to_table_qualified_name.schema == shard.name
|
|
72
73
|
end
|
|
73
74
|
end
|
|
74
75
|
|
|
76
|
+
module ClassMethods
|
|
77
|
+
def quote_local_table_name(name)
|
|
78
|
+
# postgres quotes tables and columns the same; just pass through
|
|
79
|
+
# (differs from quote_table_name_with_shard below by no logic to
|
|
80
|
+
# explicitly qualify the table)
|
|
81
|
+
quote_column_name(name)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def quote_table_name(name, shard: nil)
|
|
85
|
+
# This looks kind of weird at first glance, but older Rails versions do not actually import
|
|
86
|
+
# these methods as class methods.
|
|
87
|
+
shard = self.shard if shard.nil? && ::Rails.version < "7.2" && !@use_local_table_name
|
|
88
|
+
|
|
89
|
+
return quote_local_table_name(name) unless shard
|
|
90
|
+
|
|
91
|
+
name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(name.to_s)
|
|
92
|
+
name.instance_variable_set(:@schema, shard.name) unless name.schema
|
|
93
|
+
name.quoted
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
75
97
|
def quote_local_table_name(name)
|
|
76
|
-
|
|
77
|
-
# (differs from quote_table_name_with_shard below by no logic to
|
|
78
|
-
# explicitly qualify the table)
|
|
79
|
-
quote_column_name(name)
|
|
98
|
+
self.class.quote_local_table_name(name)
|
|
80
99
|
end
|
|
81
100
|
|
|
82
101
|
def quote_table_name(name)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(name.to_s)
|
|
86
|
-
name.instance_variable_set(:@schema, shard.name) unless name.schema
|
|
87
|
-
name.quoted
|
|
102
|
+
self.class.quote_table_name(name, shard: @use_local_table_name ? nil : shard)
|
|
88
103
|
end
|
|
89
104
|
|
|
90
|
-
def with_global_table_name(&
|
|
91
|
-
with_local_table_name(false, &
|
|
105
|
+
def with_global_table_name(&)
|
|
106
|
+
with_local_table_name(false, &)
|
|
92
107
|
end
|
|
93
108
|
|
|
94
109
|
def with_local_table_name(enable = true) # rubocop:disable Style/OptionalBooleanParameter
|
|
@@ -101,7 +116,7 @@ module Switchman
|
|
|
101
116
|
|
|
102
117
|
def add_index_options(_table_name, _column_name, **)
|
|
103
118
|
index, algorithm, if_not_exists = super
|
|
104
|
-
algorithm = nil if DatabaseServer.creating_new_shard && algorithm ==
|
|
119
|
+
algorithm = nil if DatabaseServer.creating_new_shard && algorithm == "CONCURRENTLY"
|
|
105
120
|
[index, algorithm, if_not_exists]
|
|
106
121
|
end
|
|
107
122
|
|
|
@@ -128,7 +143,7 @@ module Switchman
|
|
|
128
143
|
end
|
|
129
144
|
|
|
130
145
|
def columns(*)
|
|
131
|
-
|
|
146
|
+
with_global_table_name { super }
|
|
132
147
|
end
|
|
133
148
|
end
|
|
134
149
|
end
|
|
@@ -8,27 +8,36 @@ module Switchman
|
|
|
8
8
|
def cache_sql(sql, name, binds)
|
|
9
9
|
# have to include the shard id in the cache key because of switching dbs on the same connection
|
|
10
10
|
sql = "#{shard.id}::#{sql}"
|
|
11
|
+
key = binds.empty? ? sql : [sql, binds]
|
|
12
|
+
result = nil
|
|
13
|
+
hit = false
|
|
14
|
+
|
|
11
15
|
@lock.synchronize do
|
|
12
|
-
|
|
13
|
-
if query_cache
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
binds: binds,
|
|
17
|
-
name: name,
|
|
18
|
-
connection_id: object_id,
|
|
19
|
-
cached: true,
|
|
20
|
-
type_casted_binds: -> { type_casted_binds(binds) }
|
|
21
|
-
}
|
|
22
|
-
::ActiveSupport::Notifications.instrument(
|
|
23
|
-
'sql.active_record',
|
|
24
|
-
args
|
|
25
|
-
)
|
|
26
|
-
query_cache[sql][binds]
|
|
16
|
+
if ::Rails.version < "7.2"
|
|
17
|
+
if (result = @query_cache.delete(key))
|
|
18
|
+
hit = true
|
|
19
|
+
@query_cache[key] = result
|
|
27
20
|
else
|
|
28
|
-
query_cache[
|
|
21
|
+
result = @query_cache[key] = yield
|
|
22
|
+
@query_cache.shift if @query_cache_max_size && @query_cache.size > @query_cache_max_size
|
|
23
|
+
end
|
|
24
|
+
else
|
|
25
|
+
hit = true
|
|
26
|
+
result = @query_cache.compute_if_absent(key) do
|
|
27
|
+
hit = false
|
|
28
|
+
yield
|
|
29
29
|
end
|
|
30
|
-
|
|
30
|
+
end
|
|
31
31
|
end
|
|
32
|
+
|
|
33
|
+
if hit
|
|
34
|
+
::ActiveSupport::Notifications.instrument(
|
|
35
|
+
"sql.active_record",
|
|
36
|
+
cache_notification_info(sql, name, binds)
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
result.dup
|
|
32
41
|
end
|
|
33
42
|
end
|
|
34
43
|
end
|