Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 Handle local storage replication latency #2301

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

yannickadam
Copy link
Contributor

Motivation

When instantiating two browser windows within a short interval (<10ms), there is a high risk that the lock mechanism will not work for local storage. There is a replication latency between browser windows of ~5ms (according to tests).

Changes

  • Postpone reading the lock by 7ms in case local storage is used. This leaves time for the storage to synchronize.
  • Remove the lock when we reach max tries.
    -- The chances for this situation to happen with cookies is extremely low as we'd need to kill the browser in the middle of processStoreOperations, with a lock acquired.
    -- However, the chances for this to happen are much higher for local storage: around 7 out of 1000 per opened window.

Testing

  • Local
  • Staging
  • Unit
  • End to end

I have gone over the contributing documentation.


expect(toSessionState(stubStorage.currentValue(storageKey)).lock).toBeUndefined()

clock.cleanup()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 suggestion: ‏cleanups need to be in an afterEach hook (or maybe a finally statement?) else if the test is failing midway, it'll interfer with other tests.

// Here, typically for local storage, we must WAIT before retrieving the lock. Else, there is a chance another
// process can unknowingly write to the storage. This happens because of a latency to replicate the written value.
if (lockOptions.synchronizationLatency > 0) {
retryLater(operations, sessionStoreStrategy, numberOfRetries, lockOptions.synchronizationLatency)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 suggestion:retry is a bit confusing here, as you don't want to retry but actually continue the operation after a short while.

Maybe you could define a function that wraps everything after this if, and call it in a timeout:

      if (lockOptions.synchronizationLatency > 0) {
        setTimeout(onLockWritten, lockOptions.synchronizationLatency)
      } else {
        onLockWritten()
      }

  function onLockWritten() {
    if (lockOptions.enabled) {
      // If there is no latency, we retrieve our session to check we're still owner of our lock.
      // If the lock has changed, postpone the operation
      currentSession = retrieveSession()
      if (currentSession.lock !== currentLock) {
        retryLater(operations, sessionStoreStrategy, numberOfRetries)
        return
      }
    }

    let processedSession = operations.process(currentSession)

    if (lockOptions.enabled) {
      // If the lock has changed after process, retry later
      currentSession = retrieveSession()
      if (currentSession.lock !== currentLock) {
        retryLater(operations, sessionStoreStrategy, numberOfRetries)
        return
      }
    }

    ...
  }

Also, with this design, you wouldn't need to store the current lock value in the module scope (which adds complexity)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants