Skip to content

Commit 3a41ce9

Browse files
committed
Adds authCache that stores auth promises
1 parent 5b477c8 commit 3a41ce9

File tree

1 file changed

+34
-11
lines changed

1 file changed

+34
-11
lines changed

src/LiveQuery/ParseLiveQueryServer.js

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import uuid from 'uuid';
1313
import { runLiveQueryEventHandlers } from '../triggers';
1414
import { getAuthForSessionToken, Auth } from '../Auth';
1515
import { getCacheController } from '../Controllers';
16+
import LRU from 'lru-cache';
1617

1718
class ParseLiveQueryServer {
1819
clients: Map;
@@ -45,8 +46,17 @@ class ParseLiveQueryServer {
4546
Parse.serverURL = serverURL;
4647
Parse.initialize(config.appId, Parse.javaScriptKey, config.masterKey);
4748

49+
// The cache controller is a proper cache controller
50+
// With access to User and Roles
4851
this.cacheController = getCacheController(config)
4952

53+
// This auth cache stores the promises for each auth resolution
54+
// The main benefit is to be able to reuse the same user / session token resolution
55+
// And to chain
56+
this.authCache = new LRU({
57+
max: 500, // 500 concurrent
58+
maxAge: 60 * 60 * 1000 // 1h
59+
});
5060
// Initialize websocket server
5161
this.parseWebSocketServer = new ParseWebSocketServer(
5262
server,
@@ -334,24 +344,38 @@ class ParseLiveQueryServer {
334344
}
335345

336346
async getAuthForSessionToken(sessionToken: ?string): { auth: ?Auth, userId: ?string } {
347+
if (!sessionToken) {
348+
return {};
349+
}
350+
const fromCache = this.authCache.get(sessionToken);
351+
if (fromCache) {
352+
return fromCache;
353+
}
337354
try {
338-
const auth = await getAuthForSessionToken({ cacheController: this.cacheController, sessionToken: sessionToken });
339-
return { auth, userId: auth && auth.user && auth.user.id }// return the ID of the found user
355+
const authPromise = getAuthForSessionToken({ cacheController: this.cacheController, sessionToken: sessionToken })
356+
.then((auth) => {
357+
return { auth, userId: auth && auth.user && auth.user.id };
358+
}, () => {
359+
// If you can't continue, let's just wrap it up and delete it.
360+
// Next time, one will try again
361+
this.authCache.del(sessionToken);
362+
});
363+
this.authCache.set(sessionToken, authPromise);
364+
return authPromise;
340365
} catch(e) { /* ignore errors */ }
341366
return {};
342367
}
343368

344369
async _matchesCLP(classLevelPermissions: ?any, object: any, client: any, requestId: number, op: string): any {
345370
// try to match on user first, less expensive than with roles
346371
const subscriptionInfo = client.getSubscriptionInfo(requestId);
347-
if (typeof subscriptionInfo === 'undefined') {
348-
return Promise.resolve(['*']);
349-
}
350-
const subscriptionSessionToken = subscriptionInfo.sessionToken;
351372
const aclGroup = ['*'];
352-
const { userId } = await this.getAuthForSessionToken(subscriptionSessionToken);
353-
if (userId) {
354-
aclGroup.push(userId);
373+
let userId;
374+
if (typeof subscriptionInfo !== 'undefined') {
375+
const { userId } = await this.getAuthForSessionToken(subscriptionInfo.sessionToken);
376+
if (userId) {
377+
aclGroup.push(userId);
378+
}
355379
}
356380
try {
357381
await SchemaController.validatePermission(classLevelPermissions, object.className, aclGroup, op);
@@ -390,9 +414,8 @@ class ParseLiveQueryServer {
390414
return Promise.resolve(false);
391415
}
392416

393-
const subscriptionSessionToken = subscriptionInfo.sessionToken;
394417
// TODO: get auth there and de-duplicate code below to work with the same Auth obj.
395-
const { auth, userId } = await this.getAuthForSessionToken(subscriptionSessionToken);
418+
const { auth, userId } = await this.getAuthForSessionToken(subscriptionInfo.sessionToken);
396419
const isSubscriptionSessionTokenMatched = acl.getReadAccess(userId);
397420
if (isSubscriptionSessionTokenMatched) {
398421
return Promise.resolve(true);

0 commit comments

Comments
 (0)