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

Live query CLP #4387

Merged
merged 18 commits into from Oct 17, 2018
Merged

Live query CLP #4387

merged 18 commits into from Oct 17, 2018

Conversation

flovilmart
Copy link
Contributor

@flovilmart flovilmart commented Nov 26, 2017

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.

@flovilmart flovilmart force-pushed the liveQuery-CLP branch 2 times, most recently from 7e05e2f to 3e31d7f Compare November 26, 2017 03:17
@parse-community parse-community deleted a comment from codecov bot Nov 26, 2017
@codecov
Copy link

codecov bot commented Nov 26, 2017

Codecov Report

Merging #4387 into master will increase coverage by 0.05%.
The diff coverage is 97.95%.

Impacted file tree graph

@@            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
Impacted Files Coverage Δ
src/Routers/UsersRouter.js 93.46% <ø> (ø) ⬆️
src/triggers.js 94.31% <100%> (ø) ⬆️
src/rest.js 97.7% <100%> (+0.05%) ⬆️
src/Controllers/DatabaseController.js 94.73% <100%> (ø) ⬆️
src/Controllers/LiveQueryController.js 95.65% <100%> (+38.5%) ⬆️
src/RestWrite.js 92.88% <100%> (-0.27%) ⬇️
src/Controllers/SchemaController.js 96.32% <88.88%> (ø) ⬆️
src/LiveQuery/ParseLiveQueryServer.js 87.78% <98.64%> (+1.18%) ⬆️
src/Adapters/Storage/Mongo/MongoStorageAdapter.js 91.52% <0%> (-0.73%) ⬇️
... and 2 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 8dff708...247cc23. Read the comment docs.

logger.verbose(`Failed matching CLP for ${object.id} ${foundUserId} ${e}`);
return Parse.Promise.as(false);
}
// TODO: handle roles permissions
Copy link
Contributor Author

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

Copy link
Contributor

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 :/.

Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor Author

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(() => {
Copy link
Contributor

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.

Copy link
Contributor Author

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
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope my mistake :)

acinader
acinader previously approved these changes Nov 27, 2017
Copy link
Contributor

@acinader acinader left a 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'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prefer ===

@flovilmart
Copy link
Contributor Author

Just FWI, i didn’t manage to do E2E test on it...

@acinader
Copy link
Contributor

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
Copy link
Contributor

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 👍

@flovilmart flovilmart force-pushed the liveQuery-CLP branch 2 times, most recently from 3a41ce9 to c4f90b8 Compare August 9, 2018 02:00
@flovilmart
Copy link
Contributor Author

@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!

acinader
acinader previously approved these changes Aug 9, 2018
Copy link
Contributor

@acinader acinader left a 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 }) {
Copy link
Contributor

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?

Copy link
Contributor Author

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(),
Copy link
Contributor

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'];
Copy link
Contributor

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 });
Copy link
Contributor

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 }) {
Copy link
Contributor

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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

like that ;)

return SchemaController.testBaseCLP(this.perms[className], aclGroup, operation);
}

static testBaseCLP(classPermissions: ?any, aclGroup: string[], operation: string) {
Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Contributor

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 };
}, () => {
Copy link
Contributor

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?

@stale
Copy link

stale bot commented Sep 23, 2018

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.

@stale stale bot added the wontfix label Sep 23, 2018
@stale stale bot closed this Sep 30, 2018
@flovilmart flovilmart reopened this Oct 8, 2018
@stale stale bot removed the wontfix label Oct 8, 2018
@flovilmart
Copy link
Contributor Author

flovilmart commented Oct 9, 2018

@acinader I would still wish to bring that one in as it contains very important roles performance improvements for live queries :)

src/rest.js Show resolved Hide resolved
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({});
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

@acinader acinader left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@Moumouls
Copy link
Member

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...

@flovilmart flovilmart merged commit 7c81290 into master Oct 17, 2018
@flovilmart flovilmart deleted the liveQuery-CLP branch October 17, 2018 21:53
// 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 {};
Copy link
Contributor Author

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?

UnderratedDev pushed a commit to UnderratedDev/parse-server that referenced this pull request Mar 21, 2020
* 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants