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

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

Merged
merged 16 commits into from
Mar 6, 2025
Merged
2 changes: 2 additions & 0 deletions spec/Auth.spec.js
Original file line number Diff line number Diff line change
@@ -117,6 +117,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();
63 changes: 28 additions & 35 deletions src/Auth.js
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ import { isDeepStrictEqual } from 'util';
import { getRequestObject, resolveError } from './triggers';
import Deprecator from './Deprecator/Deprecator';
import { logger } from './logger';
import RestQuery from './RestQuery';
import { LRUCache as LRU } from 'lru-cache';
import RestWrite from './RestWrite';

// An Auth object tells you who is requesting something and whether
@@ -68,44 +68,38 @@ function nobody(config) {
return new Auth({ config, isMaster: false });
}

const throttle = {};
const throttle = new LRU({
max: 10000,
ttl: 500,
});
const renewSessionIfNeeded = async ({ config, session, sessionToken }) => {
if (!config?.extendSessionOnUse) {
return;
}
clearTimeout(throttle[sessionToken]);
throttle[sessionToken] = setTimeout(async () => {
try {
if (!session) {
const { results } = await new RestQuery(
config,
master(config),
'_Session',
{ sessionToken },
{ limit: 1 }
).execute();
session = results[0];
}
const lastUpdated = new Date(session?.updatedAt);
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
if (lastUpdated > yesterday || !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);
}
if (throttle.get(sessionToken)) {
return;
}
throttle.set(sessionToken, true);
try {
const lastUpdated = new Date(session?.updatedAt);
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
if (lastUpdated > yesterday || !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
@@ -120,7 +114,6 @@ const getAuthForSessionToken = async function ({
const userJSON = await cacheController.user.get(sessionToken);
if (userJSON) {
const cachedUser = Parse.Object.fromJSON(userJSON);
renewSessionIfNeeded({ config, sessionToken });
return Promise.resolve(
new Auth({
config,