Coverage for qreward / utils / scheduler / pools.py: 100.00%

48 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-23 19:32 +0000

1"""Running task pool for concurrency monitoring.""" 

2 

3import threading 

4import time 

5import warnings 

6from collections import OrderedDict 

7 

8# Default number of time windows to track historical peak concurrency 

9DEFAULT_WINDOW_MAX_SIZE = 12 

10 

11# Default interval (seconds) for each time window 

12DEFAULT_WINDOW_INTERVAL = 60 

13 

14# Default concurrency threshold below which tasks are always allowed 

15DEFAULT_THRESHOLD = 3 

16 

17 

18class RunningTaskPool: 

19 """Manager for monitoring and controlling task concurrency.""" 

20 

21 # Global lock for protecting global pool 

22 global_lock = threading.Lock() 

23 

24 # Global pool dictionary, storing different pool instances by key 

25 global_task_pool: dict = {} 

26 

27 @classmethod 

28 def get_pool( 

29 cls, 

30 key: str, 

31 window_max_size: int = DEFAULT_WINDOW_MAX_SIZE, 

32 window_interval: int = DEFAULT_WINDOW_INTERVAL, 

33 threshold: int = DEFAULT_THRESHOLD, 

34 ) -> "RunningTaskPool": 

35 """Get or create a task pool instance for the given key. 

36 

37 Uses singleton pattern to ensure one pool per key. 

38 

39 Args: 

40 key: Unique identifier for the pool 

41 window_max_size: Max number of time windows, default 12 

42 window_interval: Interval of each window in seconds, default 60 

43 threshold: Concurrency threshold, default 3 

44 

45 Returns: 

46 RunningTaskPool instance 

47 """ 

48 with cls.global_lock: 

49 if key not in cls.global_task_pool: 

50 cls.global_task_pool[key] = cls( 

51 window_max_size=window_max_size, 

52 window_interval=window_interval, 

53 threshold=threshold, 

54 ) 

55 return cls.global_task_pool[key] 

56 

57 def __init__( 

58 self, 

59 window_max_size: int = DEFAULT_WINDOW_MAX_SIZE, 

60 window_interval: int = DEFAULT_WINDOW_INTERVAL, 

61 threshold: int = DEFAULT_THRESHOLD, 

62 ): 

63 """Initialize task pool. 

64 

65 Args: 

66 window_max_size: Max number of time windows 

67 window_interval: Interval of each window in seconds 

68 threshold: Concurrency threshold 

69 """ 

70 self._value = 0 

71 self._max_size_map = OrderedDict() 

72 self._window_max_size = window_max_size 

73 self._window_interval = window_interval 

74 self._threshold = threshold 

75 self._lock = threading.Lock() 

76 

77 def add(self, value: int = 1) -> int: 

78 """Update current running task count and record historical peak. 

79 

80 Args: 

81 value: Value to change 

82 

83 Returns: 

84 Current running task count 

85 """ 

86 with self._lock: 

87 self._value += value 

88 key = int(time.time()) // self._window_interval 

89 if key in self._max_size_map: 

90 if self._max_size_map[key] < self._value: 

91 self._max_size_map[key] = self._value 

92 else: 

93 while len(self._max_size_map) >= self._window_max_size: 

94 self._max_size_map.popitem(last=False) 

95 self._max_size_map[key] = self._value 

96 return self._value 

97 

98 def can_submit(self, multiply: float = 1) -> bool: 

99 """Check if current task status allows execution. 

100 

101 Logic: 

102 1. If current task count is below threshold, allow execution 

103 2. If historical max exceeds current value * multiply, system is 

104 overloaded 

105 

106 Args: 

107 multiply: Multiplier for load calculation 

108 

109 Returns: 

110 True if can continue, False if should throttle 

111 """ 

112 with self._lock: 

113 if self._value <= self._threshold: 

114 return True 

115 max_value = 0 

116 for v in self._max_size_map.values(): 

117 if v > max_value: 

118 max_value = v 

119 if max_value > self._value * multiply: 

120 return True 

121 return False 

122 

123 def less_than(self, multiply: float = 1) -> bool: 

124 warnings.warn( 

125 "less_than is deprecated, use can_submit instead", 

126 DeprecationWarning, 

127 stacklevel=2, 

128 ) 

129 return self.can_submit(multiply)