-
Notifications
You must be signed in to change notification settings - Fork 54
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
Add middleware/handler to throttle requests to conform to API rate limit #109
Comments
@holtkamp thanks for this suggestion. Ill tackle this in 0.13.0. |
@OGKevin ok, nice, in case you need inspiration, implemented something which currently "does the job"... |
@holtkamp What do you mean with Then calculate if this request may trigger a 429 or not. And based on that sleep or not. But of course im open to other suggestions. |
Indeed, that approach is what I implemented. Will check coming week if I can share some details |
Currently using this approach: https://gist.github.com/holtkamp/bae00c495f80eace6aa49d12b46f5bca, maybe not very elegant, but does the job |
Apparently what we are looking for is a "LeakyBucket" implementation. Look at this PSR-7 implementation of it: |
@OGKevin any update / progress on this aspect? |
No not really 😭. Im packed with internal projects and therefore didn't get time to work on github projects. PR's are highly appreciated tho. Next week ill be on holiday so i might do some hobby coding and work on github then. Some one made a PR for c# bunq/sdk_csharp#106 (review) which looks good. |
Happy anniversary for this issue! 🎂 While Bunq is pushing a lot of "crazy awesome nice amazing" new features in well marketed "product updates", it seems some basic functionality like request throttling of the official client of a throttled API is not a priority. @OGKevin are sufficient resources assigned to API (client) development? |
thats exactly the case :P and has always been. Sorry i could not have had a proper look at this. I no longer work at bunq, hence i've been removed as maintainer. @kojoru took over, but it looks like he is running in the same issue as i was 😉. Im happy to dive into this and do open source contribution tho. ill try to have a look this weekend before i go on holiday. If not afterwards. I haven't used this SDK anymore, i've switched to golang. |
@OGKevin thanks for the response @kojoru maybe you can convince decision makers to assign some of the "marketing budget" (for example, this: https://twitter.com/bunq/status/1090923033223745537) to sponsoring OSS contributions. There are plenty skilled developers who can help improve the API client once they get some support to be able to spend time on this. |
So i took a look at this, and it seems that the current way the request and response handlers are created: https://github.com/bunq/sdk_php/tree/e9511e1c158a2c8d768d167bc05a66d7d88ea5d5/src/Http/Handler The response handlers do not have the request 🤔 So, the first task would be is to rework the response handlers to so that they get the request as well. Once this is done, then we can create a new response handler that checks for 429, and if that is the case then returns a recursive call to the client again to execute that request. I'll work on a pr to give the response handlers the request. |
This ensures that one does not need to install php and all the extension on his or her system. bunq#109
I've created a PR. the only thing that we need to figure out now is how we're going to test an actual request. I hate the fact that the SDK is making real requests which makes the test suite super slow :( never got the chance to implement a nice request mocker. Nevertheless, how can we easily trigger a 429 that goes through the api client 🤔 sdk_php/tests/Http/ResponseHandlerRateLimitTest.php Lines 60 to 66 in a1c1ba1
|
just to clarify my retry approach, as this SDK is build based on static methods and singletons. It makes no sense to keep a global mapping to throttle the requests instead of retrying on failures. If we had a long living api client, then this client could have kept a map of uri's and throttle the requests accordingly. |
Well, in my case the client gets initialized on a daily basis to perform a batch of operations. So this client is "long living". I think the "retry approach" to deal with |
Maybe we can have a look at https://github.com/picqer/moneybird-php-client/tree/master/tests for inspiration. I contributed to that library a few times and if I remember correct the testing of the connection with a mocked connection was quite decent... Also see: |
Thats not what i meant, what i meant was that for each Api call, a new client is being made. E.g.
and
This makes the client super short lived and therefore it can not nicely keep track of all the previous requests it made and therefore nicely throttle if it suspects a 429 to happen.
I see that they indeed use mocks and not make actual requests which is nice. But, in our case, mocking is kind of hard. As bunq's approach is to make everything static. And the fact that you have this sdk_php/src/Model/Generated/Endpoint/MonetaryAccountBank.php Lines 443 to 457 in e9511e1
Instead of the class having a client as field, makes it even harder to have a test double client that fakes the requests. What i normally try to do is use interfaces and make 2 implementations. One for test and one for prod, as you could see here sdk_php/src/Http/RequestRetryer.php Lines 12 to 18 in 4c13a41
However, IMO there should not be an interface for this, however there should be an interface that defines the sdk_php/src/Http/ApiClient.php Lines 212 to 218 in e9511e1
method. This way, we can have 1 "requester" for production that makes actual requests, and 1 "requester" for test that fakes requests. This makes it so that you don't even need to use a mock, just an implementation for testing. |
I guess we can drop all hopes of getting my PR looked at and merged 🤷♂ Im really sorry @holtkamp that I could not have done this while I still was a maintainer😔. |
Additional inspiration for the Bunq team: https://github.com/spatie/guzzle-rate-limiter-middleware |
Seems like another anniversary 🎂 for this issue. |
The API employs a rate limit, which is understandable.
next to the (outdated?) documentation the following seems to apply:
device-server/*
HTTP GET
: 9 requests per 9 consecutive secondsuser/*
HTTP POST
??: 5 requests per 5 consecutive secondsA way to deal with this is:
429
exceptionThis is a rather "reactive" approach and unintelligent (the minimum number of seconds to sleep seems not available in the exception?). It would be nice to have a middleware in the API client that keeps track of the number of requests submitted to each endpoint and applies throttling when required.
Since the client performs some requests when connecting. Such a throttling mechanism can not solely be implemented in userland code. A throttling approach also prevents sleeping during a test 😨 .
The text was updated successfully, but these errors were encountered: