I'm implementing a service with django-channel, I have done some explanations about my issues but you can just scroll down to bottom part where I ask my questions and ignore them.
In this service I'm using a async caching system to raise service's performance. Writing to this cache raises race condition issues. Here's two main functions of this cache
async def get_room_data_dict(self):
data = await self.cache_client.get(self.key)
return data
async def set_room_data_dict(self, room_data_dict):
is_success = await self.cache_client.set(self.key, room_data_dict)
return is_success
Now here's the problem with this method.
### coroutine 1 ###
room_data_dict = await cache_manager.get_room_data_dict()
# In the line below a context switch occurs and coroutine 2 continues to do some tasks
new_room_data_dict = await do_somthing(room_data_dict)
# coroutine 1 continue but room_data_dict is the old one and coroutine 1 going to save it so what coroutine 2 did is actually not applied
await cache_manager.set_room_data_dict(new_room_data_dict)
### coroutine 2 ###
# this coroutine continues without a context switch
room_data_dict = await cache_manager.get_room_data_dict()
new_room_data_dict = await do_somthing(room_data_dict)
await cache_manager.set_room_data_dict(new_room_data_dict)
# gets back to coroutine 1 and continues its code
Now if you've looked closely and have some OS education you will see that the change coroutine 2 does on room_data_dict is actually not applied.
Here's the thing I would've done to prevent this problem, I would change the functions like below
async def get_room_data_dict(self):
await self.room_data_dict_semaphore.acquire()
data = await self.cache_client.get(self.key)
return data
async def set_room_data_dict(self, room_data_dict):
is_success = await self.cache_client.set(self.key, room_data_dict)
self.room_data_dict_semaphore.release()
return is_success
This method would solve my problem if and only if the semaphore in the code is shared in group channel.
How about acquire/release the lock in the outer scope (coroutine 1).
Anyway various locking systems allow you to identify the lock using a string key, so that it can be acquired/released from different scopes. You can even use a distributed lock like python-redis-lock.