1212import pytest
1313from faker import Faker
1414from servicelib .redis import CouldNotAcquireLockError , RedisClientSDK , exclusive
15- from servicelib .redis ._decorators import _EXCLUSIVE_AUTO_EXTEND_TASK_NAME
15+ from servicelib .redis ._decorators import (
16+ _EXCLUSIVE_AUTO_EXTEND_TASK_NAME ,
17+ _EXCLUSIVE_TASK_NAME ,
18+ )
1619from servicelib .redis ._errors import LockLostError
1720from servicelib .utils import limited_gather , logged_gather
1821
2427]
2528
2629
30+ def _assert_exclusive_tasks_are_cancelled (lock_name : str , func : Callable ) -> None :
31+ assert _EXCLUSIVE_AUTO_EXTEND_TASK_NAME .format (redis_lock_key = lock_name ) not in [
32+ t .get_name () for t in asyncio .tasks .all_tasks ()
33+ ], "the auto extend lock task was not properly stopped!"
34+ assert _EXCLUSIVE_TASK_NAME .format (func_name = func .__name__ ) not in [
35+ t .get_name () for t in asyncio .tasks .all_tasks ()
36+ ], "the exclusive task was not properly stopped!"
37+
38+
2739async def _is_locked (redis_client_sdk : RedisClientSDK , lock_name : str ) -> bool :
2840 lock = redis_client_sdk .redis .lock (lock_name )
2941 return await lock .locked ()
@@ -162,19 +174,21 @@ async def test_exclusive_raises_if_lock_is_lost(
162174 started_event = asyncio .Event ()
163175
164176 @exclusive (redis_client_sdk , lock_key = lock_name )
165- async def _ (time_to_sleep : datetime .timedelta ) -> datetime .timedelta :
177+ async def _sleeper (time_to_sleep : datetime .timedelta ) -> datetime .timedelta :
166178 started_event .set ()
167179 await asyncio .sleep (time_to_sleep .total_seconds ())
168180 return time_to_sleep
169181
170- exclusive_task = asyncio .create_task (_ (datetime .timedelta (seconds = 10 )))
182+ exclusive_task = asyncio .create_task (_sleeper (datetime .timedelta (seconds = 10 )))
171183 await asyncio .wait_for (started_event .wait (), timeout = 2 )
172184 # let's simlulate lost lock by forcefully deleting it
173185 await redis_client_sdk .redis .delete (lock_name )
174186
175187 with pytest .raises (LockLostError ):
176188 await exclusive_task
177189
190+ _assert_exclusive_tasks_are_cancelled (lock_name , _sleeper )
191+
178192
179193@pytest .fixture
180194def lock_data (faker : Faker ) -> str :
@@ -187,7 +201,7 @@ async def test_exclusive_with_lock_value(
187201 started_event = asyncio .Event ()
188202
189203 @exclusive (redis_client_sdk , lock_key = lock_name , lock_value = lock_data )
190- async def _ (time_to_sleep : datetime .timedelta ) -> datetime .timedelta :
204+ async def _sleeper (time_to_sleep : datetime .timedelta ) -> datetime .timedelta :
191205 started_event .set ()
192206 await asyncio .sleep (time_to_sleep .total_seconds ())
193207 return time_to_sleep
@@ -197,7 +211,7 @@ async def _(time_to_sleep: datetime.timedelta) -> datetime.timedelta:
197211 assert await redis_client_sdk .lock_value (lock_name ) is None
198212
199213 # run the exclusive task
200- exclusive_task = asyncio .create_task (_ (datetime .timedelta (seconds = 3 )))
214+ exclusive_task = asyncio .create_task (_sleeper (datetime .timedelta (seconds = 3 )))
201215 await asyncio .wait_for (started_event .wait (), timeout = 2 )
202216 # expected
203217 assert await _is_locked (redis_client_sdk , lock_name ) is True
@@ -208,12 +222,14 @@ async def _(time_to_sleep: datetime.timedelta) -> datetime.timedelta:
208222 assert await _is_locked (redis_client_sdk , lock_name ) is False
209223 assert await redis_client_sdk .lock_value (lock_name ) is None
210224
225+ _assert_exclusive_tasks_are_cancelled (lock_name , _sleeper )
226+
211227
212228async def test_exclusive_task_erroring_releases_lock (
213229 redis_client_sdk : RedisClientSDK , lock_name : str
214230):
215231 @exclusive (redis_client_sdk , lock_key = lock_name )
216- async def _ () -> None :
232+ async def _raising_func () -> None :
217233 msg = "Expected error"
218234 raise RuntimeError (msg )
219235
@@ -222,10 +238,12 @@ async def _() -> None:
222238 assert await redis_client_sdk .lock_value (lock_name ) is None
223239
224240 with pytest .raises (RuntimeError ):
225- await _ ()
241+ await _raising_func ()
226242
227243 assert await redis_client_sdk .lock_value (lock_name ) is None
228244
245+ _assert_exclusive_tasks_are_cancelled (lock_name , _raising_func )
246+
229247
230248async def test_lock_acquired_in_parallel_to_update_same_resource (
231249 with_short_default_redis_lock_ttl : datetime .timedelta ,
@@ -266,19 +284,21 @@ async def _inc_counter() -> None:
266284 )
267285 assert counter .value == INCREASE_BY * INCREASE_OPERATIONS
268286
287+ _assert_exclusive_tasks_are_cancelled (lock_name , _inc_counter )
288+
269289
270- async def test_cancelling_exclusive_task_works (
290+ async def test_cancelling_exclusive_task_cancels_properly (
271291 redis_client_sdk : RedisClientSDK , lock_name : str
272292):
273293 started_event = asyncio .Event ()
274294
275295 @exclusive (redis_client_sdk , lock_key = lock_name )
276- async def _ (time_to_sleep : datetime .timedelta ) -> datetime .timedelta :
296+ async def _sleep_task (time_to_sleep : datetime .timedelta ) -> datetime .timedelta :
277297 started_event .set ()
278298 await asyncio .sleep (time_to_sleep .total_seconds ())
279299 return time_to_sleep
280300
281- exclusive_task = asyncio .create_task (_ (datetime .timedelta (seconds = 10 )))
301+ exclusive_task = asyncio .create_task (_sleep_task (datetime .timedelta (seconds = 10 )))
282302 await asyncio .wait_for (started_event .wait (), timeout = 2 )
283303 exclusive_task .cancel ()
284304
@@ -287,6 +307,4 @@ async def _(time_to_sleep: datetime.timedelta) -> datetime.timedelta:
287307
288308 assert not await _is_locked (redis_client_sdk , lock_name )
289309
290- assert _EXCLUSIVE_AUTO_EXTEND_TASK_NAME .format (redis_lock_key = lock_name ) not in [
291- t .get_name () for t in asyncio .tasks .all_tasks ()
292- ], "The auto extend lock task was not properly stopped!"
310+ _assert_exclusive_tasks_are_cancelled (lock_name , _sleep_task )
0 commit comments