Coverage for api/utils/dead_link_mask.py: 84%

31 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-07 06:14 +0000

1import django_redis 

2import structlog 

3from deepdiff import DeepHash 

4from elasticsearch_dsl import Search 

5from redis.exceptions import ConnectionError 

6 

7 

8logger = structlog.get_logger(__name__) 

9 

10 

11# 3 hours minutes (in seconds) 

12DEAD_LINK_MASK_TTL = 60 * 60 * 3 

13 

14 

15def get_query_hash(s: Search) -> str: 

16 """ 

17 Hash the search query using a deterministic algorithm. 

18 

19 Generates a deterministic Murmur3 or SHA256 hash from the serialized Search 

20 object using DeepHash so that two Search objects with the same content will 

21 produce the same hash. 

22 

23 :param s: Search object to be serialized and hashed. 

24 :return: Serialized Search object hash. 

25 """ 

26 serialized_search_obj = s.to_dict() 

27 serialized_search_obj.pop("from", None) 

28 serialized_search_obj.pop("size", None) 

29 deep_hash = DeepHash(serialized_search_obj)[serialized_search_obj] 

30 return deep_hash 

31 

32 

33def get_query_mask(query_hash: str) -> list[int]: 

34 """ 

35 Fetch an existing query mask for a given query hash or returns an empty one. 

36 

37 :param query_hash: Unique value for a particular query. 

38 :return: Boolean mask as a list of integers (0 or 1). 

39 """ 

40 redis = django_redis.get_redis_connection("default") 

41 key = f"{query_hash}:dead_link_mask" 

42 try: 

43 return list(map(int, redis.lrange(key, 0, -1))) 

44 except ConnectionError: 

45 logger.warning("Redis connect failed, cannot get cached query mask.") 

46 return [] 

47 

48 

49def save_query_mask(query_hash: str, mask: list): 

50 """ 

51 Save a query mask to redis. 

52 

53 :param mask: Boolean mask as a list of integers (0 or 1). 

54 :param query_hash: Unique value to be used as key. 

55 """ 

56 redis_pipe = django_redis.get_redis_connection("default").pipeline() 

57 key = f"{query_hash}:dead_link_mask" 

58 

59 redis_pipe.delete(key) 

60 redis_pipe.rpush(key, *mask) 

61 redis_pipe.expire(key, DEAD_LINK_MASK_TTL) 

62 

63 try: 

64 redis_pipe.execute() 

65 except ConnectionError: 

66 logger.warning("Redis connect failed, cannot cache query mask.")