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

feat: validation check for double-counting hits for a single request #364

Merged
merged 3 commits into from
Aug 3, 2023

Conversation

nfriedly
Copy link
Member

@nfriedly nfriedly commented Aug 2, 2023

This implements the idea discussed in #356 (comment) to check for cases of double-counting a single request.

There's probably some situations that this doesn't account for, but I tried to cover the main ones.

We'll want to update the PreciseMemoryStore and set it to localKeys = true after merging this. (Although, I'm not sure that localKeys is the name I want to settle on.)

Related Issues

What Does This PR Do?

Added

  • New validation check looking for double-couting of requests

Caveats/Problems/Issues

🤷‍♂️

Checklist

  • The issues that this PR fixes/closes have been mentioned above.
  • What this PR adds/changes/removes has been explained.
  • All tests (npm test) pass.
  • All added/modified code has been commented, and
    methods/classes/constants/types have been annotated with TSDoc comments.
  • If a new feature has been added or a bug has been fixed, tests have been
    added for the same.

This impliments the idea discussed in #356 (comment) to check for cases of double-counting a single request.

There's probably some situations that this doesn't account for, but I tried to cover the main ones.

We'll want to update the PreciseMemoryStore and set it to `localKeys = true` after merging this. (Although, I'm not sure that `localKeys` is the name I want to settle on.)
@nfriedly
Copy link
Member Author

nfriedly commented Aug 2, 2023

I just added https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_double_count to describe this.

source/validations.ts Outdated Show resolved Hide resolved
Validations.singleCountKeys.set(request, stores)
}

const storeKey = store.localKeys ? store : store.constructor.name
Copy link
Member Author

@nfriedly nfriedly Aug 2, 2023

Choose a reason for hiding this comment

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

I think this will be "PromisifiedStore" for any legacy store, which I suppose means there's a small chance of a false positive if two different legacy stores were used... but that seems very unlikely in practice.

It could also end up being "Object" or "Function" in some circumstances, but again, there would have to be two different stores that evaluated to the same name, both in use on a single request, to hit a false positive. So, also very unlikely.

I was trying to think about ways that store.constructor.name could throw, but I don't think it could happen. I don't think we can get this far is store is undefined or null, and I believe that any other value is guaranteed to have a .constructor.name.

Copy link
Member

Choose a reason for hiding this comment

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

I thought the same, maybe we should change the PromisifiedStore to make it set the constructor.name property to Promisified${originalStoreName}?

Copy link
Member

Choose a reason for hiding this comment

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

This might be a case where constructor.name fails. We should follow the fix mentioned in the first comment below the answer, and advise other stores to do so as well.

Copy link
Member Author

Choose a reason for hiding this comment

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

I was going to say that I think setting constructor.name on one instance would affect all other instances, but it looks like it's readonly:

> class Foo {}
undefined
> a = new Foo
Foo {}
> b = new Foo
Foo {}
> a.constructor.name
'Foo'
> b.constructor.name = 'bar'
'bar'
> a.constructor == b.constructor
true
> a.constructor.name == b.constructor.name
true
> b.constructor.name
'Foo'

As for the minification case, I think it would still work for us. They'd have to be minifiying server-side JS, and using two different stores, and the minifier would have to choose the same name for both stores, and the request would have to hit both stores for it to report a false positive.

I think I'm just going to update the docs to explain that it could be a false-positive in rare circumstances, and call this check good enough.

Copy link
Member

Choose a reason for hiding this comment

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

Sounds good!

Copy link
Member

@gamemaker1 gamemaker1 left a comment

Choose a reason for hiding this comment

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

Apart from the constructor.name part, this is great work! Looking forward to merging this soon :)

@gamemaker1
Copy link
Member

Also, I'm thinking about switching from npm to pnpm for the project - it should help in faster installs in the external tests. It takes 20 minutes to run the tests right now, I'm hoping pnpm will reduce it to at least half?

@nfriedly
Copy link
Member Author

nfriedly commented Aug 3, 2023

Also, I'm thinking about switching from npm to pnpm for the project - it should help in faster installs in the external tests. It takes 20 minutes to run the tests right now, I'm hoping pnpm will reduce it to at least half?

I've never used pnpn, so I don't really have an opinion on it. I'd say try it out and see if it's faster.

One thing that crossed my mind when creating this PR is that even I don't run all of the integration tests locally, because I don't have databases set up for all of the stores. I think that's OK - CI will test that - but perhaps we should make it more clear in https://github.com/express-rate-limit/express-rate-limit/blob/main/.github/pull_request_template.md? E.g. instead of saying "All tests (npm test) pass." we say "All library tests (npm test) pass.", and then we change npm test to just lint and run the library tests, and we change CI to do that + run the external tests. What do you think about that?

Then if we make that distinction, maybe we only use pnpm for the external tests so that contributors don't have to figure it out.

@gamemaker1
Copy link
Member

Even I have stopped running the external tests, mostly because of the time it takes, and because, as you said, CI does take care of it anyways.

but perhaps we should make it more clear in https://github.com/express-rate-limit/express-rate-limit/blob/main/.github/pull_request_template.md? E.g. instead of saying "All tests (npm test) pass." we say "All library tests (npm test) pass.", and then we change npm test to just lint and run the library tests, and we change CI to do that + run the external tests.

Sounds great, I will do this now.

@gamemaker1 gamemaker1 changed the title Validation check for double-counting hits for a single request feat: validation check for double-counting hits for a single request Aug 3, 2023
@gamemaker1
Copy link
Member

Sounds great, I will do this now.

Done with the documentation part, on the main branch. I'll work on the pnpm part in a separate pr.

This pr looks good to go!

@nfriedly
Copy link
Member Author

nfriedly commented Aug 3, 2023

Awesome, I tweaked https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_double_count so I'm going to go ahead and merge this.

I might try and do #356 (comment) and #356 (comment) before we publish the next release since they're all somewhat related.

@nfriedly nfriedly merged commit 81affa1 into main Aug 3, 2023
44 checks passed
@gamemaker1 gamemaker1 added the validations Related to the validation mechanism. label Aug 4, 2023
@gamemaker1 gamemaker1 deleted the validate-single-count branch August 10, 2023 17:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
validations Related to the validation mechanism.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants