class Aws::SessionStore::DynamoDB::Locking::Pessimistic

This class implements a pessimistic locking strategy for the DynamoDB session handler. Sessions obtain an exclusive lock for reads that is only released when the session is saved.

Public Instance Methods

get_session_data(env, sid) click to toggle source

Gets session from database and places a lock on the session while you are reading from the database.

# File lib/aws/session_store/dynamo_db/locking/pessimistic.rb, line 15
def get_session_data(env, sid)
  handle_error(env) do
    get_session_with_lock(env, sid)
  end
end
set_session_data(env, sid, session, options = {}) click to toggle source

Saves the session.

# File lib/aws/session_store/dynamo_db/locking/pessimistic.rb, line 9
def set_session_data(env, sid, session, options = {})
  super(env, sid, session, set_lock_options(env, options))
end

Private Instance Methods

add_attr() click to toggle source

Option to delete lock.

# File lib/aws/session_store/dynamo_db/locking/pessimistic.rb, line 132
def add_attr
  { 'locked_at' => { action: 'DELETE' } }
end
add_lock_attrs(env) click to toggle source

Attributes for locking.

# File lib/aws/session_store/dynamo_db/locking/pessimistic.rb, line 115
def add_lock_attrs(env)
  {
    add_attrs: add_attr, expect_attr: expect_lock_time(env)
  }
end
attempt_set_lock(sid) click to toggle source

Attempt to place a lock on the session.

# File lib/aws/session_store/dynamo_db/locking/pessimistic.rb, line 97
def attempt_set_lock(sid)
  @config.dynamo_db_client.update_item(obtain_lock_opts(sid, lock_expect))
end
bust_lock(sid, expires_at) click to toggle source

Attempt to bust the lock if the expiration date has expired.

# File lib/aws/session_store/dynamo_db/locking/pessimistic.rb, line 74
def bust_lock(sid, expires_at)
  return unless expires_at < Time.now.to_f

  @config.dynamo_db_client.update_item(obtain_lock_opts(sid))
end
exceeded_wait_time?(max_attempt_date) click to toggle source

Determine if session has waited too long to obtain lock.

@raise [Error] When time for attempting to get lock has

been exceeded.
# File lib/aws/session_store/dynamo_db/locking/pessimistic.rb, line 49
def exceeded_wait_time?(max_attempt_date)
  lock_error = Aws::SessionStore::DynamoDB::Errors::LockWaitTimeoutError
  raise lock_error if Time.now.to_f > max_attempt_date
end
expect_lock_time(env) click to toggle source

Expectation of when lock was set.

# File lib/aws/session_store/dynamo_db/locking/pessimistic.rb, line 137
def expect_lock_time(env)
  {
    expected: {
      'locked_at' => {
        value: (env['locked_at']).to_s,
        exists: true
      }
    }
  }
end
get_data(env, result) click to toggle source

@return [String] Session data.

# File lib/aws/session_store/dynamo_db/locking/pessimistic.rb, line 66
def get_data(env, result)
  lock_time = result[:attributes]['locked_at']
  env['locked_at'] = lock_time.to_f
  env['rack.initial_data'] = result[:item]['data'] if result.members.include? :item
  unpack_data(result[:attributes]['data'])
end
get_expire_date(sid) click to toggle source

Get the expiration date for the session

# File lib/aws/session_store/dynamo_db/locking/pessimistic.rb, line 91
def get_expire_date(sid)
  lock_date = lock_time(sid)
  lock_date + (0.001 * @config.lock_expiry_time) if lock_date
end
get_lock_time_opts(sid) click to toggle source

@return [Hash] Options hash for placing a lock on a session.

# File lib/aws/session_store/dynamo_db/locking/pessimistic.rb, line 55
def get_lock_time_opts(sid)
  merge_all(table_opts(sid), lock_opts)
end
get_session_with_lock(env, sid) click to toggle source

Get session with implemented locking strategy. rubocop:disable Metrics/MethodLength

# File lib/aws/session_store/dynamo_db/locking/pessimistic.rb, line 25
def get_session_with_lock(env, sid)
  expires_at = nil
  result = nil
  max_attempt_date = Time.now.to_f + @config.lock_max_wait_time
  while result.nil?
    exceeded_wait_time?(max_attempt_date)
    begin
      result = attempt_set_lock(sid)
    rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException
      expires_at ||= get_expire_date(sid)
      next if expires_at.nil?

      result = bust_lock(sid, expires_at)
      wait_to_retry(result)
    end
  end
  get_data(env, result)
end
lock_attr() click to toggle source

Lock attribute - time stamp of when session was locked.

# File lib/aws/session_store/dynamo_db/locking/pessimistic.rb, line 102
def lock_attr
  {
    attribute_updates: { 'locked_at' => updated_at },
    return_values: 'ALL_NEW'
  }
end
lock_expect() click to toggle source

Lock expectation.

# File lib/aws/session_store/dynamo_db/locking/pessimistic.rb, line 127
def lock_expect
  { expected: { 'locked_at' => { exists: false } } }
end
lock_opts() click to toggle source

Attributes to be retrieved via client

# File lib/aws/session_store/dynamo_db/locking/pessimistic.rb, line 149
def lock_opts
  {
    attributes_to_get: ['locked_at'],
    consistent_read: @config.consistent_read
  }
end
lock_time(sid) click to toggle source

@return [Time] Time stamp for which the session was locked.

# File lib/aws/session_store/dynamo_db/locking/pessimistic.rb, line 60
def lock_time(sid)
  result = @config.dynamo_db_client.get_item(get_lock_time_opts(sid))
  (result[:item]['locked_at']).to_f if result[:item]['locked_at']
end
obtain_lock_opts(sid, add_opt = {}) click to toggle source

@return [Hash] Options hash for obtaining the lock.

# File lib/aws/session_store/dynamo_db/locking/pessimistic.rb, line 81
def obtain_lock_opts(sid, add_opt = {})
  merge_all(table_opts(sid), lock_attr, add_opt)
end
set_lock_options(env, options = {}) click to toggle source

Lock options for setting lock.

# File lib/aws/session_store/dynamo_db/locking/pessimistic.rb, line 122
def set_lock_options(env, options = {})
  merge_all(options, add_lock_attrs(env))
end
updated_at() click to toggle source

Time in which session was updated.

# File lib/aws/session_store/dynamo_db/locking/pessimistic.rb, line 110
def updated_at
  { value: Time.now.to_f.to_s, action: 'PUT' }
end
wait_to_retry(result) click to toggle source

Sleep for given time period if the session is currently locked.

# File lib/aws/session_store/dynamo_db/locking/pessimistic.rb, line 86
def wait_to_retry(result)
  sleep(0.001 * @config.lock_retry_delay) if result.nil?
end