Skip to content

Adjust GHRateLimit to system time instead of depending on synchronization#595

Merged
bitwiseman merged 8 commits into
hub4j:masterfrom
bitwiseman:issue/rate-limit
Nov 12, 2019
Merged

Adjust GHRateLimit to system time instead of depending on synchronization#595
bitwiseman merged 8 commits into
hub4j:masterfrom
bitwiseman:issue/rate-limit

Conversation

@bitwiseman

@bitwiseman bitwiseman commented Nov 7, 2019

Copy link
Copy Markdown
Member

Fixes #383
Related to #591

@martinvanzijl @PauloMigAlmeida
if you have a little time to look at this over, any feedback would be appreciated.

Summary

If the client time is out of sync with global time, GHRateLimit could report a resetDate that was completely wrong - accurate to global time, but disconnected from local system time. This wouldn't be so bad, but some clients use the resetDate to budget API calls. If they believe they have 10 hours until reset and only 4000 calls available, they effective lock up.

Further, public fields and having a "Date" field that is holding unconverted EpochSeconds just bothers me.

I deprecated the old public fields and added private fields with getters. I then added awareness of the Date header returned with the Http response. Using that we can generate calculatedSecondsUntilReset and the create the resetDate by adding that to System-time that his instance was created.

As a final step, I made it so rateLimit() will update automatically when then GHRateLimit instance expires.

Improves the way reset date is calculated - uses server date if possible.

Fixes hub4j#383
@PauloMigAlmeida

Copy link
Copy Markdown
Contributor

Cool! I will take a look at it during lunchtime :)

Sometimes adding tests finds broken product code.  Sometimes it finds broken test environment.
This was the latter.

The wiremock templating returns dates in UTC format not RFC-1123 format.
Introduces hub4j#597

This does not appear to be a bug in github-api but in OkHttp 2.7.5
(and/or the Cache implementation in that version).
@PauloMigAlmeida

Copy link
Copy Markdown
Contributor

Hi @bitwiseman,

If the client time is out of sync with global time, GHRateLimit could report a resetDate that was completely wrong

Good catch! That is a nice way of fixing the problem. 👍

[Re: refactoring/improvements you made]
The only thing I would possibly change is the way that the content from /rate_limit response is parsed.

Currently, we're using the JsonRateLimit containing only the rate attribute which has been deprecated. Ideally, we should add the newer JSON attributes listed at https://developer.github.com/v3/rate_limit/#response and refactor all references to the rate attribute to resources.core.

In case you want to go down that road, the Requester.noteRateLimit() method will have to be reworked a bit. https://github.com/github-api/github-api/blob/f28edbcf8fcb57a291aaa537bc57ea79c9bcda64/src/main/java/org/kohsuke/github/Requester.java#L354-L362

same here https://github.com/github-api/github-api/blob/f28edbcf8fcb57a291aaa537bc57ea79c9bcda64/src/main/java/org/kohsuke/github/GitHub.java#L329-L338

|| headerRateLimit.getResetDate().getTime() < observed.getResetDate().getTime()
|| headerRateLimit.remaining > observed.remaining) {
|| headerRateLimit.getRemaining() > observed.getRemaining()
|| headerRateLimit.getResetEpochSeconds() < observed.getResetEpochSeconds()) {

@PauloMigAlmeida PauloMigAlmeida Nov 9, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I must be missing something really simple but... I took a look at the git history and the issues used to discuss the original implementation (back in 2017) and I still don't get why we wouldn't update the headerRateLimit variable every time updateRateLimit method is invoked.

Would you mind sharing with me your understanding of that if statement and/or any idea of when this would be applicable?

@bitwiseman bitwiseman Nov 9, 2019

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Hm, good question. Ah, I remember now. Multi-threading.
If you have 10 threads all make queries and then all try to update the rate limit. We want to pick the one with the lowest remaining count, of if a reset occurs we want to pick the one with the later reset date.

I need to write a test that covers this and add comments to the code explaining.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ahhhhh, that makes sense :)

@bitwiseman

Copy link
Copy Markdown
Member Author

@bitwiseman

bitwiseman commented Nov 10, 2019

Copy link
Copy Markdown
Member Author

@PauloMigAlmeida
So, I'm making some changes but it looks like making a new design that really includes the other rate limit info aside from core will be another bit of work. I think the change I've made is sufficient for this PR. And we can add an issue to do some further deprecation and redesign to expose the other limit information.

@PauloMigAlmeida

Copy link
Copy Markdown
Contributor

I agree with you, all of that seems a bit too much for a single PR.

If you don't mind, after you finish this refactor I would love to try implementing the newer rate limits myself. That should be fun 😄

On the other hand, if you want to implement it, please count on me for reviewing it too

} else {
// records of the same type compare to each other as normal.
return current.getResetEpochSeconds() < candidate.getResetEpochSeconds()
|| (current.getResetEpochSeconds() == candidate.getResetEpochSeconds() && current.getRemaining() > candidate.getRemaining());

@bitwiseman bitwiseman Nov 11, 2019

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@PauloMigAlmeida
This is the check that your question got me to fix.
A recorder with a later reset is for sure newer that one with an earlier one.
Only if they have the same reset should we take the one with the lower remaining count.
Previous implementation would take the lower remaining count candidate even if it had an earlier (older) reset date.
Thanks!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It makes sense! I like the train of thought you used for solving it.

@bitwiseman

Copy link
Copy Markdown
Member Author

@PauloMigAlmeida
I've created a task #605 for exposing the new limit records and integrating the header update behavior.
Assigning to you. Enjoy!

Created GHRateLimit.Record
Add the four rate limit records to GHRateLimit
Moved getLimt(), getRemaining(), and so on to point to core record for ease of use.
Fixed update check for header to not replace existing with older when remaining count is lower.

NOTE: Did not expose records other than core and did not resolve header update behavior to respect non-core records.
@bitwiseman

bitwiseman commented Nov 12, 2019

Copy link
Copy Markdown
Member Author

@PauloMigAlmeida How does this look to you now?

@PauloMigAlmeida PauloMigAlmeida left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks great! Can't wait to see it on the next version of the library 😃

} else {
// records of the same type compare to each other as normal.
return current.getResetEpochSeconds() < candidate.getResetEpochSeconds()
|| (current.getResetEpochSeconds() == candidate.getResetEpochSeconds() && current.getRemaining() > candidate.getRemaining());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It makes sense! I like the train of thought you used for solving it.

@bitwiseman bitwiseman merged commit 7036423 into hub4j:master Nov 12, 2019
@bitwiseman bitwiseman deleted the issue/rate-limit branch November 12, 2019 21:02
@jglick jglick mentioned this pull request Mar 30, 2020
4 tasks
@bitwiseman bitwiseman mentioned this pull request Mar 30, 2020
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.

Parse the response Date header and expose in the Rate Limit information

2 participants