I found a gap in how I enforce a per-user daily limit on an expensive API call.
The check is: count today’s rows for this user, and if the count is under the limit, run the call and insert a row. That works for one request at a time. If a client sends 10 requests at once, all 10 can read “count = 2” before any of them insert, so all 10 pass.
On serverless, this gets worse, because an in-memory counter doesn’t help. Each request may run in a different instance with its own memory.
The fixes I’m weighing, all in the database:
A unique constraint on (user, day, slot number). Each request claims the next slot, and a duplicate insert fails. It’s simple, but I need to handle the retry.
An atomic counter. One SQL statement increments a per-user-per-day counter and returns the new value, and the request is rejected if it’s over the limit. There’s no gap between checking and counting.
An external store like Redis. It’s fast, but it’s one more service for a small stack.
I’m leaning toward the atomic counter since the stack is Postgres only. What do others use for limits on serverless? Is there a gotcha with the counter approach, like needing to refund the slot when the downstream call fails?
Replies
Be the first to reply
Have a question or a thought to share? Add a comment above to start the conversation.