-
Notifications
You must be signed in to change notification settings - Fork 397
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
Bug:throw error in authorizationChecker #157
Comments
Which version do you use? I dont know how authorization checker works behind the scenes, but if it's implemented as a non global middleware, then this issue was fixed in the latest release via #138. |
version is 0.7.0-alpha.10. |
Can you try with the latest 0.7.0-alpha.11 release? |
There's still a bug like this, the source code should be: checkResult.then(result => {
if (!result) {
return this.handleError(new AccessDeniedError(action), actionMetadata, action);
} else {
next();
}
}).catch(error => {
this.handleError(error, actionMetadata, action); // right?
}); |
Hmm, yeah but then it should be prepared to catch synchronous errors as well, something like try {
const checkResult = this.authorizationChecker(action, actionMetadata.authorizedRoles);
if (isPromiseLike(checkResult)) {
checkResult.then(result => {
if (!result) {
return this.handleError(new AccessDeniedError(action), actionMetadata, action);
} else {
next();
}
}).catch(error => this.handleError(error, actionMetadata, action));
});
} else {
if (!checkResult) {
this.handleError(new AccessDeniedError(action), actionMetadata, action);
} else {
next();
}
}
} catch {
this.handleError(new AccessDeniedError(action), actionMetadata, action);
} I will open a PR for this in the evening, but feel free to do it if you feel so. |
What is the purpose of |
@19majkel94 we expect an async function, but nothing guarantees that it will be an async function. So it's better to prepare for a sync |
I know but const checkResult = this.authorizationChecker(action, actionMetadata.authorizedRoles);
Promise.resolve(checkResult).then(result => {
if (!result) {
return this.handleError(new AccessDeniedError(action), actionMetadata, action);
} else {
next();
}
}).catch(error => this.handleError(error, actionMetadata, action)); |
I just kept it the same way as we check for errors in other parts of the app. Your code is smaller, but wrapping |
Just made a quick perf test, even without waiting for the Promise to resolve (so just the overhead of creating a promise every time) the performance ratio of the two is ~ 1:30000. The exact numbers are:
Feel free to run the test for yourself: https://esbench.com/bench/592b117799634800a03483bd |
const next = () => { return; }
const authorizationCheckerSuccess = () => { return true };
const isPromiseLike = (value) => value.then !== undefined;
const ITERATION_LIMIT = 1000000;
(async () => {
console.time("isPromiseLike")
for (let i = 0; i < ITERATION_LIMIT; i++) {
const checkResult = authorizationCheckerSuccess();
if (isPromiseLike(checkResult)) {
// this is not important
next();
} else {
if (!checkResult) {
throw new Error();
} else {
next();
}
}
}
console.timeEnd("isPromiseLike");
})();
(async () => {
console.time("promise")
for (let i = 0; i < ITERATION_LIMIT; i++) {
const checkResult = authorizationCheckerSuccess();
const result = await Promise.resolve(checkResult);
if (!result) {
throw new Error();
} else {
next();
}
}
console.timeEnd("promise");
})(); Just comment one block to run one loop at once.
|
@19majkel94 everything is relative, so decision in "better if I do code better but avoid small performance optimization" depend on:
having to change single line of code to callback is not a problem if we can performance optimizations in a places where its important for library users. And when we are talking about public routes that can be executed from hundred to million of times its important to make it performant as we can.
yeah I hate callbacks as well and wont use them when I have promises. But question is about |
Wow, thats surprisingly differ, I wonder if it's because benchmark.js messes up something or because our machines differ.
Making every part of the lib where it checks for promises 10x faster is not a "just" especially since this is a library. But this is just my two coin.
Yes in user land usually it doesn't make a difference, in libraries it does. Users of this project want to build (hopefully) performant applications, and we should give them the tools for that. They wont care at all, if we use
Uhm, I dont see why it's error prone, and
Yes we could go back to write Assembly as well, it should be performant. Putting the joke off, you compare promise vs callback, with the 2 line or 1 line problem. They are just simply not on the same level. |
Exactly. So, I encourage you to do the real-case profiling to see all the bottlenecks and hot parts of the code. Don't make premature optimization everywhere 😉
Nope, promises are executed in the same event loop cycle as the microtasks (in node.js/V8):
10x faster, but the overall overhead on whole request-respons handling? 385.427ms vs 385.372ms? Don't bother oneself with this little things.
My coin is violation of DRY - 2x the same logic for async and sync result: try {
const checkResult = this.authorizationChecker(action, actionMetadata.authorizedRoles);
if (isPromiseLike(checkResult)) {
checkResult.then(result => {
if (!result) {
return this.handleError(new AccessDeniedError(action), actionMetadata, action);
} else {
next();
}
}).catch(error => this.handleError(error, actionMetadata, action));
});
} else {
if (!checkResult) {
this.handleError(new AccessDeniedError(action), actionMetadata, action);
} else {
next();
}
}
} catch {
this.handleError(new AccessDeniedError(action), actionMetadata, action);
} vs. const checkResult = this.authorizationChecker(action, actionMetadata.authorizedRoles);
Promise.resolve(checkResult).then(result => {
if (!result) {
return this.handleError(new AccessDeniedError(action), actionMetadata, action);
} else {
next();
}
}).catch(error => this.handleError(error, actionMetadata, action)); try-catch, then-catch, if-else... really a mess 😉
Only And Promise.resolve is a simple solution when the super-top perf isn't critical. Now you can do the wrapper like that: const promiseHelper = (maybePromise, callback) => {
if (isPromiseLike(maybePromise)) {
return maybePromise.then(callback);
} else {
callback(maybePromise);
}
} So all you need is to pass a handler callback: i = 0;
console.time("promise helper")
const callback = (result) => {
if (!result) {
throw new Error("Bad result");
} else {
next();
}
if (++i === ITERATION_LIMIT) {
console.timeEnd("promise helper");
throw new Error("Loop end!");
}
}
while(true) {
const checkResult = authorizationCheckerSuccess();
promiseHelper(checkResult, callback);
} The results for 10 000 000 iterations are:
So it's faster than the simply solution 😆 You can use lambda syntax (create handler inline as param) but then it's 3000 ms time.
This is the future, core parts written is WebAssembly! 🎉 (ironic, WebAssembly in Node.js 😆) |
While a part which runs once on every request is not super hot like a helper which would run a hundred time / request, but it's definitely hot for me. I think you miss a point, that these things adds up. A lot of small things will add up, a lot of little bit slower piece of code will make the overall app slow, while a lot of small optimization will make the overall app fast. But you are right about real-case profiling, and about the thing that this is a small part, maybe there are other parts which needs way more optimization than this one.
Thats a valid point, I agree.
Can you show me how would you use |
I like simple things: try {
const checkResult = await this.authorizationChecker(action, actionMetadata.authorizedRoles);
if (!checkResult) {
this.handleError(new AccessDeniedError(action), actionMetadata, action);
} else {
next();
}
} catch(error) {
this.handleError(error, actionMetadata, action);
} I think I should fork this project and do my vision of refactor and profiling instead of waisting time in the comments discussing about Promise.resolve or 2/4 space indent 😞 |
We want back to the beginning, with
I am sad to hear, that you feel as waste of time, when you have to discuss various topics with other people. I respect your ideas, I think thats the reason why we discuss these things, if you cannot see the value in other people opinion and ideas, maybe forking is the way to go. I am sure @pleerock will merge every PR which adds value back to the project. |
Nope, await works with promise and non promise too 😉 Try it in console/node REPL: (async () => console.log(await 5))()
It's not always a waste of time but recently I spend too much time on discussion like about indenting than the ones about features, PR reviews and issue resolving. I prefer doing and coding than deliberating how much I have many ideas and features proposals but existing codebase is a kinda mess which is hard to develop and I would like to rewrite everything from scratch. My code-style and goals are different than the pleerock's ones so it has to be a fork... but maybe one day we will join like io.js and node.js 😉 |
Wow I have never tried that, great to know!
Well lets spend you time in the threads which are interesting to you, thats a good way to prevent burning out 😛 I can see why talking about code formatting is not too exciting, but it's an importan part to prevent the project to become messy.
I dont see why we cannot do this in this repo as well. Yes it's true that sometimes as we can see, we are not on the same page, but I think these discussions are necessary. Otherway we would just throw in and out features without thinking over what it will cause. Eg interceptors make no sense to you but it turned out it is pretty usefull for others. For the code style, I don't like most of them either, but these are preferences, if we would fork every repo which has a different code style then the one I like, the I would have like a million fork 😄 With forking you split our resources, I am pretty sure we can make this repo a great self driving oss project. Yes it needs refactor, yes it needs better organization, and more documentation why things work the way they do, but it will be great in the end. So I hope we can stay on the same train and make routing controllers great again 😄 (hehe) |
Going back to described problem - is it related to #240? @NoNameProvided |
Right, so we can close this issue as the PR with fixes has been merged. |
We still need to fix it for Koa, but we can track that in the PR |
This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
If the authorizationChecker is a function that returns a rejected Promise, the client will not be able to respond.
my code:
The text was updated successfully, but these errors were encountered: