I have a piece of code, that checks if the redis db has data updates and loads them to the memory. I want only one corouitine to execute this load.
class MyService:
def __init__(self):
self.lock = asyncio.Lock()
self.latest_timestamp = None
async def _check_latest_timestamp_from_db(self):
# go to db
async def ensure_update_loaded():
latest_timestamp_from_db = await self._check_latest_timestamp_from_db()
if self.timestamp == latest_timestamp_from_db:
return
if self.lock.locked():
return # the load is in progress, we're okay to use the old data for now
async with self.lock:
# do the load
self.timestamp = latest_timestamp_from_db
From my understanding multiple coroutines can go simultaneously to this line: async with lock:
, after all the checks are passed. Yes, they will execute consequently, but the load will happen more than once.
I could double check within the lock:
async with self.lock:
if self.timestamp == latest_timestamp_from_db:
return
# do the load
self.timestamp = latest_timestamp_from_db
But I think there should be a clearer solution to my problem.
You're basically implementing a double-checked locking.
Pseudocode:
class MyService:
def __init__(self):
self.lock = asyncio.Lock()
self.latest_timestamp = None
async def _check_latest_timestamp_from_db(self):
...
async def ensure_update_loaded(self):
latest_timestamp_from_db = await self._check_latest_timestamp_from_db()
if self.latest_timestamp == latest_timestamp_from_db:
return
async with self.lock:
if self.latest_timestamp == latest_timestamp_from_db:
return
...
self.latest_timestamp = latest_timestamp_from_db
Moving the check inside the lock is correct.