Class: Gitlab::Database::LoadBalancing::ConnectionProxy

Inherits:
Object
  • Object
show all
Defined in:
lib/gitlab/database/load_balancing/connection_proxy.rb

Overview

Redirecting of ActiveRecord connections.

The ConnectionProxy class redirects ActiveRecord connection requests to the right load balancer pool, depending on the type of query.

Constant Summary collapse

WriteInsideReadOnlyTransactionError =
Class.new(StandardError)
READ_ONLY_TRANSACTION_KEY =
:load_balacing_read_only_transaction
STICKY_WRITES =

These methods perform writes after which we need to stick to the primary.

%i[
  delete
  delete_all
  insert
  update
  update_all
  exec_insert_all
].freeze
NON_STICKY_READS =
%i[
  sanitize_limit
  select
  select_one
  select_rows
  quote_column_name
].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(load_balancer) ⇒ ConnectionProxy

hosts - The hosts to use for load balancing.



38
39
40
# File 'lib/gitlab/database/load_balancing/connection_proxy.rb', line 38

def initialize(load_balancer)
  @load_balancer = load_balancer
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missingObject

Delegates all unknown messages to a read-write connection.



92
93
94
95
96
97
98
# File 'lib/gitlab/database/load_balancing/connection_proxy.rb', line 92

def method_missing(...)
  if current_session.fallback_to_replicas_for_ambiguous_queries?
    read_using_load_balancer(...)
  else
    write_using_load_balancer(...)
  end
end

Instance Attribute Details

#load_balancerObject (readonly)

Returns the value of attribute load_balancer.



16
17
18
# File 'lib/gitlab/database/load_balancing/connection_proxy.rb', line 16

def load_balancer
  @load_balancer
end

Instance Method Details

#read_using_load_balancerObject

Performs a read using the load balancer.

name - The name of the method to call on a connection object.



103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/gitlab/database/load_balancing/connection_proxy.rb', line 103

def read_using_load_balancer(...)
  if current_session.use_primary? &&
      !current_session.use_replicas_for_read_queries?
    @load_balancer.read_write do |connection|
      connection.public_send(...)
    end
  else
    @load_balancer.read do |connection|
      connection.public_send(...)
    end
  end
end

#respond_to_missing?(name, include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


85
86
87
88
89
# File 'lib/gitlab/database/load_balancing/connection_proxy.rb', line 85

def respond_to_missing?(name, include_private = false)
  @load_balancer.read_write do |connection|
    connection.respond_to?(name, include_private)
  end
end

#schema_cache(*args, **kwargs, &block) ⇒ Object



65
66
67
68
69
70
# File 'lib/gitlab/database/load_balancing/connection_proxy.rb', line 65

def schema_cache(*args, **kwargs, &block)
  # Ignore primary stickiness for schema_cache queries and always use replicas
  @load_balancer.read do |connection|
    connection.public_send(:schema_cache, *args, **kwargs, &block)
  end
end

#select_all(arel, name = nil, binds = [], preparable: nil, async: false) ⇒ Object



42
43
44
45
46
47
48
49
50
# File 'lib/gitlab/database/load_balancing/connection_proxy.rb', line 42

def select_all(arel, name = nil, binds = [], preparable: nil, async: false)
  if arel.respond_to?(:locked) && arel.locked
    # SELECT ... FOR UPDATE queries should be sent to the primary.
    current_session.write!
    write_using_load_balancer(:select_all, arel, name, binds)
  else
    read_using_load_balancer(:select_all, arel, name, binds)
  end
end

#transaction(*args, **kwargs, &block) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/gitlab/database/load_balancing/connection_proxy.rb', line 72

def transaction(*args, **kwargs, &block)
  if current_session.fallback_to_replicas_for_ambiguous_queries?
    track_read_only_transaction!
    read_using_load_balancer(:transaction, *args, **kwargs, &block)
  else
    current_session.write!
    write_using_load_balancer(:transaction, *args, **kwargs, &block)
  end

ensure
  untrack_read_only_transaction!
end

#write_using_load_balancerObject

Performs a write using the load balancer.

name - The name of the method to call on a connection object. sticky - If set to true the session will stick to the master after

the write.


121
122
123
124
125
126
127
128
129
# File 'lib/gitlab/database/load_balancing/connection_proxy.rb', line 121

def write_using_load_balancer(...)
  if read_only_transaction?
    raise WriteInsideReadOnlyTransactionError, 'A write query is performed inside a read-only transaction'
  end

  @load_balancer.read_write do |connection|
    connection.public_send(...)
  end
end