fix: share the in-flight sign in so a cold token opens one session - #25
Open
sdbennett wants to merge 1 commit into
Open
fix: share the in-flight sign in so a cold token opens one session#25sdbennett wants to merge 1 commit into
sdbennett wants to merge 1 commit into
Conversation
getToken had no in-flight guard. Every caller arriving while the token was null or older than 14 minutes ran its own sign in, so N concurrent callers opened N Data API sessions. Only the last one was kept in this.token; the rest were never referenced again and never deleted, so they sat against the server's session limit until it timed them out. Consumers hit this whenever a page fans out. A tracker-api endpoint that answers three concurrent requests after every save opened three sessions and abandoned two on any cold start, which is every time someone comes back after a quarter of an hour and logs time. Sharing the pending promise means one sign in however many callers arrive together. The promise is cleared when it settles, so a failed sign in is not cached and the next caller retries rather than being handed a rejection for good. This also covers the invalid-token retry: request() nulls the token on code 952 and calls itself, and concurrent retries now coalesce the same way. That path is not reachable from a subclass, because the retry happens inside request(). Three tests: one sign in for three concurrent callers, recovery after a failed sign in, and every waiter rejecting when the shared sign in fails. Verified by mutation: dropping the shared promise fails the first and leaves the other sixteen in the file passing.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
getTokenhas no in-flight guard:Every caller arriving while the token is
nullor older than 14 minutes passes that check and runs its own sign in. N concurrent callers open N Data API sessions. Only the last one is kept inthis.token; the rest are never referenced again and neverDELETEd, so they sit against the server's session limit until FileMaker times them out.It is invisible from outside the library, because
getTokenis private and the retry path that also triggers it lives insiderequest.Where we hit it
tracker-apiserves a page that fires three concurrentGET /v1/time-entriesafter every save. On any cold start that opened three sessions and abandoned two, which is every time someone comes back after a quarter of an hour and logs time.We first worked around it in the consumer by serialising the first request of an idle period, but that meant mirroring this file's private 14 minute constant from outside, which quietly breaks the moment it changes. Fixing it here removes the workaround and covers every other consumer.
The change
Share the pending promise, and clear it when it settles:
One sign in however many callers arrive together. Clearing on settle rather than on success matters: a rejected sign in must not stay cached, or the client can never recover from one bad login.
The body of the old
getTokenmoves tomintTokenunchanged, so the sign in itself, its error handling andlastCallbookkeeping are untouched.This also covers the invalid-token retry.
requestnulls the token on code 952 and calls itself, so concurrent retries after a server restart or an admin session clear now coalesce the same way. That path cannot be reached from a subclass at all.Tests
Three added to
test/Client.test.ts, in the existingfetchMockstyle:Verified by mutation rather than assumed: dropping the shared promise fails the first and leaves the other sixteen in that file passing.
npx jest68 passing across the three suites,npm run lintclean,npm run buildsucceeds.Compatibility
No public API change.
token,lastCall, the 14 minute window, the sign in request and every error are as before. The only new state is one private field.Raised from a fork because I do not have push access on this repo.
maintainer_can_modifyis on.