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


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

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.


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

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.


95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/gitlab/database/load_balancing/connection_proxy.rb', line 95

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

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

Returns:

  • (Boolean)

77
78
79
80
81
# File 'lib/gitlab/database/load_balancing/connection_proxy.rb', line 77

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

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


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

def select_all(arel, name = nil, binds = [], preparable: nil)
  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


64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/gitlab/database/load_balancing/connection_proxy.rb', line 64

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.

113
114
115
116
117
118
119
120
121
# File 'lib/gitlab/database/load_balancing/connection_proxy.rb', line 113

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.send(...)
  end
end