-
-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Live query CLP #4387
Live query CLP #4387
Conversation
7e05e2f
to
3e31d7f
Compare
Codecov Report
@@ Coverage Diff @@
## master #4387 +/- ##
=========================================
+ Coverage 93.75% 93.8% +0.05%
=========================================
Files 123 123
Lines 8884 8915 +31
=========================================
+ Hits 8329 8363 +34
+ Misses 555 552 -3
Continue to review full report at Codecov.
|
logger.verbose(`Failed matching CLP for ${object.id} ${foundUserId} ${e}`); | ||
return Parse.Promise.as(false); | ||
} | ||
// TODO: handle roles permissions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can probably let those out for now, because
- nothing was supported before
- The default behaviour is the safe one.
- roles require more i/o / query, and could be cached properly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm, I don't know about this one. Having the live query responses behave differently in terms of roles could cause some unexpected headaches :/.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An alternative could be a cache of roles?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep, we'd need a roles cache somehow, perhaps refactoring the Auth module.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactoring is finally done in #4940 :)
spec/helper.js
Outdated
const newConfiguration = Object.assign({}, defaultConfiguration, changedConfiguration, { | ||
__indexBuildCompletionCallbackForTests: indexBuildPromise => indexBuildPromise.then(resolve, reject), | ||
__indexBuildCompletionCallbackForTests: indexBuildPromise => indexBuildPromise.then(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused here, what is going on? I don't see who wants this parse server back? And I don't see how line 140 is or needs to be changed?? just trying to follow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We resolve with the parseServer instance now, that has access to everything, config. HttpServer etc...
} | ||
return Parse.Promise.as(['*']); | ||
}).then((aclGroup) => { | ||
console.log(aclGroup); // eslint-disable-line |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prolly didn't mean to leave this in?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope my mistake :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm. I had a nit and question.
} | ||
|
||
_getCLPOperation(query: any) { | ||
return typeof query == 'object' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prefer ===
Just FWI, i didn’t manage to do E2E test on it... |
sorry to hear that ;) |
.travis.yml
Outdated
@@ -52,7 +52,6 @@ jobs: | |||
env: | |||
before_script: skip | |||
after_script: skip | |||
script: npm install -g nsp && nsp check |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Liking that we have nsp showing up as a separate check now 👍
b968d2f
to
9b623a7
Compare
289c37e
to
3f31039
Compare
3a41ce9
to
c4f90b8
Compare
@flavionegrao Can you have a look at this one? I believe it shoudl be pefromant enough for your needs. Let me know if you need anything more ;) @acinader Can you have a look? the last feature missing is still checking for role permissions in CLPs if needed. I'll finish it up tomorrow most likely. This will fit well in the v3.0! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like you have more to do.
unfortunately, I am missing some base level knowledge to be of much help reviewing this. interesting for me to read, but I can't really validate the logic.
I do see how this would be a big performance win, so we should get it out there.
src/Auth.js
Outdated
@@ -5,8 +5,9 @@ const Parse = require('parse/node'); | |||
// An Auth object tells you who is requesting something and whether | |||
// the master key was used. | |||
// userObject is a Parse.User and can be null if there's no user. | |||
function Auth({ config, isMaster = false, isReadOnly = false, user, installationId } = {}) { | |||
function Auth({ config, cacheController = undefined, isMaster = false, isReadOnly = false, user, installationId }) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
may as well match the false
pattern of the rest of the signature instead of using underfined
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
undefined is more accurate as it's an object, and it may not be passed :)
src/Auth.js
Outdated
if (results.length !== 1 || !results[0]['user']) { | ||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token'); | ||
} | ||
var now = new Date(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make const
src/Auth.js
Outdated
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, | ||
'Session token is expired.'); | ||
} | ||
var obj = results[0]['user']; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make const
src/Auth.js
Outdated
cacheController.user.put(sessionToken, obj); | ||
} | ||
const userObject = Parse.Object.fromJSON(obj); | ||
return new Auth({config, cacheController, isMaster: false, installationId, user: userObject }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing whitespace { config
src/Auth.js
Outdated
}; | ||
|
||
var getAuthForLegacySessionToken = function({config, sessionToken, installationId } = {}) { | ||
var getAuthForLegacySessionToken = function({config, sessionToken, installationId }) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing whitespace{ config
src/Auth.js
Outdated
|
||
return new RestQuery(this.config, master(this.config), '_Role', restWhere, {}) | ||
.execute() | ||
.then(({ results }) => results); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
like that ;)
src/Controllers/SchemaController.js
Outdated
return SchemaController.testBaseCLP(this.perms[className], aclGroup, operation); | ||
} | ||
|
||
static testBaseCLP(classPermissions: ?any, aclGroup: string[], operation: string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hm. object member testBaseCLP
and static member testBaseCLP
...fishy?
Parse.initialize(config.appId, Parse.javaScriptKey, config.masterKey); | ||
|
||
// The cache controller is a proper cache controller | ||
// With access to User and Roles |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lower case w in with: with access to....
this.cacheController = getCacheController(config) | ||
|
||
// This auth cache stores the promises for each auth resolution | ||
// The main benefit is to be able to reuse the same user / session token resolution |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
leading caps.....
const authPromise = getAuthForSessionToken({ cacheController: this.cacheController, sessionToken: sessionToken }) | ||
.then((auth) => { | ||
return { auth, userId: auth && auth.user && auth.user.id }; | ||
}, () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe make the error handler a catch
instead?
ecc212b
to
08bba4f
Compare
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
@acinader I would still wish to bring that one in as it contains very important roles performance improvements for live queries :) |
eab73bb
to
bf467b2
Compare
const promise = parseLiveQueryServer.getAuthForSessionToken('pleaseThrow'); | ||
expect(parseLiveQueryServer.authCache.get('pleaseThrow')).toBe(promise); | ||
// after the promise finishes, it should have removed it from the cache | ||
expect(await promise).toEqual({}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't the promise be rejected? is this testing that? i'm confused.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No it should not, the error should be swallowed and the cache should be cleaned if it throws. this the behaviour that I expected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
The new system will fix, an important problem when a Parse.User is referenced in more than 100 Roles. Because, the Parse Live Server get the first 100 randoms Roles where the user is referenced so sometimes ( and more and more often), the Role associated to the current Object Event doesn't exist in results and then the Live Query fail... |
// If you can't continue, let's just wrap it up and delete it. | ||
// Next time, one will try again | ||
this.authCache.del(sessionToken); | ||
return {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@westhom This is why the CPU is spinning like a madman on invalid tokens. here we delete the entry in the cache which forces the session to be retried. If the session token is invalid, it's unlikely it will become valid any time soon.
@acinader @dplewis what do you think? should we
- continue with a default auth and no user ID and expire after a shorter period like 5 minutes?
- cut the connection
- reject connections where the auth token is invalid?
What would be best?
* Auth module refactoring in order to be reusable * Ensure cache controller is properly forwarded from helpers * Nits * Adds support for static validation * Adds support for CLP in Live query (no support for roles yet) * Adds e2e test to validate liveQuery hooks is properly called * Adds tests over LiveQueryController to ensure data is correctly transmitted * nits * Fixes for flow types * Removes usage of Parse.Promise * Use the Auth module for authentication and caches * Cleaner implementation of getting auth * Adds authCache that stores auth promises * Proper testing of the caching * nits
Just a proof of concept for now, we'll see how down the rabbit hole it will lead us.
The premise is passing the perms alongside the object when notifying the LiveQuery server so it can do the matches agains the clients without having to access the DB.
I wanna check how badly it is in terms of coverage now that I refactored it all.