Skip to content

Question/propostal: can we use SELECT ... FOR UPDATE SKIP LOCKED if using a "token buffer"? #1660

@lucashungaro

Description

@lucashungaro

Sorry about posting a question here - but this is a proposal for a code change (which I can work on if this makes sense to you), so I wanted to check before opening a PR.

We currently use this gem, and it works great. However, we occasionally experience issues due to deadlocks when updating a user's token (this is not caused by the gem itself, but rather a special combination of events occurring on the platform).

Given that we use the following configuration options:

  config.change_headers_on_each_request = true

  config.token_lifespan = 12.hours

  config.max_number_of_devices = 1

  config.batch_request_buffer_throttle = 120.seconds

We have a large buffer (120 seconds) precisely to alleviate that issue. So I was thinking, if we are using a buffer, can't we use a SELECT ... FOR UPDATE SKIP LOCKED or SELECT ... FOR UPDATE NOWAIT when refreshing the token to avoid waiting for locked records? Here's the relevant code:

  def refresh_headers
    # Lock the user record during any auth_header updates to ensure
    # we don't have write contention from multiple threads
    @resource.with_lock do
      # should not append auth header if @resource related token was
      # cleared by sign out in the meantime
      return if @used_auth_by_token && @resource.tokens[@token.client].nil?

      _auth_header_from_batch_request = auth_header_from_batch_request

      # update the response header
      response.headers.merge!(_auth_header_from_batch_request)

      # set a server cookie if configured and is not a batch request
      if DeviseTokenAuth.cookie_enabled && !@is_batch_request
        set_cookie(_auth_header_from_batch_request)
      end
    end # end lock
  end

My thinking is: if we have a buffer and the latest token is still valid (within said buffer), can we try to acquire the lock, but return the previous token if it can't be acquired immediately? I'm reading the code to understand if that's already discarded at the time we acquired the lock (i.e., the buffer is no longer in effect), but wanted to ask before taking a stab at opening a PR.

Thanks in advance.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions