Skip to content
Snippets Groups Projects
Commit 78e65338 authored by Kamil Trzcińśki's avatar Kamil Trzcińśki
Browse files

Clearly define workflows when using replicas

This replaces other load balancing code
with a set of clearly defined workflows:

- `read_only`: we intent do only read data, disallowing writes
- `isolated_write`: we intent to perform isolated write
parent 81299584
No related branches found
No related tags found
No related merge requests found
Showing with 81 additions and 569 deletions
Loading
Loading
@@ -53,7 +53,7 @@ def self.safe_find_or_create_by!(*args, &block)
# Start a new transaction with a shorter-than-usual statement timeout. This is
# currently one third of the default 15-second timeout
def self.with_fast_read_statement_timeout(timeout_ms = 5000)
::Gitlab::Database::LoadBalancing::Session.current.fallback_to_replicas_for_ambiguous_queries do
::Gitlab::Database::LoadBalancing::Session.read_only do
transaction(requires_new: true) do
connection.exec_query("SET LOCAL statement_timeout = #{timeout_ms}")
 
Loading
Loading
Loading
Loading
@@ -372,7 +372,7 @@ def heartbeat(values)
# not want to upgrade database connection proxy to use the primary
# database after heartbeat write happens.
#
::Gitlab::Database::LoadBalancing::Session.without_sticky_writes do
::Gitlab::Database::LoadBalancing::Session.isolated_write do
values = values&.slice(:version, :revision, :platform, :architecture, :ip_address, :config) || {}
values[:contacted_at] = Time.current
 
Loading
Loading
Loading
Loading
@@ -20,7 +20,7 @@ def jira_dvcs_integration_field(cloud: true)
end
 
def log_jira_dvcs_integration_usage(cloud: true)
::Gitlab::Database::LoadBalancing::Session.without_sticky_writes do
::Gitlab::Database::LoadBalancing::Session.isolated_write do
integration_field = self.class.jira_dvcs_integration_field(cloud: cloud)
 
# The feature usage is used only once later to query the feature usage in a
Loading
Loading
Loading
Loading
@@ -137,13 +137,12 @@ def each_build(params, &blk)
 
def retrieve_queue(queue_query_proc)
##
# We want to reset a load balancing session to discard the side
# effects of writes that could have happened prior to this moment.
# We want to ensure that retrieving queue uses read only replicas
#
::Gitlab::Database::LoadBalancing::Session.clear_session
@metrics.observe_queue_time(:retrieve, @runner.runner_type) do
queue_query_proc.call
::Gitlab::Database::LoadBalancing::Session.read_only do
@metrics.observe_queue_time(:retrieve, @runner.runner_type) do
queue_query_proc.call
end
end
end
 
Loading
Loading
Loading
Loading
@@ -17,7 +17,7 @@ def initialize(author)
def execute
return unless @user
 
::Gitlab::Database::LoadBalancing::Session.without_sticky_writes { record_activity }
::Gitlab::Database::LoadBalancing::Session.isolated_write { record_activity }
end
 
private
Loading
Loading
Loading
Loading
@@ -167,17 +167,14 @@ By default, queries use read-only replicas, but due to
primary for some time and reverts to secondaries after they have either caught up or after 30 seconds.
Doing this can lead to a considerable amount of unnecessary load on the primary.
To prevent switching to the primary [merge request 56849](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56849) introduced the
`without_sticky_writes` block. Typically, this method can be applied to prevent primary stickiness
`isolated_write` block. Typically, this method can be applied to prevent primary stickiness
after a trivial or insignificant write which doesn't affect the following queries in the same session.
 
To learn when a usage timestamp update can lead the session to stick to the primary and how to
prevent it by using `without_sticky_writes`, see [merge request 57328](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57328)
As a counterpart of the `without_sticky_writes` utility,
[merge request 59167](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59167) introduced
`use_replicas_for_read_queries`. This method forces all read-only queries inside its block to read
replicas regardless of the current primary stickiness.
This utility is reserved for cases where queries can tolerate replication lag.
As a counterpart of the `isolated_write` utility we have `read_only`.
This method only allows to perform read-only queries inside.
The queries are by default executed again `replicas` and does not affect
primary stickiness. This utility is reserved for cases where queries
can tolerate replication lag.
 
