I found a gap in how I enforce a per-user daily limit on an expensive API call.

by•

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?

6 views

Add a comment

Replies

Be the first to reply

Have a question or a thought to share? Add a comment above to start the conversation.