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
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-23 19:32 +0000
1"""Running task pool for concurrency monitoring."""
3import threading
4import time
5import warnings
6from collections import OrderedDict
8# Default number of time windows to track historical peak concurrency
9DEFAULT_WINDOW_MAX_SIZE = 12
11# Default interval (seconds) for each time window
12DEFAULT_WINDOW_INTERVAL = 60
14# Default concurrency threshold below which tasks are always allowed
15DEFAULT_THRESHOLD = 3
18class RunningTaskPool:
19 """Manager for monitoring and controlling task concurrency."""
21 # Global lock for protecting global pool
22 global_lock = threading.Lock()
24 # Global pool dictionary, storing different pool instances by key
25 global_task_pool: dict = {}
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.
37 Uses singleton pattern to ensure one pool per key.
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
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]
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.
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()
77 def add(self, value: int = 1) -> int:
78 """Update current running task count and record historical peak.
80 Args:
81 value: Value to change
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
98 def can_submit(self, multiply: float = 1) -> bool:
99 """Check if current task status allows execution.
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
106 Args:
107 multiply: Multiplier for load calculation
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
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)