fix: Using Parse Server option extendSessionOnUse does not correctly clear memory and functions as a debounce instead of a throttle (#8683)

This commit is contained in:
Daniel
2025-03-06 11:34:52 +11:00
committed by GitHub
parent 3cb3c5f7c6
commit 6258a6a112
2 changed files with 40 additions and 32 deletions

View File

@@ -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();

View File

@@ -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];
}
}, 500);
if (!shouldUpdateSessionExpiry(config, session) || !session) {
return;
}
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