parse-server icon indicating copy to clipboard operation
parse-server copied to clipboard

fix: Use LRUCache for `extendSessionOnUse`

Open dblythy opened this issue 2 years ago • 9 comments
trafficstars

Pull Request

Issue

Currently, extendSessionOnUse functions as a debounce, and does not clear the throttle store.

Closes: #8682

Approach

  • Change timeout logic (debounce) to a throttle, where the first session call is executed, and then subsequent calls do not extend
  • Sessions are no longer looked up and extended if they have been pulled from the session cache, as the only way they get into the session cache in the first place, is if they have been used recently (I believe it's safe to assume they have been extended already)

Tasks

  • [ ] Add new Parse Error codes to Parse JS SDK

dblythy avatar Jul 06 '23 10:07 dblythy

Thanks for opening this pull request!

@Moumouls could you add your thoughts?

dblythy avatar Jul 06 '23 10:07 dblythy

Codecov Report

Attention: Patch coverage is 87.50000% with 2 lines in your changes missing coverage. Please review.

Project coverage is 93.55%. Comparing base (34867b7) to head (f8680e7). Report is 22 commits behind head on alpha.

Files with missing lines Patch % Lines
src/Auth.js 87.50% 2 Missing :warning:
Additional details and impacted files
@@            Coverage Diff             @@
##            alpha    #8683      +/-   ##
==========================================
- Coverage   93.56%   93.55%   -0.02%     
==========================================
  Files         186      186              
  Lines       14836    14838       +2     
==========================================
  Hits        13881    13881              
- Misses        955      957       +2     

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

codecov[bot] avatar Jul 06 '23 10:07 codecov[bot]

I will reformat the title to use the proper commit message syntax.

Is this only a perf PR or does it actually fix a bug, or both - for the changelog entry.

mtrezza avatar Jul 07 '23 10:07 mtrezza

Bug and perf i think @mtrezza

Moumouls avatar Jul 07 '23 10:07 Moumouls

Then what would be a better PR title, to inform developers what the bug is.

mtrezza avatar Jul 07 '23 12:07 mtrezza

@dblythy Could you rewrite the PR title? If the PR includes a bug fix and a perf improvement, then the PR is a fix since that is higher ranked and the perf improvement could be added as additional note.

mtrezza avatar Jul 14 '23 01:07 mtrezza

@Moumouls Do you think you could take a look at this Auth thing as well? We just need to resolve the conflict to merge this, but not sure how to do that. If you could make a suggestion I'd commit the change.

mtrezza avatar Mar 24 '24 00:03 mtrezza

The cache is just to reduce the method from being called constantly when the same session token is used. If there are multiple servers, the session will just be continued to extended every time - it could be optimised by using a distributed cache.

dblythy avatar Jan 29 '25 02:01 dblythy

Ok, I understand that a shared cache in this case is a separate feature that doesn't exist currently, correct?

Sessions are no longer looked up and extended if they have been pulled from the session cache, as the only way they get into the session cache in the first place, is if they have been used recently (I believe it's safe to assume they have been extended already)

Is the session expiration date also in the cache? Would it be possible that a session has expired since getting into the cache and is still in the cache?

mtrezza avatar Jan 29 '25 05:01 mtrezza

Ok, I understand that a shared cache in this case is a separate feature that doesn't exist currently, correct?

Correct

Is the session expiration date also in the cache

The ttl on the cache is 500ms. It's just to stop bursty requests from continuously extending the session

dblythy avatar Jan 29 '25 05:01 dblythy

How do we get to the 500ms? Why not 100ms or 10s?

mtrezza avatar Jan 29 '25 06:01 mtrezza

It's an arbitrary number that is currently used - it just represents the window in which using the same token won't be extended (it will at the start of the 500ms, but not again until 500ms after). Would you suggest higher?

dblythy avatar Jan 29 '25 07:01 dblythy

Just thinking out loud: could there happen a situation where long burst of repeating requests within that window would use the token from LRUCache so many times that they would essentially prevent it from auto extending and make it expire? Thinking LRUCache timeout of 5 seconds and token validity say 60 seconds?

mman avatar Jan 29 '25 08:01 mman

No, I don't think so, the mechanism is a throttle so it should throttle it to one request every 500ms. Previously with the mechanism at a debouncer, this could occur.

The only changes to the current implementation in this PR is:

  • Use a LRU cache instead of a POJO
  • Change the debouncer to a throttle

dblythy avatar Jan 29 '25 08:01 dblythy

Not sure I follow, @dblythy you answered with 500ms to @mman's example of 5s cache TTL. What if the cache TTL was 60 seconds, and the token TTL was 10 seconds? I'm trying to figure out where the boundaries are for the cache TTL, based on a token TTL that the developer can set freely in Parse Server options, lowest value being 1s I believe.

mtrezza avatar Jan 29 '25 09:01 mtrezza

The LRU cache here is a fixed, un-configurable time of 500ms. I think the 5s was just presented as a potential scenario, which wouldn't make a difference anyway as again, the mechanism is a throttle (it will trigger once at the start of every ttl)

In any case, I'm not sure configuration of the ttl value is within the scope, the current implementation is fixed at 500ms.

dblythy avatar Jan 29 '25 09:01 dblythy

Yes, it was a theoretical number to see if we can come up with a scenario that it would break the auto-extend-algorithm. I think we all agree that we want to:

  1. serve requests fast without necessarily checking the sessionToken (requiring a read) on every use
  2. extend the sessionToken validity (thus incurring a write) if we meet the criteria, and only once.
  3. make the algorithm work reliably so when users use the valid sessionToken, it always gets extended and we do not end up in situation where it expires despite being used...

Point 3 being the most important, because this is how Parse Server worked for a long long time, where user would be kicked out after months of actively using the app... and only workaround was to set session length to nonsense number like 10 years... which is a bad practice...

mman avatar Jan 29 '25 09:01 mman

I would make the quick point that in order to properly implement all 3 points, we would need to transitions to JWTs with refresh tokens. This current mechanism is a convenience method but the long term solution is JWTs.

dblythy avatar Jan 29 '25 09:01 dblythy

I would make the quick point that in order to properly implement all 3 points, we would need to transitions to JWTs with refresh tokens. This current mechanism is a convenience method but the long term solution is JWTs.

On the other hand, transitioning to JWT will make everything more complex... the current system is easy to understand and if we get this one right, I think it will work flawlessly...

mman avatar Jan 29 '25 10:01 mman

I agree that the scope here is just the fix as currently presented. I'm trying to understand what constraints the TTL value has, and based on that determine a value that makes most sense.

What would happen if the cache TTL was 60 seconds and the token TTL was 10 seconds? Just to understand whether @mman's concern is valid.

mtrezza avatar Jan 29 '25 11:01 mtrezza

It would

  • add 10 seconds to the token expiry the first time it is used
  • not extend the token for the next 60 seconds (meaning it would expire)
  • meaning that the token would expire

No, the only way the concern is valid is if the session length is less than the throttle ttl (which is why I set it to 500ms)

dblythy avatar Jan 29 '25 11:01 dblythy

Ok, so we have identified the constraint that the cache TTL must be greater than the token TTL. Given that the cache TTL is not configurable, and if the minimum configurable token TTL is 1s, the cache TTL must be < 1s. To avoid race conditions, we'd need to take the cache lookup time into account, which is likely <1ms for a local cache. Taking also other code execution times into account, it should be enough to reserve 10ms in total, but let's make this super safe with 100ms.

So the cache TTL could be up to 900ms.

  • What could be the difference between 500ms and 900ms cache TTL in practice?

  • Would it make sense to set the cache TTL as a function of the token TTL, which we know from the Parse Server config? In theory, someone could issue tokens with different TTLs, so maybe that's not feasible?

  • Another aspect: is it currently possible to configure a token TTL of 0s, and if yes, do we open any vulnerabilities by caching it for 1s?

mtrezza avatar Jan 29 '25 20:01 mtrezza

What could be the difference between 500ms and 900ms cache TTL in practice?

No real difference. The intent is to just prevent the extension being called every time for bursty requests. 900ms would extend the burst window.

With this feature we want to extend the session each time it is used. But if there is a burst of requests, it will continuously extend the session for each request, causing unnecessary load. That's the intent of the throttle.

Another aspect: is it currently possible to configure a token TTL of 0s

Session lengths must be greater than 0

dblythy avatar Feb 03 '25 09:02 dblythy

The intent is to just prevent the extension being called every time for bursty requests

Why and when do these bursts occur in the first place, and in what frequency and duration are they expected?

mtrezza avatar Feb 03 '25 21:02 mtrezza

Why and when do these bursts occur in the first place

It's down to the specific use case, but consider simple functionality that queries an object and then saves it - it would be using the same sessionToken 2 times within <10ms

dblythy avatar Feb 09 '25 05:02 dblythy

Got it, I think that means max. cache TTL must be < min. session token expiration, which is 1 second. If the cache TTL is 2s and the token expiration is 1s, then the cache TTL would effectively override the token expiration with 2s. 900ms may be too close and cause a race condition, still causing the override. So your suggested 500ms seems to be a fair value.

That also answer my previous question "Would it make sense to set the cache TTL as a function of the token TTL," with "no", since token deletion can happen at any time, regardless of what the standard token expiration is set to.

mtrezza avatar Feb 09 '25 12:02 mtrezza

🎉 This change has been released in version 8.0.1-alpha.1

parseplatformorg avatar Mar 06 '25 00:03 parseplatformorg

🎉 This change has been released in version 8.0.1

parseplatformorg avatar Mar 17 '25 01:03 parseplatformorg