Background Info
My web app is built on top of async FastAPI. The server deploying this app will start 4 workers and each worker will start 2 threads for handling concurrent requests.
Description
Most services in my web app rely heavily on a token required from an external API. The problem is that this token has a certain expiration duration, meaning I’ll have to fetch it again when the existing one expires.
To prevent the token from being refreshed multiple times, I did the following things:
- Cache the token in the MySQL database so that I can access the token within the expiration duration without fetching the API.
- When the existing token expires, I apply an
asyncio.Lock
on all coroutines so that it’s ensured only one coroutine can exclusively do the fetching operation.
Problems
The above approach did solve the problem but buried a hidden issue. Since I stored the token in MySQL database, I’ll have to keep the connections open during the entire refresh operation (This is a bad approach as stated in SQLAlchemy docs). After referencing Python docs, I came to realize it’s not an appropriate solution to apply asyncio.Lock
in a web app with multiple threads.
As a result, I’m considering caching the token in redis. But then, how should I properly lock the refreshing operation so that only one token exists in redis even under high concurrency environment?