Internally, our database load balancer classifies the queries based on their main statement (`select`, `update`, `delete`, etc.). When in doubt, it redirects the queries to the primary database. Hence, there are some common cases the load balancer sends the queries to the primary unnecessarily:
 
Loading
Loading
Loading
Loading
@@ -18,7 +18,7 @@ def merge_requests_count
@merge_requests_count ||=
# We want to make sure the load of the following query
# lands on the read replica instead of the primary db
current_load_balancing_session.use_replicas_for_read_queries do
current_load_balancing_session.read_only do
count_service.new(@group, @current_user, params).count
end
end
Loading
Loading
Loading
Loading
@@ -11,7 +11,6 @@ module LoadBalancing
# the right load balancer pool, depending on the type of query.
class ConnectionProxy
WriteInsideReadOnlyTransactionError = Class.new(StandardError)
READ_ONLY_TRANSACTION_KEY = :load_balacing_read_only_transaction
 
attr_reader :load_balancer
 
Loading
Loading
@@ -33,6 +32,8 @@ class ConnectionProxy
quote_column_name
).freeze
 
ALLOWED_EXEC_QUERY = %r{^SET }
# hosts - The hosts to use for load balancing.
def initialize(hosts = [])
@load_balancer = LoadBalancer.new(hosts)
Loading
Loading
@@ -60,33 +61,33 @@ def select_all(arel, name = nil, binds = [], preparable: nil)
end
end
 
def exec_query(*args, &block)
# first param of args is SQL
if ALLOWED_EXEC_QUERY.match?(args.first.match)
read_using_load_balancer(:exec_query, args, &block)
else
write_using_load_balancer(:exec_query, args, &block)
end
end
def transaction(*args, &block)
if current_session.fallback_to_replicas_for_ambiguous_queries?
track_read_only_transaction!
if current_session.transaction_using_replica?
read_using_load_balancer(:transaction, args, &block)
else
write_using_load_balancer(:transaction, args, sticky: true, &block)
end
ensure
untrack_read_only_transaction!
end
 
# Delegates all unknown messages to a read-write connection.
def method_missing(name, *args, &block)
if current_session.fallback_to_replicas_for_ambiguous_queries?
read_using_load_balancer(name, args, &block)
else
write_using_load_balancer(name, args, &block)
end
write_using_load_balancer(name, args, &block)
end
 
# Performs a read using the load balancer.
#
# name - The name of the method to call on a connection object.
def read_using_load_balancer(name, args, &block)
if current_session.use_primary? &&
!current_session.use_replicas_for_read_queries?
if current_session.use_primary?
@load_balancer.read_write do |connection|
connection.send(name, *args, &block)
end
Loading
Loading
@@ -103,7 +104,7 @@ def read_using_load_balancer(name, args, &block)
# sticky - If set to true the session will stick to the master after
# the write.
def write_using_load_balancer(name, args, sticky: false, &block)
if read_only_transaction?
if current_session.read_only?
raise WriteInsideReadOnlyTransactionError, 'A write query is performed inside a read-only transaction'
end
 
Loading
Loading
@@ -122,18 +123,6 @@ def write_using_load_balancer(name, args, sticky: false, &block)
def current_session
::Gitlab::Database::LoadBalancing::Session.current
end
def track_read_only_transaction!
Thread.current[READ_ONLY_TRANSACTION_KEY] = true
end
def untrack_read_only_transaction!
Thread.current[READ_ONLY_TRANSACTION_KEY] = nil
end
def read_only_transaction?
Thread.current[READ_ONLY_TRANSACTION_KEY] == true
end
end
end
end
Loading
Loading
Loading
Loading
@@ -19,22 +19,52 @@ def self.clear_session
RequestStore.delete(CACHE_KEY)
end
 
def self.without_sticky_writes(&block)
current.ignore_writes(&block)
# Always uses replicas disallowing writes
def self.read_only(&blk)
with_new_session do |session|
session.prevent_writes!
yield
end
end
# Uses primary to perform isolated write
# that does not change an overall replica usage
def self.isolated_write(&blk)
with_new_session do |session|
session.use_primary!
yield
end
end
def self.with_new_session
previous = RequestStore[CACHE_KEY]
current = new
RequestStore[CACHE_KEY] = current
yield(current)
ensure
RequestStore[CACHE_KEY] = previous
end
 
