diff --git a/spec/Auth.spec.js b/spec/Auth.spec.js index 4284c7365b..a055cda5bc 100644 --- a/spec/Auth.spec.js +++ b/spec/Auth.spec.js @@ -106,6 +106,8 @@ describe('Auth', () => { updatedAt: updatedAt.toISOString(), } ); + Parse.Server.cacheController.clear(); + await new Promise(resolve => setTimeout(resolve, 1000)); await session.fetch(); await new Promise(resolve => setTimeout(resolve, 1000)); await session.fetch(); diff --git a/src/Auth.js b/src/Auth.js index dbb34f9a62..d872b1fa62 100644 --- a/src/Auth.js +++ b/src/Auth.js @@ -2,6 +2,7 @@ const Parse = require('parse/node'); import { isDeepStrictEqual } from 'util'; import { getRequestObject, resolveError } from './triggers'; import { logger } from './logger'; +import { LRUCache as LRU } from 'lru-cache'; import RestQuery from './RestQuery'; import RestWrite from './RestWrite'; @@ -67,6 +68,10 @@ function nobody(config) { return new Auth({ config, isMaster: false }); } +const throttle = new LRU({ + max: 10000, + ttl: 500, +}); /** * Checks whether session should be updated based on last update time & session length. */ @@ -78,44 +83,45 @@ function shouldUpdateSessionExpiry(config, session) { return lastUpdated <= skipRange; } -const throttle = {}; const renewSessionIfNeeded = async ({ config, session, sessionToken }) => { if (!config?.extendSessionOnUse) { return; } - clearTimeout(throttle[sessionToken]); - throttle[sessionToken] = setTimeout(async () => { - try { - if (!session) { - const query = await RestQuery({ - method: RestQuery.Method.get, - config, - auth: master(config), - runBeforeFind: false, - className: '_Session', - restWhere: { sessionToken }, - restOptions: { limit: 1 }, - }); - const { results } = await query.execute(); - session = results[0]; - } - if (!shouldUpdateSessionExpiry(config, session) || !session) { - return; - } - const expiresAt = config.generateSessionExpiresAt(); - await new RestWrite( + if (throttle.get(sessionToken)) { + return; + } + throttle.set(sessionToken, true); + try { + if (!session) { + const query = await RestQuery({ + method: RestQuery.Method.get, config, - master(config), - '_Session', - { objectId: session.objectId }, - { expiresAt: Parse._encode(expiresAt) } - ).execute(); - } catch (e) { - if (e?.code !== Parse.Error.OBJECT_NOT_FOUND) { - logger.error('Could not update session expiry: ', e); - } + auth: master(config), + runBeforeFind: false, + className: '_Session', + restWhere: { sessionToken }, + restOptions: { limit: 1 }, + }); + const { results } = await query.execute(); + session = results[0]; + } + + if (!shouldUpdateSessionExpiry(config, session) || !session) { + return; } - }, 500); + const expiresAt = config.generateSessionExpiresAt(); + await new RestWrite( + config, + master(config), + '_Session', + { objectId: session.objectId }, + { expiresAt: Parse._encode(expiresAt) } + ).execute(); + } catch (e) { + if (e?.code !== Parse.Error.OBJECT_NOT_FOUND) { + logger.error('Could not update session expiry: ', e); + } + } }; // Returns a promise that resolves to an Auth object