Reading Time: 6 Minutes ReadThis is the second post of my series about working around Google Drive APIs. If you haven’t read my first article on Google Drive APIs, I urge you to do it. It gives some context that might be required for understanding everything I’m referring to. Not to mention that it might be useful for you on its own anyway. I touched some areas I think the Drive API lacks — like methods used by the UI not available publicly. I mentioned constraints present due to the original underlying architecture: how folders were acting like labels. Furthermore, I highlighted how some small issues in the Drive’s backend can have a huge influence on products using it — like noop actions triggering change notifications. So… let’s start with the toughest nut to crack. I say this because by far this was the biggest black box and has cost us the most time to address in accordance with our quality standards. In many cases, we had to strive to achieve as high throughput as possible which resulted in multiple iterations to tweak/perfect the system. Despite how minor a new information available might have seemed it could have easily resulted in a refactor with a considerable size.
Rate LimitingOh boy, you are in a world of pain on this one. Do you know what is the official recommendation for mitigating the rate limit exceeded issues? Use exponential backoff. That’s it. You would probably think, they would help you in some way in avoiding spamming the API unnecessarily, right? Nah. That’s for losers. You are a pro. Solve it yourself. (Check how GitHub API handles rate limits for example, and you will understand what I’m talking about). There is no official explanation for how they calculate it, how it works, nothing. All you can do is do benchmarks and based on the findings do educate guesses. If you are lucky you might get hints from support. Apart from that… Yes, exponential backoff works… and it’s enough… until a point. When you reach that and you see that so much of your resources — and essentially money — get burnt on requests that get rejected due to a rate limit you start to think how to optimize it.
Rate Limiting – ImplementationThe first step — to go under the rate limit by a big margin so you won’t have issues — could be rather easy. The next one on the other hand — when you try to do as many requests as possible because you want lower latency and/or you just have a lot of tasks to do and you need the throughput — is where it gets rather tricky. You essentially have to implement your own solution that changes the throughput based on the rate limits you encounter. Don’t get me wrong. Using exponential backoff, and reducing the throughput on our side if we reach the rate limit is a “must-have” functionality. My problem is that’s it’s the ONLY input the whole system can use. Then if this wouldn’t be enough the rate changes based on the nature of your calls. This is where the educated guesses come into play:
- Read-only calls have higher throughput than write calls.
- Burst throughput is higher than sustained.
- Read throughput seems to be unaffected by the cardinality of files they are aimed at, meanwhile write has lower throughput if concurrent calls try to modify the same file.
- Read throughput seems to be the same regardless of the concurrency of calls, meanwhile write suffers if the concurrency is too high.