def initialize
@use_primary = false
@performed_write = false
@ignore_writes = false
@fallback_to_replicas_for_ambiguous_queries = false
@use_replicas_for_read_queries = false
@read_only = false
end
 
def use_primary?
@use_primary
end
 
def read_only?
@read_only
end
def prevent_writes!
@read_only = true
end
alias_method :using_primary?, :use_primary?
 
def use_primary!
Loading
Loading
@@ -49,69 +79,20 @@ def use_primary(&blk)
@use_primary = used_primary || @performed_write
end
 
def ignore_writes(&block)
@ignore_writes = true
yield
ensure
@ignore_writes = false
end
# Indicates that the read SQL statements from anywhere inside this
# blocks should use a replica, regardless of the current primary
# stickiness or whether a write query is already performed in the
# current session. This interface is reserved mostly for performance
# purpose. This is a good tool to push expensive queries, which can
# tolerate the replica lags, to the replicas.
#
# Write and ambiguous queries inside this block are still handled by
# the primary.
def use_replicas_for_read_queries(&blk)
previous_flag = @use_replicas_for_read_queries
@use_replicas_for_read_queries = true
yield
ensure
@use_replicas_for_read_queries = previous_flag
end
def use_replicas_for_read_queries?
@use_replicas_for_read_queries == true
end
# Indicate that the ambiguous SQL statements from anywhere inside this
# block should use a replica. The ambiguous statements include:
# - Transactions.
# - Custom queries (via exec_query, execute, etc.)
# - In-flight connection configuration change (SET LOCAL statement_timeout = 5000)
#
# This is a weak enforcement. This helper incorporates well with
# primary stickiness:
# - If the queries are about to write
# - The current session already performed writes
# - It prefers to use primary, aka, use_primary or use_primary! were called
def fallback_to_replicas_for_ambiguous_queries(&blk)
previous_flag = @fallback_to_replicas_for_ambiguous_queries
@fallback_to_replicas_for_ambiguous_queries = true
yield
ensure
@fallback_to_replicas_for_ambiguous_queries = previous_flag
end
def fallback_to_replicas_for_ambiguous_queries?
@fallback_to_replicas_for_ambiguous_queries == true && !use_primary? && !performed_write?
end
def write!
@performed_write = true
return if @ignore_writes
use_primary!
end
 
def performed_write?
@performed_write
end
# In some cases in a read only we want to allow opening transaction
# making this still forced to be read-only
def transaction_using_replica?
@read_only && !@use_primary
end
end
end
end
Loading
Loading
Loading
Loading
@@ -147,41 +147,6 @@
end
end
end
context 'session does not fallback to replicas for ambiguous queries' do
let(:primary) { double(:connection) }
before do
allow(session).to receive(:fallback_to_replicas_for_ambiguous_queries?).and_return(false)
allow(session).to receive(:use_replicas_for_read_queries?).and_return(false)
allow(session).to receive(:use_primary?).and_return(true)
allow(primary).to receive(:transaction).and_yield
allow(primary).to receive(:select)
allow(primary).to receive(:insert)
end
context 'with a read query' do
it 'runs the transaction and any nested queries on the primary and stick to it' do
expect(proxy.load_balancer).to receive(:read_write)
.twice.and_yield(primary)
expect(proxy.load_balancer).not_to receive(:read)
expect(session).to receive(:write!)
proxy.transaction { proxy.select('true') }
end
end
context 'with a write query' do
it 'runs the transaction and any nested queries on the primary and stick to it' do
expect(proxy.load_balancer).to receive(:read_write)
.twice.and_yield(primary)
expect(proxy.load_balancer).not_to receive(:read)
expect(session).to receive(:write!).twice
proxy.transaction { proxy.insert('something') }
end
end
end
end
 
describe '#method_missing' do
Loading
Loading
@@ -240,7 +205,6 @@
context 'with a regular session' do
it 'uses a secondary' do
allow(session).to receive(:use_primary?).and_return(false)
allow(session).to receive(:use_replicas_for_read_queries?).and_return(false)
 
