Commit 0274e1f0 authored by Sean McGivern's avatar Sean McGivern

Merge branch '1334-use-redis-sessions-store' into 'master'

Use Redis session instance

See merge request gitlab-org/gitlab!74202
parents f0d69f67 66af8321
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
# #
class ActiveSession class ActiveSession
include ActiveModel::Model include ActiveModel::Model
include ::Gitlab::Redis::SessionsStoreHelper
SESSION_BATCH_SIZE = 200 SESSION_BATCH_SIZE = 200
ALLOWED_NUMBER_OF_ACTIVE_SESSIONS = 100 ALLOWED_NUMBER_OF_ACTIVE_SESSIONS = 100
...@@ -43,7 +44,7 @@ class ActiveSession ...@@ -43,7 +44,7 @@ class ActiveSession
end end
def self.set(user, request) def self.set(user, request)
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
session_private_id = request.session.id.private_id session_private_id = request.session.id.private_id
client = DeviceDetector.new(request.user_agent) client = DeviceDetector.new(request.user_agent)
timestamp = Time.current timestamp = Time.current
...@@ -76,7 +77,7 @@ class ActiveSession ...@@ -76,7 +77,7 @@ class ActiveSession
end end
def self.list(user) def self.list(user)
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
cleaned_up_lookup_entries(redis, user).map do |raw_session| cleaned_up_lookup_entries(redis, user).map do |raw_session|
load_raw_session(raw_session) load_raw_session(raw_session)
end end
...@@ -84,7 +85,7 @@ class ActiveSession ...@@ -84,7 +85,7 @@ class ActiveSession
end end
def self.cleanup(user) def self.cleanup(user)
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
clean_up_old_sessions(redis, user) clean_up_old_sessions(redis, user)
cleaned_up_lookup_entries(redis, user) cleaned_up_lookup_entries(redis, user)
end end
...@@ -104,7 +105,7 @@ class ActiveSession ...@@ -104,7 +105,7 @@ class ActiveSession
def self.destroy_session(user, session_id) def self.destroy_session(user, session_id)
return unless session_id return unless session_id
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
destroy_sessions(redis, user, [session_id].compact) destroy_sessions(redis, user, [session_id].compact)
end end
end end
...@@ -113,7 +114,7 @@ class ActiveSession ...@@ -113,7 +114,7 @@ class ActiveSession
sessions = not_impersonated(user) sessions = not_impersonated(user)
sessions.reject! { |session| session.current?(current_rack_session) } if current_rack_session sessions.reject! { |session| session.current?(current_rack_session) } if current_rack_session
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
session_ids = (sessions.map(&:session_id) | sessions.map(&:session_private_id)).compact session_ids = (sessions.map(&:session_id) | sessions.map(&:session_private_id)).compact
destroy_sessions(redis, user, session_ids) if session_ids.any? destroy_sessions(redis, user, session_ids) if session_ids.any?
end end
...@@ -124,15 +125,15 @@ class ActiveSession ...@@ -124,15 +125,15 @@ class ActiveSession
end end
def self.rack_key_name(session_id) def self.rack_key_name(session_id)
"#{Gitlab::Redis::SharedState::SESSION_NAMESPACE}:#{session_id}" "#{Gitlab::Redis::Sessions::SESSION_NAMESPACE}:#{session_id}"
end end
def self.key_name(user_id, session_id = '*') def self.key_name(user_id, session_id = '*')
"#{Gitlab::Redis::SharedState::USER_SESSIONS_NAMESPACE}:#{user_id}:#{session_id}" "#{Gitlab::Redis::Sessions::USER_SESSIONS_NAMESPACE}:#{user_id}:#{session_id}"
end end
def self.lookup_key_name(user_id) def self.lookup_key_name(user_id)
"#{Gitlab::Redis::SharedState::USER_SESSIONS_LOOKUP_NAMESPACE}:#{user_id}" "#{Gitlab::Redis::Sessions::USER_SESSIONS_LOOKUP_NAMESPACE}:#{user_id}"
end end
def self.list_sessions(user) def self.list_sessions(user)
...@@ -143,7 +144,7 @@ class ActiveSession ...@@ -143,7 +144,7 @@ class ActiveSession
# #
# Returns an array of strings # Returns an array of strings
def self.session_ids_for_user(user_id) def self.session_ids_for_user(user_id)
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
redis.smembers(lookup_key_name(user_id)) redis.smembers(lookup_key_name(user_id))
end end
end end
...@@ -156,7 +157,7 @@ class ActiveSession ...@@ -156,7 +157,7 @@ class ActiveSession
def self.sessions_from_ids(session_ids) def self.sessions_from_ids(session_ids)
return [] if session_ids.empty? return [] if session_ids.empty?
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
session_keys = rack_session_keys(session_ids) session_keys = rack_session_keys(session_ids)
session_keys.each_slice(SESSION_BATCH_SIZE).flat_map do |session_keys_batch| session_keys.each_slice(SESSION_BATCH_SIZE).flat_map do |session_keys_batch|
...@@ -228,9 +229,9 @@ class ActiveSession ...@@ -228,9 +229,9 @@ class ActiveSession
# only the single key entries are automatically expired by redis, the # only the single key entries are automatically expired by redis, the
# lookup entries in the set need to be removed manually. # lookup entries in the set need to be removed manually.
session_ids_and_entries = session_ids.zip(entries) session_ids_and_entries = session_ids.zip(entries)
redis.pipelined do redis.pipelined do |pipeline|
session_ids_and_entries.reject { |_session_id, entry| entry }.each do |session_id, _entry| session_ids_and_entries.reject { |_session_id, entry| entry }.each do |session_id, _entry|
redis.srem(lookup_key_name(user.id), session_id) pipeline.srem(lookup_key_name(user.id), session_id)
end end
end end
......
--- ---
name: use_multi_store name: use_multi_store
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73660 introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73660
rollout_issue_url: rollout_issue_url: https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/1429
milestone: '14.5' milestone: '14.5'
type: development type: development
group: group::memory group: group::memory
......
...@@ -19,31 +19,22 @@ cookie_key = if Rails.env.development? ...@@ -19,31 +19,22 @@ cookie_key = if Rails.env.development?
"_gitlab_session" "_gitlab_session"
end end
if Gitlab::Utils.to_boolean(ENV['GITLAB_REDIS_STORE_WITH_SESSION_STORE'], default: true) store = if Gitlab::Utils.to_boolean(ENV['GITLAB_USE_REDIS_SESSIONS_STORE'], default: true)
store = Gitlab::Redis::SharedState.store( Gitlab::Redis::Sessions.store(
namespace: Gitlab::Redis::SharedState::SESSION_NAMESPACE namespace: Gitlab::Redis::Sessions::SESSION_NAMESPACE
) )
else
Gitlab::Application.config.session_store( Gitlab::Redis::SharedState.store(
:redis_store, # Using the cookie_store would enable session replay attacks. namespace: Gitlab::Redis::Sessions::SESSION_NAMESPACE
redis_store: store,
key: cookie_key,
secure: Gitlab.config.gitlab.https,
httponly: true,
expires_in: Settings.gitlab['session_expire_delay'] * 60,
path: Rails.application.config.relative_url_root.presence || '/'
) )
else end
sessions_config = Gitlab::Redis::SharedState.params
sessions_config[:namespace] = Gitlab::Redis::SharedState::SESSION_NAMESPACE
Gitlab::Application.config.session_store( Gitlab::Application.config.session_store(
:redis_store, # Using the cookie_store would enable session replay attacks. :redis_store, # Using the cookie_store would enable session replay attacks.
servers: sessions_config, redis_store: store,
key: cookie_key, key: cookie_key,
secure: Gitlab.config.gitlab.https, secure: Gitlab.config.gitlab.https,
httponly: true, httponly: true,
expires_in: Settings.gitlab['session_expire_delay'] * 60, expires_in: Settings.gitlab['session_expire_delay'] * 60,
path: Rails.application.config.relative_url_root.presence || '/' path: Rails.application.config.relative_url_root.presence || '/'
) )
end
...@@ -51,12 +51,12 @@ Session data can be accessed directly through Redis. This can let you check up o ...@@ -51,12 +51,12 @@ Session data can be accessed directly through Redis. This can let you check up o
```ruby ```ruby
# Get a list of sessions # Get a list of sessions
session_ids = Gitlab::Redis::SharedState.with do |redis| session_ids = Gitlab::Redis::Sessions.with do |redis|
redis.smembers("#{Gitlab::Redis::SharedState::USER_SESSIONS_LOOKUP_NAMESPACE}:#{user.id}") redis.smembers("#{Gitlab::Redis::Sessions::USER_SESSIONS_LOOKUP_NAMESPACE}:#{user.id}")
end end
# Retrieve a specific session # Retrieve a specific session
session_data = Gitlab::Redis::SharedState.with { |redis| redis.get("#{Gitlab::Redis::SharedState::SESSION_NAMESPACE}:#{session_id}") } session_data = Gitlab::Redis::Sessions.with { |redis| redis.get("#{Gitlab::Redis::Sessions::SESSION_NAMESPACE}:#{session_id}") }
Marshal.load(session_data) Marshal.load(session_data)
``` ```
......
...@@ -4,20 +4,20 @@ module Gitlab ...@@ -4,20 +4,20 @@ module Gitlab
module Auth module Auth
module Otp module Otp
class SessionEnforcer class SessionEnforcer
OTP_SESSIONS_NAMESPACE = 'session:otp' include ::Gitlab::Redis::SessionsStoreHelper
def initialize(key) def initialize(key)
@key = key @key = key
end end
def update_session def update_session
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
redis.setex(key_name, session_expiry_in_seconds, true) redis.setex(key_name, session_expiry_in_seconds, true)
end end
end end
def access_restricted? def access_restricted?
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
!redis.get(key_name) !redis.get(key_name)
end end
end end
...@@ -27,7 +27,7 @@ module Gitlab ...@@ -27,7 +27,7 @@ module Gitlab
attr_reader :key attr_reader :key
def key_name def key_name
@key_name ||= "#{OTP_SESSIONS_NAMESPACE}:#{key.id}" @key_name ||= "#{Gitlab::Redis::Sessions::OTP_SESSIONS_NAMESPACE}:#{key.id}"
end end
def session_expiry_in_seconds def session_expiry_in_seconds
......
...@@ -45,7 +45,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do ...@@ -45,7 +45,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
expect(response).to have_gitlab_http_status(:not_found) expect(response).to have_gitlab_http_status(:not_found)
end end
context 'with an active session', :clean_gitlab_redis_shared_state do context 'with an active session', :clean_gitlab_redis_sessions do
let(:session_id) { '42' } let(:session_id) { '42' }
let(:session_time) { 5.minutes.ago } let(:session_time) { 5.minutes.ago }
let(:stored_session) do let(:stored_session) do
...@@ -53,7 +53,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do ...@@ -53,7 +53,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
end end
before do before do
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
redis.set("session:gitlab:#{session_id}", Marshal.dump(stored_session)) redis.set("session:gitlab:#{session_id}", Marshal.dump(stored_session))
redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id]) redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id])
end end
......
...@@ -85,7 +85,7 @@ RSpec.describe 'Login' do ...@@ -85,7 +85,7 @@ RSpec.describe 'Login' do
expect(page.body).to have_link('Register now', href: new_user_registration_path) expect(page.body).to have_link('Register now', href: new_user_registration_path)
end end
describe 'with two-factor authentication required', :clean_gitlab_redis_shared_state do describe 'with two-factor authentication required', :clean_gitlab_redis_sessions do
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:smartcard_identity) { create(:smartcard_identity, user: user) } let_it_be(:smartcard_identity) { create(:smartcard_identity, user: user) }
......
...@@ -27,7 +27,7 @@ RSpec.describe Gitlab::Auth::GroupSaml::SessionEnforcer do ...@@ -27,7 +27,7 @@ RSpec.describe Gitlab::Auth::GroupSaml::SessionEnforcer do
allow(saml_provider).to receive(:git_check_enforced?).and_return(true) allow(saml_provider).to receive(:git_check_enforced?).and_return(true)
end end
context 'with an active session', :clean_gitlab_redis_shared_state do context 'with an active session', :clean_gitlab_redis_sessions do
let(:session_id) { '42' } let(:session_id) { '42' }
let(:session_time) { 5.minutes.ago } let(:session_time) { 5.minutes.ago }
let(:stored_session) do let(:stored_session) do
...@@ -35,7 +35,7 @@ RSpec.describe Gitlab::Auth::GroupSaml::SessionEnforcer do ...@@ -35,7 +35,7 @@ RSpec.describe Gitlab::Auth::GroupSaml::SessionEnforcer do
end end
before do before do
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
redis.set("session:gitlab:#{session_id}", Marshal.dump(stored_session)) redis.set("session:gitlab:#{session_id}", Marshal.dump(stored_session))
redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id]) redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id])
end end
...@@ -63,14 +63,14 @@ RSpec.describe Gitlab::Auth::GroupSaml::SessionEnforcer do ...@@ -63,14 +63,14 @@ RSpec.describe Gitlab::Auth::GroupSaml::SessionEnforcer do
end end
end end
context 'with two active sessions', :clean_gitlab_redis_shared_state do context 'with two active sessions', :clean_gitlab_redis_sessions do
let(:second_session_id) { '52' } let(:second_session_id) { '52' }
let(:second_stored_session) do let(:second_stored_session) do
{ 'active_group_sso_sign_ins' => { create(:saml_provider, enforced_sso: true).id => session_time } } { 'active_group_sso_sign_ins' => { create(:saml_provider, enforced_sso: true).id => session_time } }
end end
before do before do
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
redis.set("session:gitlab:#{second_session_id}", Marshal.dump(second_stored_session)) redis.set("session:gitlab:#{second_session_id}", Marshal.dump(second_stored_session))
redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id, second_session_id]) redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id, second_session_id])
end end
...@@ -79,7 +79,7 @@ RSpec.describe Gitlab::Auth::GroupSaml::SessionEnforcer do ...@@ -79,7 +79,7 @@ RSpec.describe Gitlab::Auth::GroupSaml::SessionEnforcer do
it_behaves_like 'not enforced' it_behaves_like 'not enforced'
end end
context 'with two active sessions for the same provider and one pre-sso', :clean_gitlab_redis_shared_state do context 'with two active sessions for the same provider and one pre-sso', :clean_gitlab_redis_sessions do
let(:second_session_id) { '52' } let(:second_session_id) { '52' }
let(:third_session_id) { '62' } let(:third_session_id) { '62' }
let(:second_stored_session) do let(:second_stored_session) do
...@@ -87,7 +87,7 @@ RSpec.describe Gitlab::Auth::GroupSaml::SessionEnforcer do ...@@ -87,7 +87,7 @@ RSpec.describe Gitlab::Auth::GroupSaml::SessionEnforcer do
end end
before do before do
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
redis.set("session:gitlab:#{second_session_id}", Marshal.dump(second_stored_session)) redis.set("session:gitlab:#{second_session_id}", Marshal.dump(second_stored_session))
redis.set("session:gitlab:#{third_session_id}", Marshal.dump({})) redis.set("session:gitlab:#{third_session_id}", Marshal.dump({}))
redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id, second_session_id, third_session_id]) redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id, second_session_id, third_session_id])
...@@ -192,14 +192,14 @@ RSpec.describe Gitlab::Auth::GroupSaml::SessionEnforcer do ...@@ -192,14 +192,14 @@ RSpec.describe Gitlab::Auth::GroupSaml::SessionEnforcer do
allow(saml_provider).to receive(:git_check_enforced?).and_return(false) allow(saml_provider).to receive(:git_check_enforced?).and_return(false)
end end
context 'with an active session', :clean_gitlab_redis_shared_state do context 'with an active session', :clean_gitlab_redis_sessions do
let(:session_id) { '42' } let(:session_id) { '42' }
let(:stored_session) do let(:stored_session) do
{ 'active_group_sso_sign_ins' => { saml_provider.id => 5.minutes.ago } } { 'active_group_sso_sign_ins' => { saml_provider.id => 5.minutes.ago } }
end end
before do before do
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
redis.set("session:gitlab:#{session_id}", Marshal.dump(stored_session)) redis.set("session:gitlab:#{session_id}", Marshal.dump(stored_session))
redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id]) redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id])
end end
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::Auth::Otp::SessionEnforcer, :clean_gitlab_redis_shared_state do RSpec.describe Gitlab::Auth::Otp::SessionEnforcer, :clean_gitlab_redis_sessions do
let_it_be(:key) { create(:key)} let_it_be(:key) { create(:key)}
describe '#update_session' do describe '#update_session' do
...@@ -13,12 +13,12 @@ RSpec.describe Gitlab::Auth::Otp::SessionEnforcer, :clean_gitlab_redis_shared_st ...@@ -13,12 +13,12 @@ RSpec.describe Gitlab::Auth::Otp::SessionEnforcer, :clean_gitlab_redis_shared_st
end end
it 'registers a session in Redis' do it 'registers a session in Redis' do
expect(Gitlab::Redis::SharedState).to receive(:with).and_yield(redis) expect(Gitlab::Redis::Sessions).to receive(:with).and_yield(redis)
session_expiry_in_seconds = Gitlab::CurrentSettings.git_two_factor_session_expiry.minutes.to_i session_expiry_in_seconds = Gitlab::CurrentSettings.git_two_factor_session_expiry.minutes.to_i
expect(redis).to( expect(redis).to(
receive(:setex) receive(:setex)
.with("#{described_class::OTP_SESSIONS_NAMESPACE}:#{key.id}", .with("#{::Gitlab::Redis::Sessions::OTP_SESSIONS_NAMESPACE}:#{key.id}",
session_expiry_in_seconds, session_expiry_in_seconds,
true) true)
.once) .once)
...@@ -48,8 +48,8 @@ RSpec.describe Gitlab::Auth::Otp::SessionEnforcer, :clean_gitlab_redis_shared_st ...@@ -48,8 +48,8 @@ RSpec.describe Gitlab::Auth::Otp::SessionEnforcer, :clean_gitlab_redis_shared_st
context 'with existing session' do context 'with existing session' do
before do before do
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
redis.set("#{described_class::OTP_SESSIONS_NAMESPACE}:#{key.id}", true ) redis.set("#{::Gitlab::Redis::Sessions::OTP_SESSIONS_NAMESPACE}:#{key.id}", true )
end end
end end
......
...@@ -29,14 +29,14 @@ RSpec.describe Gitlab::Auth::Smartcard::SessionEnforcer do ...@@ -29,14 +29,14 @@ RSpec.describe Gitlab::Auth::Smartcard::SessionEnforcer do
stub_smartcard_setting(enabled: true, required_for_git_access: true) stub_smartcard_setting(enabled: true, required_for_git_access: true)
end end
context 'with a smartcard session', :clean_gitlab_redis_shared_state do context 'with a smartcard session', :clean_gitlab_redis_sessions do
let(:session_id) { '42' } let(:session_id) { '42' }
let(:stored_session) do let(:stored_session) do
{ 'smartcard_signins' => { 'last_signin_at' => 5.minutes.ago } } { 'smartcard_signins' => { 'last_signin_at' => 5.minutes.ago } }
end end
before do before do
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
redis.set("session:gitlab:#{session_id}", Marshal.dump(stored_session)) redis.set("session:gitlab:#{session_id}", Marshal.dump(stored_session))
redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id]) redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id])
end end
......
...@@ -8,14 +8,14 @@ RSpec.describe Gitlab::Auth::Smartcard::Session do ...@@ -8,14 +8,14 @@ RSpec.describe Gitlab::Auth::Smartcard::Session do
subject { described_class.new.active?(user) } subject { described_class.new.active?(user) }
context 'with a smartcard session', :clean_gitlab_redis_shared_state do context 'with a smartcard session', :clean_gitlab_redis_sessions do
let(:session_id) { '42' } let(:session_id) { '42' }
let(:stored_session) do let(:stored_session) do
{ 'smartcard_signins' => { 'last_signin_at' => 5.minutes.ago } } { 'smartcard_signins' => { 'last_signin_at' => 5.minutes.ago } }
end end
before do before do
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
redis.set("session:gitlab:#{session_id}", Marshal.dump(stored_session)) redis.set("session:gitlab:#{session_id}", Marshal.dump(stored_session))
redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id]) redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id])
end end
......
...@@ -712,14 +712,14 @@ RSpec.describe Gitlab::GitAccess do ...@@ -712,14 +712,14 @@ RSpec.describe Gitlab::GitAccess do
project.add_developer(user) project.add_developer(user)
end end
context 'user with a smartcard session', :clean_gitlab_redis_shared_state do context 'user with a smartcard session', :clean_gitlab_redis_sessions do
let(:session_id) { '42' } let(:session_id) { '42' }
let(:stored_session) do let(:stored_session) do
{ 'smartcard_signins' => { 'last_signin_at' => 5.minutes.ago } } { 'smartcard_signins' => { 'last_signin_at' => 5.minutes.ago } }
end end
before do before do
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
redis.set("session:gitlab:#{session_id}", Marshal.dump(stored_session)) redis.set("session:gitlab:#{session_id}", Marshal.dump(stored_session))
redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id]) redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id])
end end
...@@ -772,10 +772,10 @@ RSpec.describe Gitlab::GitAccess do ...@@ -772,10 +772,10 @@ RSpec.describe Gitlab::GitAccess do
stub_licensed_features(git_two_factor_enforcement: true) stub_licensed_features(git_two_factor_enforcement: true)
end end
context 'with an OTP session', :clean_gitlab_redis_shared_state do context 'with an OTP session', :clean_gitlab_redis_sessions do
before do before do
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
redis.set("#{Gitlab::Auth::Otp::SessionEnforcer::OTP_SESSIONS_NAMESPACE}:#{key.id}", true) redis.set("#{Gitlab::Redis::Sessions::OTP_SESSIONS_NAMESPACE}:#{key.id}", true)
end end
end end
...@@ -803,11 +803,11 @@ RSpec.describe Gitlab::GitAccess do ...@@ -803,11 +803,11 @@ RSpec.describe Gitlab::GitAccess do
def stub_redis def stub_redis
redis = double(:redis) redis = double(:redis)
expect(Gitlab::Redis::SharedState).to receive(:with).at_most(:twice).and_yield(redis) expect(Gitlab::Redis::Sessions).to receive(:with).at_most(:twice).and_yield(redis)
expect(redis).to( expect(redis).to(
receive(:get) receive(:get)
.with("#{Gitlab::Auth::Otp::SessionEnforcer::OTP_SESSIONS_NAMESPACE}:#{key.id}")) .with("#{Gitlab::Redis::Sessions::OTP_SESSIONS_NAMESPACE}:#{key.id}"))
.at_most(:twice) .at_most(:twice)
.and_return(value_of_key) .and_return(value_of_key)
end end
......
...@@ -154,14 +154,14 @@ RSpec.describe API::Internal::Base do ...@@ -154,14 +154,14 @@ RSpec.describe API::Internal::Base do
project.add_developer(user) project.add_developer(user)
end end
context 'user with a smartcard session', :clean_gitlab_redis_shared_state do context 'user with a smartcard session', :clean_gitlab_redis_sessions do
let(:session_id) { '42' } let(:session_id) { '42' }
let(:stored_session) do let(:stored_session) do
{ 'smartcard_signins' => { 'last_signin_at' => 5.minutes.ago } } { 'smartcard_signins' => { 'last_signin_at' => 5.minutes.ago } }
end end
before do before do
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
redis.set("session:gitlab:#{session_id}", Marshal.dump(stored_session)) redis.set("session:gitlab:#{session_id}", Marshal.dump(stored_session))
redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id]) redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id])
end end
......
...@@ -54,14 +54,14 @@ RSpec.describe Repositories::GitHttpController, type: :request do ...@@ -54,14 +54,14 @@ RSpec.describe Repositories::GitHttpController, type: :request do
project.add_developer(user) project.add_developer(user)
end end
context 'user with a smartcard session', :clean_gitlab_redis_shared_state do context 'user with a smartcard session', :clean_gitlab_redis_sessions do
let(:session_id) { '42' } let(:session_id) { '42' }
let(:stored_session) do let(:stored_session) do
{ 'smartcard_signins' => { 'last_signin_at' => 5.minutes.ago } } { 'smartcard_signins' => { 'last_signin_at' => 5.minutes.ago } }
end end
before do before do
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
redis.set("session:gitlab:#{session_id}", Marshal.dump(stored_session)) redis.set("session:gitlab:#{session_id}", Marshal.dump(stored_session))
redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id]) redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id])
end end
......
...@@ -2,12 +2,14 @@ ...@@ -2,12 +2,14 @@
module Gitlab module Gitlab
class AnonymousSession class AnonymousSession
include ::Gitlab::Redis::SessionsStoreHelper
def initialize(remote_ip) def initialize(remote_ip)
@remote_ip = remote_ip @remote_ip = remote_ip
end end
def count_session_ip def count_session_ip
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
redis.pipelined do redis.pipelined do
redis.incr(session_lookup_name) redis.incr(session_lookup_name)
redis.expire(session_lookup_name, 24.hours) redis.expire(session_lookup_name, 24.hours)
...@@ -16,13 +18,13 @@ module Gitlab ...@@ -16,13 +18,13 @@ module Gitlab
end end
def session_count def session_count
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
redis.get(session_lookup_name).to_i redis.get(session_lookup_name).to_i
end end
end end
def cleanup_session_per_ip_count def cleanup_session_per_ip_count
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
redis.del(session_lookup_name) redis.del(session_lookup_name)
end end
end end
...@@ -32,7 +34,7 @@ module Gitlab ...@@ -32,7 +34,7 @@ module Gitlab
attr_reader :remote_ip attr_reader :remote_ip
def session_lookup_name def session_lookup_name
@session_lookup_name ||= "#{Gitlab::Redis::SharedState::IP_SESSIONS_LOOKUP_NAMESPACE}:#{remote_ip}" @session_lookup_name ||= "#{Gitlab::Redis::Sessions::IP_SESSIONS_LOOKUP_NAMESPACE}:#{remote_ip}"
end end
end end
end end
...@@ -194,7 +194,7 @@ module Gitlab ...@@ -194,7 +194,7 @@ module Gitlab
def increment_method_missing_count(command_name) def increment_method_missing_count(command_name)
@method_missing_counter ||= Gitlab::Metrics.counter(:gitlab_redis_multi_store_method_missing_total, 'Client side Redis MultiStore method missing') @method_missing_counter ||= Gitlab::Metrics.counter(:gitlab_redis_multi_store_method_missing_total, 'Client side Redis MultiStore method missing')
@method_missing_counter.increment(command: command_name, innamece_name: instance_name) @method_missing_counter.increment(command: command_name, instance_name: instance_name)
end end
def validate_stores! def validate_stores!
......
...@@ -3,10 +3,46 @@ ...@@ -3,10 +3,46 @@
module Gitlab module Gitlab
module Redis module Redis
class Sessions < ::Gitlab::Redis::Wrapper class Sessions < ::Gitlab::Redis::Wrapper
SESSION_NAMESPACE = 'session:gitlab'
USER_SESSIONS_NAMESPACE = 'session:user:gitlab'
USER_SESSIONS_LOOKUP_NAMESPACE = 'session:lookup:user:gitlab'
IP_SESSIONS_LOOKUP_NAMESPACE = 'session:lookup:ip:gitlab2'
OTP_SESSIONS_NAMESPACE = 'session:otp'
class << self
# The data we store on Sessions used to be stored on SharedState. # The data we store on Sessions used to be stored on SharedState.
def self.config_fallback def config_fallback
SharedState SharedState
end end
private
def redis
# Don't use multistore if redis.sessions configuration is not provided
return super if config_fallback?
primary_store = ::Redis.new(params)
secondary_store = ::Redis.new(config_fallback.params)
MultiStore.new(primary_store, secondary_store, name)
end
end
def store(extras = {})
# Don't use multistore if redis.sessions configuration is not provided
return super if self.class.config_fallback?
primary_store = create_redis_store(redis_store_options, extras)
secondary_store = create_redis_store(self.class.config_fallback.params, extras)
MultiStore.new(primary_store, secondary_store, self.class.name)
end
private
def create_redis_store(options, extras)
::Redis::Store.new(options.merge(extras))
end
end end
end end
end end
# frozen_string_literal: true
module Gitlab
module Redis
module SessionsStoreHelper
extend ActiveSupport::Concern
module StoreMethods
def redis_store_class
use_redis_session_store? ? Gitlab::Redis::Sessions : Gitlab::Redis::SharedState
end
private
def use_redis_session_store?
Gitlab::Utils.to_boolean(ENV['GITLAB_USE_REDIS_SESSIONS_STORE'], default: true)
end
end
include StoreMethods
included do
extend StoreMethods
end
end
end
end
...@@ -3,10 +3,6 @@ ...@@ -3,10 +3,6 @@
module Gitlab module Gitlab
module Redis module Redis
class SharedState < ::Gitlab::Redis::Wrapper class SharedState < ::Gitlab::Redis::Wrapper
SESSION_NAMESPACE = 'session:gitlab'
USER_SESSIONS_NAMESPACE = 'session:user:gitlab'
USER_SESSIONS_LOOKUP_NAMESPACE = 'session:lookup:user:gitlab'
IP_SESSIONS_LOOKUP_NAMESPACE = 'session:lookup:ip:gitlab2'
end end
end end
end end
...@@ -28,7 +28,7 @@ module Gitlab ...@@ -28,7 +28,7 @@ module Gitlab
end end
def pool def pool
@pool ||= ConnectionPool.new(size: pool_size) { ::Redis.new(params) } @pool ||= ConnectionPool.new(size: pool_size) { redis }
end end
def pool_size def pool_size
...@@ -67,6 +67,10 @@ module Gitlab ...@@ -67,6 +67,10 @@ module Gitlab
File.expand_path('../../..', __dir__) File.expand_path('../../..', __dir__)
end end
def config_fallback?
config_file_name == config_fallback&.config_file_name
end
def config_file_name def config_file_name
[ [
# Instance specific config sources: # Instance specific config sources:
...@@ -100,6 +104,12 @@ module Gitlab ...@@ -100,6 +104,12 @@ module Gitlab
"::Gitlab::Instrumentation::Redis::#{store_name}".constantize "::Gitlab::Instrumentation::Redis::#{store_name}".constantize
end end
private
def redis
::Redis.new(params)
end
end end
def initialize(rails_env = nil) def initialize(rails_env = nil)
......
...@@ -100,13 +100,15 @@ namespace :gitlab do ...@@ -100,13 +100,15 @@ namespace :gitlab do
namespace :sessions do namespace :sessions do
desc "GitLab | Cleanup | Sessions | Clean ActiveSession lookup keys" desc "GitLab | Cleanup | Sessions | Clean ActiveSession lookup keys"
task active_sessions_lookup_keys: :gitlab_environment do task active_sessions_lookup_keys: :gitlab_environment do
session_key_pattern = "#{Gitlab::Redis::SharedState::USER_SESSIONS_LOOKUP_NAMESPACE}:*" use_redis_session_store = Gitlab::Utils.to_boolean(ENV['GITLAB_USE_REDIS_SESSIONS_STORE'], default: true)
redis_store_class = use_redis_session_store ? Gitlab::Redis::Sessions : Gitlab::Redis::SharedState
session_key_pattern = "#{Gitlab::Redis::Sessions::USER_SESSIONS_LOOKUP_NAMESPACE}:*"
last_save_check = Time.at(0) last_save_check = Time.at(0)
wait_time = 10.seconds wait_time = 10.seconds
cursor = 0 cursor = 0
total_users_scanned = 0 total_users_scanned = 0
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
begin begin
cursor, keys = redis.scan(cursor, match: session_key_pattern) cursor, keys = redis.scan(cursor, match: session_key_pattern)
total_users_scanned += keys.count total_users_scanned += keys.count
......
...@@ -2,12 +2,12 @@ ...@@ -2,12 +2,12 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe ApplicationCable::Connection, :clean_gitlab_redis_shared_state do RSpec.describe ApplicationCable::Connection, :clean_gitlab_redis_sessions do
let(:session_id) { Rack::Session::SessionId.new('6919a6f1bb119dd7396fadc38fd18d0d') } let(:session_id) { Rack::Session::SessionId.new('6919a6f1bb119dd7396fadc38fd18d0d') }
context 'when session cookie is set' do context 'when session cookie is set' do
before do before do
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
redis.set("session:gitlab:#{session_id.private_id}", Marshal.dump(session_hash)) redis.set("session:gitlab:#{session_id.private_id}", Marshal.dump(session_hash))
end end
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Active user sessions', :clean_gitlab_redis_shared_state do RSpec.describe 'Active user sessions', :clean_gitlab_redis_sessions do
it 'successful login adds a new active user login' do it 'successful login adds a new active user login' do
now = Time.zone.parse('2018-03-12 09:06') now = Time.zone.parse('2018-03-12 09:06')
Timecop.freeze(now) do Timecop.freeze(now) do
...@@ -29,13 +29,13 @@ RSpec.describe 'Active user sessions', :clean_gitlab_redis_shared_state do ...@@ -29,13 +29,13 @@ RSpec.describe 'Active user sessions', :clean_gitlab_redis_shared_state do
it 'successful login cleans up obsolete entries' do it 'successful login cleans up obsolete entries' do
user = create(:user) user = create(:user)
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
redis.sadd("session:lookup:user:gitlab:#{user.id}", '59822c7d9fcdfa03725eff41782ad97d') redis.sadd("session:lookup:user:gitlab:#{user.id}", '59822c7d9fcdfa03725eff41782ad97d')
end end
gitlab_sign_in(user) gitlab_sign_in(user)
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).not_to include '59822c7d9fcdfa03725eff41782ad97d' expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).not_to include '59822c7d9fcdfa03725eff41782ad97d'
end end
end end
...@@ -44,14 +44,14 @@ RSpec.describe 'Active user sessions', :clean_gitlab_redis_shared_state do ...@@ -44,14 +44,14 @@ RSpec.describe 'Active user sessions', :clean_gitlab_redis_shared_state do
user = create(:user) user = create(:user)
personal_access_token = create(:personal_access_token, user: user) personal_access_token = create(:personal_access_token, user: user)
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
redis.sadd("session:lookup:user:gitlab:#{user.id}", '59822c7d9fcdfa03725eff41782ad97d') redis.sadd("session:lookup:user:gitlab:#{user.id}", '59822c7d9fcdfa03725eff41782ad97d')
end end
visit user_path(user, :atom, private_token: personal_access_token.token) visit user_path(user, :atom, private_token: personal_access_token.token)
expect(page.status_code).to eq 200 expect(page.status_code).to eq 200
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).to include '59822c7d9fcdfa03725eff41782ad97d' expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).to include '59822c7d9fcdfa03725eff41782ad97d'
end end
end end
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Login', :clean_gitlab_redis_shared_state do RSpec.describe 'Login', :clean_gitlab_redis_sessions do
include TermsHelper include TermsHelper
include UserLoginHelper include UserLoginHelper
include SessionHelpers include SessionHelpers
...@@ -84,7 +84,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_shared_state do ...@@ -84,7 +84,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_shared_state do
expect(page).to have_content('Your account has been blocked.') expect(page).to have_content('Your account has been blocked.')
end end
it 'does not update Devise trackable attributes', :clean_gitlab_redis_shared_state do it 'does not update Devise trackable attributes' do
expect(authentication_metrics) expect(authentication_metrics)
.to increment(:user_blocked_counter) .to increment(:user_blocked_counter)
.and increment(:user_unauthenticated_counter) .and increment(:user_unauthenticated_counter)
...@@ -161,7 +161,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_shared_state do ...@@ -161,7 +161,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_shared_state do
expect(page).to have_content('Invalid login or password.') expect(page).to have_content('Invalid login or password.')
end end
it 'does not update Devise trackable attributes', :clean_gitlab_redis_shared_state do it 'does not update Devise trackable attributes' do
expect(authentication_metrics) expect(authentication_metrics)
.to increment(:user_unauthenticated_counter) .to increment(:user_unauthenticated_counter)
.and increment(:user_password_invalid_counter) .and increment(:user_password_invalid_counter)
......
...@@ -10,25 +10,37 @@ RSpec.describe 'Session initializer for GitLab' do ...@@ -10,25 +10,37 @@ RSpec.describe 'Session initializer for GitLab' do
end end
describe 'config#session_store' do describe 'config#session_store' do
context 'when the GITLAB_REDIS_STORE_WITH_SESSION_STORE env is not set' do context 'when the GITLAB_USE_REDIS_SESSIONS_STORE env is not set' do
before do before do
stub_env('GITLAB_REDIS_STORE_WITH_SESSION_STORE', nil) stub_env('GITLAB_USE_REDIS_SESSIONS_STORE', nil)
end end
it 'initialized as a redis_store with a proper Redis::Store instance' do it 'initialized with Multistore as ENV var defaults to true' do
expect(subject).to receive(:session_store).with(:redis_store, a_hash_including(redis_store: kind_of(::Redis::Store))) expect(subject).to receive(:session_store).with(:redis_store, a_hash_including(redis_store: kind_of(::Redis::Store)))
load_session_store load_session_store
end end
end end
context 'when the GITLAB_REDIS_STORE_WITH_SESSION_STORE env is disabled' do context 'when the GITLAB_USE_REDIS_SESSIONS_STORE env is disabled' do
before do before do
stub_env('GITLAB_REDIS_STORE_WITH_SESSION_STORE', false) stub_env('GITLAB_USE_REDIS_SESSIONS_STORE', false)
end end
it 'initialized as a redis_store with a proper servers configuration' do it 'initialized as a redis_store with a proper servers configuration' do
expect(subject).to receive(:session_store).with(:redis_store, a_hash_including(servers: kind_of(Hash))) expect(subject).to receive(:session_store).with(:redis_store, a_hash_including(redis_store: kind_of(Redis::Store)))
load_session_store
end
end
context 'when the GITLAB_USE_REDIS_SESSIONS_STORE env is enabled' do
before do
stub_env('GITLAB_USE_REDIS_SESSIONS_STORE', true)
end
it 'initialized as a redis_store with a proper servers configuration' do
expect(subject).to receive(:session_store).with(:redis_store, a_hash_including(redis_store: kind_of(::Redis::Store)))
load_session_store load_session_store
end end
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::AnonymousSession, :clean_gitlab_redis_shared_state do RSpec.describe Gitlab::AnonymousSession, :clean_gitlab_redis_sessions do
let(:default_session_id) { '6919a6f1bb119dd7396fadc38fd18d0d' } let(:default_session_id) { '6919a6f1bb119dd7396fadc38fd18d0d' }
let(:additional_session_id) { '7919a6f1bb119dd7396fadc38fd18d0d' } let(:additional_session_id) { '7919a6f1bb119dd7396fadc38fd18d0d' }
...@@ -16,7 +16,7 @@ RSpec.describe Gitlab::AnonymousSession, :clean_gitlab_redis_shared_state do ...@@ -16,7 +16,7 @@ RSpec.describe Gitlab::AnonymousSession, :clean_gitlab_redis_shared_state do
it 'adds session id to proper key' do it 'adds session id to proper key' do
subject.count_session_ip subject.count_session_ip
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
expect(redis.get("session:lookup:ip:gitlab2:127.0.0.1").to_i).to eq 1 expect(redis.get("session:lookup:ip:gitlab2:127.0.0.1").to_i).to eq 1
end end
end end
...@@ -25,7 +25,7 @@ RSpec.describe Gitlab::AnonymousSession, :clean_gitlab_redis_shared_state do ...@@ -25,7 +25,7 @@ RSpec.describe Gitlab::AnonymousSession, :clean_gitlab_redis_shared_state do
freeze_time do freeze_time do
subject.count_session_ip subject.count_session_ip
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
expect(redis.ttl("session:lookup:ip:gitlab2:127.0.0.1")).to eq(24.hours.to_i) expect(redis.ttl("session:lookup:ip:gitlab2:127.0.0.1")).to eq(24.hours.to_i)
end end
end end
...@@ -36,7 +36,7 @@ RSpec.describe Gitlab::AnonymousSession, :clean_gitlab_redis_shared_state do ...@@ -36,7 +36,7 @@ RSpec.describe Gitlab::AnonymousSession, :clean_gitlab_redis_shared_state do
subject.count_session_ip subject.count_session_ip
new_anonymous_session.count_session_ip new_anonymous_session.count_session_ip
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
expect(redis.get("session:lookup:ip:gitlab2:127.0.0.1").to_i).to eq(2) expect(redis.get("session:lookup:ip:gitlab2:127.0.0.1").to_i).to eq(2)
end end
end end
...@@ -45,7 +45,7 @@ RSpec.describe Gitlab::AnonymousSession, :clean_gitlab_redis_shared_state do ...@@ -45,7 +45,7 @@ RSpec.describe Gitlab::AnonymousSession, :clean_gitlab_redis_shared_state do
describe '#stored_sessions' do describe '#stored_sessions' do
it 'returns all anonymous sessions per ip' do it 'returns all anonymous sessions per ip' do
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
redis.set("session:lookup:ip:gitlab2:127.0.0.1", 2) redis.set("session:lookup:ip:gitlab2:127.0.0.1", 2)
end end
...@@ -54,13 +54,13 @@ RSpec.describe Gitlab::AnonymousSession, :clean_gitlab_redis_shared_state do ...@@ -54,13 +54,13 @@ RSpec.describe Gitlab::AnonymousSession, :clean_gitlab_redis_shared_state do
end end
it 'removes obsolete lookup through ip entries' do it 'removes obsolete lookup through ip entries' do
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
redis.set("session:lookup:ip:gitlab2:127.0.0.1", 2) redis.set("session:lookup:ip:gitlab2:127.0.0.1", 2)
end end
subject.cleanup_session_per_ip_count subject.cleanup_session_per_ip_count
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
expect(redis.exists("session:lookup:ip:gitlab2:127.0.0.1")).to eq(false) expect(redis.exists("session:lookup:ip:gitlab2:127.0.0.1")).to eq(false)
end end
end end
......
...@@ -114,6 +114,12 @@ RSpec.describe Gitlab::Redis::MultiStore do ...@@ -114,6 +114,12 @@ RSpec.describe Gitlab::Redis::MultiStore do
end end
RSpec.shared_examples_for 'fallback read from the secondary store' do RSpec.shared_examples_for 'fallback read from the secondary store' do
let(:counter) { Gitlab::Metrics::NullMetric.instance }
before do
allow(Gitlab::Metrics).to receive(:counter).and_return(counter)
end
it 'fallback and execute on secondary instance' do it 'fallback and execute on secondary instance' do
expect(secondary_store).to receive(name).with(*args).and_call_original expect(secondary_store).to receive(name).with(*args).and_call_original
...@@ -128,7 +134,7 @@ RSpec.describe Gitlab::Redis::MultiStore do ...@@ -128,7 +134,7 @@ RSpec.describe Gitlab::Redis::MultiStore do
end end
it 'increment read fallback count metrics' do it 'increment read fallback count metrics' do
expect(multi_store).to receive(:increment_read_fallback_count).with(name) expect(counter).to receive(:increment).with(command: name, instance_name: instance_name)
subject subject
end end
...@@ -401,9 +407,12 @@ RSpec.describe Gitlab::Redis::MultiStore do ...@@ -401,9 +407,12 @@ RSpec.describe Gitlab::Redis::MultiStore do
end end
context 'with unsupported command' do context 'with unsupported command' do
let(:counter) { Gitlab::Metrics::NullMetric.instance }
before do before do
primary_store.flushdb primary_store.flushdb
secondary_store.flushdb secondary_store.flushdb
allow(Gitlab::Metrics).to receive(:counter).and_return(counter)
end end
let_it_be(:key) { "redis:counter" } let_it_be(:key) { "redis:counter" }
...@@ -426,7 +435,7 @@ RSpec.describe Gitlab::Redis::MultiStore do ...@@ -426,7 +435,7 @@ RSpec.describe Gitlab::Redis::MultiStore do
end end
it 'increments method missing counter' do it 'increments method missing counter' do
expect(multi_store).to receive(:increment_method_missing_count).with(:incr) expect(counter).to receive(:increment).with(command: :incr, instance_name: instance_name)
subject subject
end end
......
...@@ -4,4 +4,54 @@ require 'spec_helper' ...@@ -4,4 +4,54 @@ require 'spec_helper'
RSpec.describe Gitlab::Redis::Sessions do RSpec.describe Gitlab::Redis::Sessions do
include_examples "redis_new_instance_shared_examples", 'sessions', Gitlab::Redis::SharedState include_examples "redis_new_instance_shared_examples", 'sessions', Gitlab::Redis::SharedState
describe 'redis instance used in connection pool' do
before do
clear_pool
end
context 'when redis.sessions configuration is not provided' do
it 'uses ::Redis instance' do
expect(described_class).to receive(:config_fallback?).and_return(true)
described_class.pool.with do |redis_instance|
expect(redis_instance).to be_instance_of(::Redis)
end
end
end
context 'when redis.sessions configuration is provided' do
it 'instantiates an instance of MultiStore' do
expect(described_class).to receive(:config_fallback?).and_return(false)
described_class.pool.with do |redis_instance|
expect(redis_instance).to be_instance_of(::Gitlab::Redis::MultiStore)
end
end
end
def clear_pool
described_class.remove_instance_variable(:@pool)
rescue NameError
# raised if @pool was not set; ignore
end
end
describe '#store' do
subject { described_class.store(namespace: described_class::SESSION_NAMESPACE) }
context 'when redis.sessions configuration is provided' do
it 'instantiates ::Redis instance' do
expect(described_class).to receive(:config_fallback?).and_return(true)
expect(subject).to be_instance_of(::Redis::Store)
end
end
context 'when redis.sessions configuration is not provided' do
it 'instantiates an instance of MultiStore' do
expect(described_class).to receive(:config_fallback?).and_return(false)
expect(subject).to be_instance_of(::Gitlab::Redis::MultiStore)
end
end
end
end end
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do RSpec.describe ActiveSession, :clean_gitlab_redis_sessions do
let(:user) do let(:user) do
create(:user).tap do |user| create(:user).tap do |user|
user.current_sign_in_at = Time.current user.current_sign_in_at = Time.current
...@@ -44,7 +44,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do ...@@ -44,7 +44,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
describe '.list' do describe '.list' do
it 'returns all sessions by user' do it 'returns all sessions by user' do
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", Marshal.dump({ session_id: 'a' })) redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", Marshal.dump({ session_id: 'a' }))
redis.set("session:user:gitlab:#{user.id}:59822c7d9fcdfa03725eff41782ad97d", Marshal.dump({ session_id: 'b' })) redis.set("session:user:gitlab:#{user.id}:59822c7d9fcdfa03725eff41782ad97d", Marshal.dump({ session_id: 'b' }))
redis.set("session:user:gitlab:9999:5c8611e4f9c69645ad1a1492f4131358", '') redis.set("session:user:gitlab:9999:5c8611e4f9c69645ad1a1492f4131358", '')
...@@ -62,7 +62,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do ...@@ -62,7 +62,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
end end
it 'does not return obsolete entries and cleans them up' do it 'does not return obsolete entries and cleans them up' do
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", Marshal.dump({ session_id: 'a' })) redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", Marshal.dump({ session_id: 'a' }))
redis.sadd( redis.sadd(
...@@ -76,7 +76,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do ...@@ -76,7 +76,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
expect(ActiveSession.list(user)).to eq [{ session_id: 'a' }] expect(ActiveSession.list(user)).to eq [{ session_id: 'a' }]
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
expect(redis.sscan_each("session:lookup:user:gitlab:#{user.id}").to_a).to eq ['6919a6f1bb119dd7396fadc38fd18d0d'] expect(redis.sscan_each("session:lookup:user:gitlab:#{user.id}").to_a).to eq ['6919a6f1bb119dd7396fadc38fd18d0d']
end end
end end
...@@ -88,7 +88,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do ...@@ -88,7 +88,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
describe '.list_sessions' do describe '.list_sessions' do
it 'uses the ActiveSession lookup to return original sessions' do it 'uses the ActiveSession lookup to return original sessions' do
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
# Emulate redis-rack: https://github.com/redis-store/redis-rack/blob/c75f7f1a6016ee224e2615017fbfee964f23a837/lib/rack/session/redis.rb#L88 # Emulate redis-rack: https://github.com/redis-store/redis-rack/blob/c75f7f1a6016ee224e2615017fbfee964f23a837/lib/rack/session/redis.rb#L88
redis.set("session:gitlab:#{rack_session.private_id}", Marshal.dump({ _csrf_token: 'abcd' })) redis.set("session:gitlab:#{rack_session.private_id}", Marshal.dump({ _csrf_token: 'abcd' }))
...@@ -109,7 +109,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do ...@@ -109,7 +109,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
it 'uses the user lookup table to return session ids' do it 'uses the user lookup table to return session ids' do
session_ids = ['59822c7d9fcdfa03725eff41782ad97d'] session_ids = ['59822c7d9fcdfa03725eff41782ad97d']
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
redis.sadd("session:lookup:user:gitlab:#{user.id}", session_ids) redis.sadd("session:lookup:user:gitlab:#{user.id}", session_ids)
end end
...@@ -119,7 +119,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do ...@@ -119,7 +119,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
describe '.sessions_from_ids' do describe '.sessions_from_ids' do
it 'uses the ActiveSession lookup to return original sessions' do it 'uses the ActiveSession lookup to return original sessions' do
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
# Emulate redis-rack: https://github.com/redis-store/redis-rack/blob/c75f7f1a6016ee224e2615017fbfee964f23a837/lib/rack/session/redis.rb#L88 # Emulate redis-rack: https://github.com/redis-store/redis-rack/blob/c75f7f1a6016ee224e2615017fbfee964f23a837/lib/rack/session/redis.rb#L88
redis.set("session:gitlab:#{rack_session.private_id}", Marshal.dump({ _csrf_token: 'abcd' })) redis.set("session:gitlab:#{rack_session.private_id}", Marshal.dump({ _csrf_token: 'abcd' }))
end end
...@@ -128,7 +128,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do ...@@ -128,7 +128,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
end end
it 'avoids a redis lookup for an empty array' do it 'avoids a redis lookup for an empty array' do
expect(Gitlab::Redis::SharedState).not_to receive(:with) expect(Gitlab::Redis::Sessions).not_to receive(:with)
expect(ActiveSession.sessions_from_ids([])).to eq([]) expect(ActiveSession.sessions_from_ids([])).to eq([])
end end
...@@ -137,7 +137,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do ...@@ -137,7 +137,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
stub_const('ActiveSession::SESSION_BATCH_SIZE', 1) stub_const('ActiveSession::SESSION_BATCH_SIZE', 1)
redis = double(:redis) redis = double(:redis)
expect(Gitlab::Redis::SharedState).to receive(:with).and_yield(redis) expect(Gitlab::Redis::Sessions).to receive(:with).and_yield(redis)
sessions = %w[session-a session-b] sessions = %w[session-a session-b]
mget_responses = sessions.map { |session| [Marshal.dump(session)]} mget_responses = sessions.map { |session| [Marshal.dump(session)]}
...@@ -151,7 +151,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do ...@@ -151,7 +151,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
it 'sets a new redis entry for the user session and a lookup entry' do it 'sets a new redis entry for the user session and a lookup entry' do
ActiveSession.set(user, request) ActiveSession.set(user, request)
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
expect(redis.scan_each.to_a).to include( expect(redis.scan_each.to_a).to include(
"session:user:gitlab:#{user.id}:2::418729c72310bbf349a032f0bb6e3fce9f5a69df8f000d8ae0ac5d159d8f21ae", "session:user:gitlab:#{user.id}:2::418729c72310bbf349a032f0bb6e3fce9f5a69df8f000d8ae0ac5d159d8f21ae",
"session:lookup:user:gitlab:#{user.id}" "session:lookup:user:gitlab:#{user.id}"
...@@ -201,7 +201,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do ...@@ -201,7 +201,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
describe '.destroy_session' do describe '.destroy_session' do
shared_examples 'removes all session data' do shared_examples 'removes all session data' do
before do before do
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
redis.set("session:user:gitlab:#{user.id}:#{active_session_lookup_key}", '') redis.set("session:user:gitlab:#{user.id}:#{active_session_lookup_key}", '')
# Emulate redis-rack: https://github.com/redis-store/redis-rack/blob/c75f7f1a6016ee224e2615017fbfee964f23a837/lib/rack/session/redis.rb#L88 # Emulate redis-rack: https://github.com/redis-store/redis-rack/blob/c75f7f1a6016ee224e2615017fbfee964f23a837/lib/rack/session/redis.rb#L88
redis.set("session:gitlab:#{rack_session.private_id}", '') redis.set("session:gitlab:#{rack_session.private_id}", '')
...@@ -216,7 +216,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do ...@@ -216,7 +216,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
it 'removes the devise session' do it 'removes the devise session' do
subject subject
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
expect(redis.scan_each(match: "session:gitlab:*").to_a).to be_empty expect(redis.scan_each(match: "session:gitlab:*").to_a).to be_empty
end end
end end
...@@ -224,7 +224,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do ...@@ -224,7 +224,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
it 'removes the lookup entry' do it 'removes the lookup entry' do
subject subject
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
expect(redis.scan_each(match: "session:lookup:user:gitlab:#{user.id}").to_a).to be_empty expect(redis.scan_each(match: "session:lookup:user:gitlab:#{user.id}").to_a).to be_empty
end end
end end
...@@ -232,7 +232,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do ...@@ -232,7 +232,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
it 'removes the ActiveSession' do it 'removes the ActiveSession' do
subject subject
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
expect(redis.scan_each(match: "session:user:gitlab:*").to_a).to be_empty expect(redis.scan_each(match: "session:user:gitlab:*").to_a).to be_empty
end end
end end
...@@ -269,7 +269,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do ...@@ -269,7 +269,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
let(:current_session_id) { '6919a6f1bb119dd7396fadc38fd18d0d' } let(:current_session_id) { '6919a6f1bb119dd7396fadc38fd18d0d' }
before do before do
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
# setup for current user # setup for current user
[current_session_id, '59822c7d9fcdfa03725eff41782ad97d'].each do |session_public_id| [current_session_id, '59822c7d9fcdfa03725eff41782ad97d'].each do |session_public_id|
session_private_id = Rack::Session::SessionId.new(session_public_id).private_id session_private_id = Rack::Session::SessionId.new(session_public_id).private_id
...@@ -303,7 +303,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do ...@@ -303,7 +303,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
session_private_id = Rack::Session::SessionId.new(current_session_id).private_id session_private_id = Rack::Session::SessionId.new(current_session_id).private_id
ActiveSession.destroy_all_but_current(user, request.session) ActiveSession.destroy_all_but_current(user, request.session)
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
expect( expect(
redis.smembers(described_class.lookup_key_name(user.id)) redis.smembers(described_class.lookup_key_name(user.id))
).to eq([session_private_id]) ).to eq([session_private_id])
...@@ -312,7 +312,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do ...@@ -312,7 +312,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
it 'does not remove impersonated sessions' do it 'does not remove impersonated sessions' do
impersonated_session_id = '6919a6f1bb119dd7396fadc38fd18eee' impersonated_session_id = '6919a6f1bb119dd7396fadc38fd18eee'
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
redis.set(described_class.key_name(user.id, impersonated_session_id), redis.set(described_class.key_name(user.id, impersonated_session_id),
Marshal.dump(ActiveSession.new(session_id: Rack::Session::SessionId.new(impersonated_session_id), is_impersonated: true))) Marshal.dump(ActiveSession.new(session_id: Rack::Session::SessionId.new(impersonated_session_id), is_impersonated: true)))
redis.sadd(described_class.lookup_key_name(user.id), impersonated_session_id) redis.sadd(described_class.lookup_key_name(user.id), impersonated_session_id)
...@@ -331,7 +331,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do ...@@ -331,7 +331,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
end end
it 'removes obsolete lookup entries' do it 'removes obsolete lookup entries' do
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", '') redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", '')
redis.sadd("session:lookup:user:gitlab:#{user.id}", '6919a6f1bb119dd7396fadc38fd18d0d') redis.sadd("session:lookup:user:gitlab:#{user.id}", '6919a6f1bb119dd7396fadc38fd18d0d')
redis.sadd("session:lookup:user:gitlab:#{user.id}", '59822c7d9fcdfa03725eff41782ad97d') redis.sadd("session:lookup:user:gitlab:#{user.id}", '59822c7d9fcdfa03725eff41782ad97d')
...@@ -339,7 +339,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do ...@@ -339,7 +339,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
ActiveSession.cleanup(user) ActiveSession.cleanup(user)
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).to eq ['6919a6f1bb119dd7396fadc38fd18d0d'] expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).to eq ['6919a6f1bb119dd7396fadc38fd18d0d']
end end
end end
...@@ -353,7 +353,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do ...@@ -353,7 +353,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
let(:max_number_of_sessions_plus_two) { ActiveSession::ALLOWED_NUMBER_OF_ACTIVE_SESSIONS + 2 } let(:max_number_of_sessions_plus_two) { ActiveSession::ALLOWED_NUMBER_OF_ACTIVE_SESSIONS + 2 }
before do before do
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
(1..max_number_of_sessions_plus_two).each do |number| (1..max_number_of_sessions_plus_two).each do |number|
redis.set( redis.set(
"session:user:gitlab:#{user.id}:#{number}", "session:user:gitlab:#{user.id}:#{number}",
...@@ -370,7 +370,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do ...@@ -370,7 +370,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
it 'removes obsolete active sessions entries' do it 'removes obsolete active sessions entries' do
ActiveSession.cleanup(user) ActiveSession.cleanup(user)
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
sessions = redis.scan_each(match: "session:user:gitlab:#{user.id}:*").to_a sessions = redis.scan_each(match: "session:user:gitlab:#{user.id}:*").to_a
expect(sessions.count).to eq(ActiveSession::ALLOWED_NUMBER_OF_ACTIVE_SESSIONS) expect(sessions.count).to eq(ActiveSession::ALLOWED_NUMBER_OF_ACTIVE_SESSIONS)
...@@ -381,7 +381,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do ...@@ -381,7 +381,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
it 'removes obsolete lookup entries' do it 'removes obsolete lookup entries' do
ActiveSession.cleanup(user) ActiveSession.cleanup(user)
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
lookup_entries = redis.smembers("session:lookup:user:gitlab:#{user.id}") lookup_entries = redis.smembers("session:lookup:user:gitlab:#{user.id}")
expect(lookup_entries.count).to eq(ActiveSession::ALLOWED_NUMBER_OF_ACTIVE_SESSIONS) expect(lookup_entries.count).to eq(ActiveSession::ALLOWED_NUMBER_OF_ACTIVE_SESSIONS)
...@@ -390,7 +390,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do ...@@ -390,7 +390,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
end end
it 'removes obsolete lookup entries even without active session' do it 'removes obsolete lookup entries even without active session' do
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
redis.sadd( redis.sadd(
"session:lookup:user:gitlab:#{user.id}", "session:lookup:user:gitlab:#{user.id}",
"#{max_number_of_sessions_plus_two + 1}" "#{max_number_of_sessions_plus_two + 1}"
...@@ -399,7 +399,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do ...@@ -399,7 +399,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
ActiveSession.cleanup(user) ActiveSession.cleanup(user)
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
lookup_entries = redis.smembers("session:lookup:user:gitlab:#{user.id}") lookup_entries = redis.smembers("session:lookup:user:gitlab:#{user.id}")
expect(lookup_entries.count).to eq(ActiveSession::ALLOWED_NUMBER_OF_ACTIVE_SESSIONS) expect(lookup_entries.count).to eq(ActiveSession::ALLOWED_NUMBER_OF_ACTIVE_SESSIONS)
...@@ -413,7 +413,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do ...@@ -413,7 +413,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
context 'when the number of active sessions is lower than the limit' do context 'when the number of active sessions is lower than the limit' do
before do before do
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
((max_number_of_sessions_plus_two - 4)..max_number_of_sessions_plus_two).each do |number| ((max_number_of_sessions_plus_two - 4)..max_number_of_sessions_plus_two).each do |number|
redis.del("session:user:gitlab:#{user.id}:#{number}") redis.del("session:user:gitlab:#{user.id}:#{number}")
end end
...@@ -421,17 +421,17 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do ...@@ -421,17 +421,17 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
end end
it 'does not remove active session entries, but removes lookup entries' do it 'does not remove active session entries, but removes lookup entries' do
lookup_entries_before_cleanup = Gitlab::Redis::SharedState.with do |redis| lookup_entries_before_cleanup = Gitlab::Redis::Sessions.with do |redis|
redis.smembers("session:lookup:user:gitlab:#{user.id}") redis.smembers("session:lookup:user:gitlab:#{user.id}")
end end
sessions_before_cleanup = Gitlab::Redis::SharedState.with do |redis| sessions_before_cleanup = Gitlab::Redis::Sessions.with do |redis|
redis.scan_each(match: "session:user:gitlab:#{user.id}:*").to_a redis.scan_each(match: "session:user:gitlab:#{user.id}:*").to_a
end end
ActiveSession.cleanup(user) ActiveSession.cleanup(user)
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
lookup_entries = redis.smembers("session:lookup:user:gitlab:#{user.id}") lookup_entries = redis.smembers("session:lookup:user:gitlab:#{user.id}")
sessions = redis.scan_each(match: "session:user:gitlab:#{user.id}:*").to_a sessions = redis.scan_each(match: "session:user:gitlab:#{user.id}:*").to_a
expect(sessions.count).to eq(sessions_before_cleanup.count) expect(sessions.count).to eq(sessions_before_cleanup.count)
...@@ -446,7 +446,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do ...@@ -446,7 +446,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
let(:max_number_of_sessions_plus_two) { ActiveSession::ALLOWED_NUMBER_OF_ACTIVE_SESSIONS + 2 } let(:max_number_of_sessions_plus_two) { ActiveSession::ALLOWED_NUMBER_OF_ACTIVE_SESSIONS + 2 }
before do before do
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
(1..max_number_of_sessions_plus_two).each do |number| (1..max_number_of_sessions_plus_two).each do |number|
redis.set( redis.set(
"session:user:gitlab:#{user.id}:#{number}", "session:user:gitlab:#{user.id}:#{number}",
...@@ -463,7 +463,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do ...@@ -463,7 +463,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
it 'removes obsolete active sessions entries' do it 'removes obsolete active sessions entries' do
ActiveSession.cleanup(user) ActiveSession.cleanup(user)
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
sessions = redis.scan_each(match: "session:user:gitlab:#{user.id}:*").to_a sessions = redis.scan_each(match: "session:user:gitlab:#{user.id}:*").to_a
expect(sessions.count).to eq(ActiveSession::ALLOWED_NUMBER_OF_ACTIVE_SESSIONS) expect(sessions.count).to eq(ActiveSession::ALLOWED_NUMBER_OF_ACTIVE_SESSIONS)
......
...@@ -377,11 +377,11 @@ RSpec.describe API::Commits do ...@@ -377,11 +377,11 @@ RSpec.describe API::Commits do
end end
context 'when using warden' do context 'when using warden' do
it 'increments usage counters', :clean_gitlab_redis_shared_state do it 'increments usage counters', :clean_gitlab_redis_sessions do
session_id = Rack::Session::SessionId.new('6919a6f1bb119dd7396fadc38fd18d0d') session_id = Rack::Session::SessionId.new('6919a6f1bb119dd7396fadc38fd18d0d')
session_hash = { 'warden.user.user.key' => [[user.id], user.encrypted_password[0, 29]] } session_hash = { 'warden.user.user.key' => [[user.id], user.encrypted_password[0, 29]] }
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
redis.set("session:gitlab:#{session_id.private_id}", Marshal.dump(session_hash)) redis.set("session:gitlab:#{session_id.private_id}", Marshal.dump(session_hash))
end end
......
...@@ -17,10 +17,10 @@ module SessionHelpers ...@@ -17,10 +17,10 @@ module SessionHelpers
end end
def get_session_keys def get_session_keys
Gitlab::Redis::SharedState.with { |redis| redis.scan_each(match: 'session:gitlab:*').to_a } Gitlab::Redis::Sessions.with { |redis| redis.scan_each(match: 'session:gitlab:*').to_a }
end end
def get_ttl(key) def get_ttl(key)
Gitlab::Redis::SharedState.with { |redis| redis.ttl(key) } Gitlab::Redis::Sessions.with { |redis| redis.ttl(key) }
end end
end end
...@@ -93,18 +93,23 @@ RSpec.shared_examples "redis_shared_examples" do ...@@ -93,18 +93,23 @@ RSpec.shared_examples "redis_shared_examples" do
subject { described_class.new(rails_env).store } subject { described_class.new(rails_env).store }
shared_examples 'redis store' do shared_examples 'redis store' do
let(:redis_store) { ::Redis::Store }
let(:redis_store_to_s) { "Redis Client connected to #{host} against DB #{redis_database}" }
it 'instantiates Redis::Store' do it 'instantiates Redis::Store' do
is_expected.to be_a(::Redis::Store) is_expected.to be_a(redis_store)
expect(subject.to_s).to eq("Redis Client connected to #{host} against DB #{redis_database}")
expect(subject.to_s).to eq(redis_store_to_s)
end end
context 'with the namespace' do context 'with the namespace' do
let(:namespace) { 'namespace_name' } let(:namespace) { 'namespace_name' }
let(:redis_store_to_s) { "Redis Client connected to #{host} against DB #{redis_database} with namespace #{namespace}" }
subject { described_class.new(rails_env).store(namespace: namespace) } subject { described_class.new(rails_env).store(namespace: namespace) }
it "uses specified namespace" do it "uses specified namespace" do
expect(subject.to_s).to eq("Redis Client connected to #{host} against DB #{redis_database} with namespace #{namespace}") expect(subject.to_s).to eq(redis_store_to_s)
end end
end end
end end
......
...@@ -18,19 +18,19 @@ RSpec.shared_examples 'snippet edit usage data counters' do ...@@ -18,19 +18,19 @@ RSpec.shared_examples 'snippet edit usage data counters' do
end end
end end
context 'when user is not sessionless' do context 'when user is not sessionless', :clean_gitlab_redis_sessions do
before do before do
session_id = Rack::Session::SessionId.new('6919a6f1bb119dd7396fadc38fd18d0d') session_id = Rack::Session::SessionId.new('6919a6f1bb119dd7396fadc38fd18d0d')
session_hash = { 'warden.user.user.key' => [[current_user.id], current_user.encrypted_password[0, 29]] } session_hash = { 'warden.user.user.key' => [[current_user.id], current_user.encrypted_password[0, 29]] }
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
redis.set("session:gitlab:#{session_id.private_id}", Marshal.dump(session_hash)) redis.set("session:gitlab:#{session_id.private_id}", Marshal.dump(session_hash))
end end
cookies[Gitlab::Application.config.session_options[:key]] = session_id.public_id cookies[Gitlab::Application.config.session_options[:key]] = session_id.public_id
end end
it 'tracks usage data actions', :clean_gitlab_redis_shared_state do it 'tracks usage data actions', :clean_gitlab_redis_sessions do
expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).to receive(:track_snippet_editor_edit_action) expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).to receive(:track_snippet_editor_edit_action)
post_graphql_mutation(mutation) post_graphql_mutation(mutation)
......
...@@ -166,14 +166,14 @@ RSpec.describe 'gitlab:cleanup rake tasks', :silence_stdout do ...@@ -166,14 +166,14 @@ RSpec.describe 'gitlab:cleanup rake tasks', :silence_stdout do
end end
context 'sessions' do context 'sessions' do
describe 'gitlab:cleanup:sessions:active_sessions_lookup_keys', :clean_gitlab_redis_shared_state do describe 'gitlab:cleanup:sessions:active_sessions_lookup_keys', :clean_gitlab_redis_sessions do
subject(:rake_task) { run_rake_task('gitlab:cleanup:sessions:active_sessions_lookup_keys') } subject(:rake_task) { run_rake_task('gitlab:cleanup:sessions:active_sessions_lookup_keys') }
let!(:user) { create(:user) } let!(:user) { create(:user) }
let(:existing_session_id) { '5' } let(:existing_session_id) { '5' }
before do before do
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
redis.set("session:user:gitlab:#{user.id}:#{existing_session_id}", redis.set("session:user:gitlab:#{user.id}:#{existing_session_id}",
Marshal.dump(true)) Marshal.dump(true))
redis.sadd("session:lookup:user:gitlab:#{user.id}", (1..10).to_a) redis.sadd("session:lookup:user:gitlab:#{user.id}", (1..10).to_a)
...@@ -185,7 +185,7 @@ RSpec.describe 'gitlab:cleanup rake tasks', :silence_stdout do ...@@ -185,7 +185,7 @@ RSpec.describe 'gitlab:cleanup rake tasks', :silence_stdout do
end end
it 'removes expired active session lookup keys' do it 'removes expired active session lookup keys' do
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::Sessions.with do |redis|
lookup_key = "session:lookup:user:gitlab:#{user.id}" lookup_key = "session:lookup:user:gitlab:#{user.id}"
expect { subject }.to change { redis.scard(lookup_key) }.from(10).to(1) expect { subject }.to change { redis.scard(lookup_key) }.from(10).to(1)
expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).to( expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).to(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment