Class: Gitlab::Database::LoadBalancing::RackMiddleware

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

Overview

Rack middleware to handle sticking when serving Rails requests. Grape API calls are handled separately as different API endpoints need to stick based on different objects.

Constant Summary collapse

STICK_OBJECT =
'load_balancing.stick_object'

Instance Method Summary collapse

Constructor Details

#initialize(app) ⇒ RackMiddleware

Returns a new instance of RackMiddleware.



12
13
14
# File 'lib/gitlab/database/load_balancing/rack_middleware.rb', line 12

def initialize(app)
  @app = app
end

Instance Method Details

#call(env) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/gitlab/database/load_balancing/rack_middleware.rb', line 16

def call(env)
  # Ensure that any state that may have run before the first request
  # doesn't linger around.
  clear

  find_caught_up_replica(env)

  result = @app.call(env)

  ActiveSupport::Notifications.instrument('web_transaction_completed.load_balancing')

  stick_if_necessary(env)

  result
ensure
  clear
end

#clearObject



57
58
59
60
# File 'lib/gitlab/database/load_balancing/rack_middleware.rb', line 57

def clear
  ::Gitlab::Database::LoadBalancing.release_hosts
  ::Gitlab::Database::LoadBalancing::Session.clear_session
end

#find_caught_up_replica(env) ⇒ Object

Determine if we need to stick based on currently available user data.

Typically this code will only be reachable for Rails requests as Grape data is not yet available at this point.



38
39
40
41
42
43
44
# File 'lib/gitlab/database/load_balancing/rack_middleware.rb', line 38

def find_caught_up_replica(env)
  namespaces_and_ids = sticking_namespaces(env)

  namespaces_and_ids.each do |(sticking, namespace, id)|
    sticking.find_caught_up_replica(namespace, id)
  end
end

#stick_if_necessary(env) ⇒ Object

Determine if we need to stick after handling a request.



47
48
49
50
51
52
53
54
55
# File 'lib/gitlab/database/load_balancing/rack_middleware.rb', line 47

def stick_if_necessary(env)
  return unless ::Gitlab::Database::LoadBalancing::Session.current.performed_write?

  namespaces_and_ids = sticking_namespaces(env)

  namespaces_and_ids.each do |sticking, namespace, id|
    sticking.stick(namespace, id)
  end
end

#sticking_namespaces(env) ⇒ Object

Determines the sticking namespace and identifier based on the Rack environment.

For Rails requests this uses warden, but Grape and others have to manually set the right environment variable.



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/gitlab/database/load_balancing/rack_middleware.rb', line 67

def sticking_namespaces(env)
  warden = env['warden']

  if warden && warden.user
    # When sticking per user, _only_ sticking the main connection could
    # result in the application trying to read data from a different
    # connection, while that data isn't available yet.
    #
    # To prevent this from happening, we scope sticking to all the
    # models that support load balancing. In the future (if we
    # determined this to be OK) we may be able to relax this.
    ::Gitlab::Database::LoadBalancing.base_models.map do |model|
      [model.sticking, :user, warden.user.id]
    end
  elsif env[STICK_OBJECT].present?
    env[STICK_OBJECT].to_a
  else
    []
  end
end