expect(connection).to receive(:foo).with('foo')
expect(proxy.load_balancer).to receive(:read).and_yield(connection)
Loading
Loading
@@ -252,7 +216,6 @@
context 'with a regular session and forcing all reads to replicas' do
it 'uses a secondary' do
allow(session).to receive(:use_primary?).and_return(false)
allow(session).to receive(:use_replicas_for_read_queries?).and_return(true)
 
expect(connection).to receive(:foo).with('foo')
expect(proxy.load_balancer).to receive(:read).and_yield(connection)
Loading
Loading
@@ -264,7 +227,6 @@
context 'with a session using the primary but forcing all reads to replicas' do
it 'uses a secondary' do
allow(session).to receive(:use_primary?).and_return(true)
allow(session).to receive(:use_replicas_for_read_queries?).and_return(true)
 
expect(connection).to receive(:foo).with('foo')
expect(proxy.load_balancer).to receive(:read).and_yield(connection)
Loading
Loading
@@ -276,7 +238,6 @@
describe 'with a session using the primary' do
it 'uses the primary' do
allow(session).to receive(:use_primary?).and_return(true)
allow(session).to receive(:use_replicas_for_read_queries?).and_return(false)
 
expect(connection).to receive(:foo).with('foo')
 
Loading
Loading
Loading
Loading
@@ -22,28 +22,6 @@
end
end
 
describe '.without_sticky_writes' do
it 'ignores sticky write events sent by a connection proxy' do
described_class.without_sticky_writes do
described_class.current.write!
end
session = described_class.current
expect(session).not_to be_using_primary
end
it 'still is aware of write that happened' do
described_class.without_sticky_writes do
described_class.current.write!
end
session = described_class.current
expect(session.performed_write?).to be true
end
end
describe '#use_primary?' do
it 'returns true when the primary should be used' do
instance = described_class.new
Loading
Loading
@@ -118,236 +96,4 @@
expect(instance.performed_write?).to eq(true)
end
end
describe '#ignore_writes' do
it 'ignores write events' do
instance = described_class.new
instance.ignore_writes { instance.write! }
expect(instance).not_to be_using_primary
expect(instance.performed_write?).to eq true
end
it 'does not prevent using primary if an exception is raised' do
instance = described_class.new
instance.ignore_writes { raise ArgumentError } rescue ArgumentError
instance.write!
expect(instance).to be_using_primary
end
end
describe '#use_replicas_for_read_queries' do
let(:instance) { described_class.new }
it 'sets the flag inside the block' do
expect do |blk|
instance.use_replicas_for_read_queries do
expect(instance.use_replicas_for_read_queries?).to eq(true)
# call yield probe
blk.to_proc.call
end
end.to yield_control
expect(instance.use_replicas_for_read_queries?).to eq(false)
end
it 'restores state after use' do
expect do |blk|
instance.use_replicas_for_read_queries do
instance.use_replicas_for_read_queries do
expect(instance.use_replicas_for_read_queries?).to eq(true)
# call yield probe
blk.to_proc.call
end
expect(instance.use_replicas_for_read_queries?).to eq(true)
end
end.to yield_control
expect(instance.use_replicas_for_read_queries?).to eq(false)
end
context 'when primary was used before' do
before do
instance.use_primary!
end
it 'sets the flag inside the block' do
expect do |blk|
instance.use_replicas_for_read_queries do
expect(instance.use_replicas_for_read_queries?).to eq(true)
# call yield probe
blk.to_proc.call
end
end.to yield_control
expect(instance.use_replicas_for_read_queries?).to eq(false)
end
end
context 'when a write query is performed before' do
before do
instance.write!
end
it 'sets the flag inside the block' do
expect do |blk|
instance.use_replicas_for_read_queries do
expect(instance.use_replicas_for_read_queries?).to eq(true)
# call yield probe
blk.to_proc.call
end
end.to yield_control
expect(instance.use_replicas_for_read_queries?).to eq(false)
end
end
end
describe '#fallback_to_replicas_for_ambiguous_queries' do
let(:instance) { described_class.new }
it 'sets the flag inside the block' do
expect do |blk|
instance.fallback_to_replicas_for_ambiguous_queries do
expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(true)
# call yield probe
blk.to_proc.call
end
end.to yield_control
expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false)
end
it 'restores state after use' do
expect do |blk|
instance.fallback_to_replicas_for_ambiguous_queries do
instance.fallback_to_replicas_for_ambiguous_queries do
expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(true)
# call yield probe
blk.to_proc.call
end
expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(true)
end
end.to yield_control
expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false)
end
context 'when primary was used before' do
before do
instance.use_primary!
end
it 'uses primary during block' do
expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false)
expect do |blk|
instance.fallback_to_replicas_for_ambiguous_queries do
expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false)
# call yield probe
blk.to_proc.call
end
end.to yield_control
expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false)
end
end
context 'when a write was performed before' do
before do
instance.write!
end
it 'uses primary during block' do
expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false)
expect do |blk|
instance.fallback_to_replicas_for_ambiguous_queries do
expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false)
# call yield probe
blk.to_proc.call
end
end.to yield_control
expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false)
end
end
context 'when primary was used inside the block' do
it 'uses primary aterward' do
expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false)
instance.fallback_to_replicas_for_ambiguous_queries do
expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(true)
instance.use_primary!
expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false)
end
expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false)
end
it 'restores state after use' do
instance.fallback_to_replicas_for_ambiguous_queries do
instance.fallback_to_replicas_for_ambiguous_queries do
expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(true)
instance.use_primary!
expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false)
end
expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false)
end
expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false)
end
end
context 'when a write was performed inside the block' do
it 'uses primary aterward' do
expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false)
instance.fallback_to_replicas_for_ambiguous_queries do
expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(true)
instance.write!
expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false)
end
expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false)
end
it 'restores state after use' do
instance.fallback_to_replicas_for_ambiguous_queries do
instance.fallback_to_replicas_for_ambiguous_queries do
expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(true)
instance.write!
expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false)
end
expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false)
end
expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false)
end
end
end
end
Loading
Loading
@@ -520,74 +520,31 @@
false, [:replica, :primary]
],
 
# use_replicas_for_read_queries does not affect read queries
# read_only does forces read queries to use replica
[
-> {
::Gitlab::Database::LoadBalancing::Session.current.use_replicas_for_read_queries do
::Gitlab::Database::LoadBalancing::Session.current.read_only do
model.where(name: 'test1').to_a
end
},
false, [:replica]
],
 
# use_replicas_for_read_queries does not affect write queries
# isolated_write does not affect write queries
[
-> {
::Gitlab::Database::LoadBalancing::Session.current.use_replicas_for_read_queries do
::Gitlab::Database::LoadBalancing::Session.current.isolated_write do
model.create!(name: 'test1')
end
},
false, [:primary]
],
 
# use_replicas_for_read_queries does not affect ambiguous queries
[
-> {
::Gitlab::Database::LoadBalancing::Session.current.use_replicas_for_read_queries do
model.connection.exec_query("SELECT 1")
end
},
false, [:primary]
],
# use_replicas_for_read_queries ignores use_primary! for read queries
[
-> {
::Gitlab::Database::LoadBalancing::Session.current.use_primary!
::Gitlab::Database::LoadBalancing::Session.current.use_replicas_for_read_queries do
model.where(name: 'test1').to_a
end
},
false, [:replica]
],
# use_replicas_for_read_queries adheres use_primary! for write queries
[
-> {
::Gitlab::Database::LoadBalancing::Session.current.use_primary!
::Gitlab::Database::LoadBalancing::Session.current.use_replicas_for_read_queries do
model.create!(name: 'test1')
end
},
false, [:primary]
],
# use_replicas_for_read_queries adheres use_primary! for ambiguous queries
[
-> {
::Gitlab::Database::LoadBalancing::Session.current.use_primary!
::Gitlab::Database::LoadBalancing::Session.current.use_replicas_for_read_queries do
model.connection.exec_query('SELECT 1')
end
},
false, [:primary]
],
# use_replicas_for_read_queries ignores use_primary blocks
# read_only ignores use_primary blocks
[
-> {
::Gitlab::Database::LoadBalancing::Session.current.use_primary do
::Gitlab::Database::LoadBalancing::Session.current.use_replicas_for_read_queries do
::Gitlab::Database::LoadBalancing::Session.current.read_only do
model.where(name: 'test1').to_a
end
end
Loading
Loading
@@ -595,133 +552,15 @@
false, [:replica]
],
 
# use_replicas_for_read_queries ignores a session already performed write
# read_only ignores a session already performed write
[
-> {
::Gitlab::Database::LoadBalancing::Session.current.write!
::Gitlab::Database::LoadBalancing::Session.current.use_replicas_for_read_queries do
::Gitlab::Database::LoadBalancing::Session.current.read_only do
model.where(name: 'test1').to_a
end
},
false, [:replica]
],
# fallback_to_replicas_for_ambiguous_queries
[
-> {
::Gitlab::Database::LoadBalancing::Session.current.fallback_to_replicas_for_ambiguous_queries do
model.first
model.where(name: 'test1').to_a
end
},
false, [:replica, :replica]
],
# fallback_to_replicas_for_ambiguous_queries for read-only transaction
[
-> {
::Gitlab::Database::LoadBalancing::Session.current.fallback_to_replicas_for_ambiguous_queries do
model.transaction do
model.first
model.where(name: 'test1').to_a
end
end
},
false, [:replica, :replica]
],
# A custom read query inside fallback_to_replicas_for_ambiguous_queries
[
-> {
::Gitlab::Database::LoadBalancing::Session.current.fallback_to_replicas_for_ambiguous_queries do
model.connection.exec_query("SELECT 1")
end
},
false, [:replica]
],
# A custom read query inside a transaction fallback_to_replicas_for_ambiguous_queries
[
-> {
::Gitlab::Database::LoadBalancing::Session.current.fallback_to_replicas_for_ambiguous_queries do
model.transaction do
model.connection.exec_query("SET LOCAL statement_timeout = 5000")
model.count
end
end
},
true, [:replica, :replica, :replica, :replica]
],
# fallback_to_replicas_for_ambiguous_queries after a write
[
-> {
model.create!(name: 'Test1')
::Gitlab::Database::LoadBalancing::Session.current.fallback_to_replicas_for_ambiguous_queries do
model.connection.exec_query("SELECT 1")
end
},
false, [:primary, :primary]
],
# fallback_to_replicas_for_ambiguous_queries after use_primary!
[
-> {
::Gitlab::Database::LoadBalancing::Session.current.use_primary!
::Gitlab::Database::LoadBalancing::Session.current.fallback_to_replicas_for_ambiguous_queries do
model.connection.exec_query("SELECT 1")
end
},
false, [:primary]
],
# fallback_to_replicas_for_ambiguous_queries inside use_primary
[
-> {
::Gitlab::Database::LoadBalancing::Session.current.use_primary do
::Gitlab::Database::LoadBalancing::Session.current.fallback_to_replicas_for_ambiguous_queries do
model.connection.exec_query("SELECT 1")
end
end
},
false, [:primary]
],
# use_primary inside fallback_to_replicas_for_ambiguous_queries
[
-> {
::Gitlab::Database::LoadBalancing::Session.current.fallback_to_replicas_for_ambiguous_queries do
::Gitlab::Database::LoadBalancing::Session.current.use_primary do
model.connection.exec_query("SELECT 1")
end
end
},
false, [:primary]
],
# A write query inside fallback_to_replicas_for_ambiguous_queries
[
-> {
::Gitlab::Database::LoadBalancing::Session.current.fallback_to_replicas_for_ambiguous_queries do
model.connection.exec_query("SELECT 1")
model.delete_all
model.connection.exec_query("SELECT 1")
end
},
false, [:replica, :primary, :primary]
],
# use_replicas_for_read_queries incorporates with fallback_to_replicas_for_ambiguous_queries
[
-> {
::Gitlab::Database::LoadBalancing::Session.current.use_replicas_for_read_queries do
::Gitlab::Database::LoadBalancing::Session.current.fallback_to_replicas_for_ambiguous_queries do
model.connection.exec_query('SELECT 1')
model.where(name: 'test1').to_a
end
end
},
false, [:replica, :replica]
]
]
end
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment