redis-allocator 0.3.2__py3-none-any.whl → 0.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- __version__ = '0.3.2'
1
+ __version__ = '0.4.0'
@@ -97,7 +97,7 @@ class RedisAllocatableClass(ABC):
97
97
 
98
98
  def open(self):
99
99
  """Open the object."""
100
- pass
100
+ return self
101
101
 
102
102
  def close(self):
103
103
  """close the object."""
@@ -162,6 +162,7 @@ class RedisAllocatorObject(Generic[U]):
162
162
  """Open the object."""
163
163
  if self.obj is not None:
164
164
  self.obj.open()
165
+ return self
165
166
 
166
167
  def close(self):
167
168
  """Kill the object."""
@@ -174,21 +175,42 @@ class RedisAllocatorObject(Generic[U]):
174
175
  return self.obj.is_healthy()
175
176
  return True
176
177
 
177
- def set_unhealthy(self, duration: int = 3600):
178
+ def set_healthy(self, duration: Timeout = 3600):
179
+ """Set the object as healthy."""
180
+ if self.obj is not None and self.obj.name is not None:
181
+ self.allocator.update_soft_bind(self.obj.name, self.key, duration)
182
+ if self.allocator.shared:
183
+ self.allocator.unlock(self.key)
184
+
185
+ def set_unhealthy(self, duration: Timeout = 3600):
178
186
  """Set the object as unhealthy."""
179
187
  if self.obj is not None and self.obj.name is not None:
180
188
  self.allocator.unbind_soft_bind(self.obj.name)
181
189
  self.allocator.update(self.key, timeout=duration)
182
190
 
183
- def refresh(self, timeout: Timeout = 120):
191
+ def refresh(self, timeout: Timeout = 120, cache_timeout: Timeout = 3600):
184
192
  """Refresh the object."""
185
193
  self.close()
186
194
  new_obj = self.allocator.policy.malloc(self.allocator, timeout=timeout,
187
- obj=self.obj, params=self.params)
195
+ obj=self.obj, params=self.params,
196
+ cache_timeout=cache_timeout)
188
197
  if new_obj is not None:
189
198
  self.obj = new_obj.obj
190
199
  self.key = new_obj.key
191
200
  self.params = new_obj.params
201
+ self.open()
202
+
203
+ def refresh_until_healthy(self, timeout: Timeout = 120, max_attempts: int = 10, lock_duration: Timeout = 3600, cache_timeout: Timeout = 3600):
204
+ """Refresh the object until it is healthy."""
205
+ for _ in range(max_attempts):
206
+ try:
207
+ if self.is_healthy():
208
+ return
209
+ except Exception as e:
210
+ logger.error(f"Error checking health of {self.key}: {e}")
211
+ self.set_unhealthy(lock_duration)
212
+ self.refresh(timeout, cache_timeout)
213
+ raise RuntimeError("the objects is still unhealthy after %d attempts", max_attempts)
192
214
 
193
215
  @property
194
216
  def unique_id(self) -> str:
@@ -282,9 +304,11 @@ class RedisAllocatorPolicy(ABC):
282
304
 
283
305
  def check_health_once(self, r_obj: RedisAllocatorObject, duration: int = 3600) -> bool:
284
306
  """Check the health of the object."""
285
- with contextlib.closing(r_obj):
307
+ with contextlib.closing(r_obj.open()):
286
308
  try:
287
309
  if r_obj.is_healthy():
310
+ if r_obj.allocator.shared:
311
+ r_obj.allocator.unlock(r_obj.key) # set the key as free
288
312
  return True
289
313
  else:
290
314
  r_obj.set_unhealthy(duration)
@@ -308,6 +332,7 @@ class RedisAllocatorPolicy(ABC):
308
332
  A tuple containing the number of healthy and unhealthy items in the allocator
309
333
  """
310
334
  with ThreadPoolExecutor(max_workers=max_threads) as executor:
335
+ inputs = []
311
336
  for key in allocator.keys():
312
337
  if params_fn is not None:
313
338
  params = params_fn(key)
@@ -317,9 +342,11 @@ class RedisAllocatorPolicy(ABC):
317
342
  obj = obj_fn(key)
318
343
  else:
319
344
  obj = None
320
- alloc_obj = RedisAllocatorObject(allocator, key, obj, params)
321
- executor.submit(self.check_health_once, alloc_obj, lock_duration)
322
- executor.shutdown(wait=True)
345
+ inputs.append(RedisAllocatorObject(allocator, key, obj, params))
346
+ results = list(executor.map(self.check_health_once, inputs, timeout=max_threads * lock_duration / (len(inputs) + 1)))
347
+ healthy = sum(results)
348
+ unhealthy = len(results) - healthy
349
+ return healthy, unhealthy
323
350
 
324
351
 
325
352
  class DefaultRedisAllocatorPolicy(RedisAllocatorPolicy):
@@ -346,7 +373,8 @@ class DefaultRedisAllocatorPolicy(RedisAllocatorPolicy):
346
373
  """
347
374
 
348
375
  def __init__(self, gc_count: int = 5, update_interval: int = 300,
349
- expiry_duration: int = -1, updater: Optional[RedisAllocatorUpdater] = None):
376
+ expiry_duration: int = -1, updater: Optional[RedisAllocatorUpdater] = None,
377
+ auto_close: bool = False):
350
378
  """Initialize the default allocation policy.
351
379
 
352
380
  Args:
@@ -354,6 +382,7 @@ class DefaultRedisAllocatorPolicy(RedisAllocatorPolicy):
354
382
  update_interval: Interval in seconds between pool updates
355
383
  expiry_duration: Default timeout for pool items (-1 means no timeout)
356
384
  updater: Optional updater for refreshing the pool's keys
385
+ auto_close: If True, the allocator will automatically close the object when it is not unique
357
386
  """
358
387
  self.gc_count = gc_count
359
388
  self.update_interval: float = update_interval
@@ -362,6 +391,7 @@ class DefaultRedisAllocatorPolicy(RedisAllocatorPolicy):
362
391
  self._allocator: Optional[weakref.ReferenceType['RedisAllocator']] = None
363
392
  self._update_lock_key: Optional[str] = None
364
393
  self.objects: weakref.WeakValueDictionary[str, RedisAllocatorObject] = weakref.WeakValueDictionary()
394
+ self.auto_close = auto_close
365
395
 
366
396
  def initialize(self, allocator: 'RedisAllocator'):
367
397
  """Initialize the policy with an allocator instance.
@@ -407,9 +437,10 @@ class DefaultRedisAllocatorPolicy(RedisAllocatorPolicy):
407
437
  key = allocator.malloc_key(timeout, obj_name,
408
438
  cache_timeout=cache_timeout)
409
439
  alloc_obj = RedisAllocatorObject(allocator, key, obj, params)
410
- old_obj = self.objects.get(alloc_obj.unique_id, None)
411
- if old_obj is not None:
412
- old_obj.close()
440
+ if self.auto_close:
441
+ old_obj = self.objects.get(alloc_obj.unique_id, None)
442
+ if old_obj is not None:
443
+ old_obj.close()
413
444
  self.objects[alloc_obj.unique_id] = alloc_obj
414
445
  return alloc_obj
415
446
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: redis-allocator
3
- Version: 0.3.2
3
+ Version: 0.4.0
4
4
  Summary: Redis-based resource allocation system.
5
5
  Home-page: https://github.com/invoker-bot/RedisAllocator-python
6
6
  Author: Invoker Bot
@@ -230,7 +230,7 @@ if allocated_obj:
230
230
 
231
231
  # Using soft binding (associates a name with a resource)
232
232
  allocator.update_soft_bind("worker-1", "resource-1")
233
- # Later...
233
+ # Unbind soft binding
234
234
  allocator.unbind_soft_bind("worker-1")
235
235
 
236
236
  # Garbage collection (reclaims unused resources)
@@ -291,9 +291,9 @@ graph TD
291
291
 
292
292
  subgraph "PoolHash Contents (Doubly-Linked Free List)"
293
293
  Key1 --> Key2["Key2: Key1||Key3||Expiry"]
294
- Key2 --> Key3["Key3: Key2||...||Expiry"]
295
- Key3 --> ...
296
- KeyN_1[...] --> KeyN
294
+ Key2 --> Key3["Key3: Key2||Key4||Expiry"]
295
+ Key3 --> Key4["Key4: Key3||Key5||Expiry"]
296
+ KeyN_1["KeyN: KeyN-1||""||Expiry"] --> KeyN
297
297
  end
298
298
 
299
299
  subgraph "Allocated Keys (Non-Shared Mode)"
@@ -1,15 +1,15 @@
1
1
  redis_allocator/__init__.py,sha256=TVjUm-8YEu_MQD_PkfeIKiVknpCJBrUY9cWN1LlaZcU,1016
2
- redis_allocator/_version.py,sha256=bqu4G7Is-8Were-hMi-SthEalL7QW-PAbKWGZ9pVB6U,22
3
- redis_allocator/allocator.py,sha256=Q0oDhUyjlpk6WoibvmlFS0pEmxk3qwLUHB277P3EMrE,47120
2
+ redis_allocator/_version.py,sha256=2eiWQI55fd-roDdkt4Hvl9WzrTJ4xQo33VzFud6D03U,22
3
+ redis_allocator/allocator.py,sha256=Bo4Ck7mqg-Z8KdQH-iqeHfdEbyhMqBD0c2g5T3E5sBU,48730
4
4
  redis_allocator/lock.py,sha256=fqf6WUWHKYenEArWopMIF6kWEnDfADC-bZvnQImsQVo,27400
5
5
  redis_allocator/task_queue.py,sha256=8DjNr2uxhzCsHatV_CHOeGh7_K9pqQZFApSbe2blRO0,14989
6
- redis_allocator-0.3.2.dist-info/licenses/LICENSE,sha256=Wt4X1rHpffQfEiyWcDUx8BMLjXxfPqaiYZ7Lgsj7L4c,1068
6
+ redis_allocator-0.4.0.dist-info/licenses/LICENSE,sha256=Wt4X1rHpffQfEiyWcDUx8BMLjXxfPqaiYZ7Lgsj7L4c,1068
7
7
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- tests/conftest.py,sha256=1QA3fxdX2XmVDEXY9L9D6NAWlAxlyifK8W9rnO24ig8,3947
8
+ tests/conftest.py,sha256=Ts82uylQSzP_GcaN0E02o3xcFdjw20cXNzh3RAdYKW4,3967
9
9
  tests/test_allocator.py,sha256=hFKgLe_yONtEjjm6zssUnhK0tzihG_1xZMziztHmqqA,22404
10
10
  tests/test_lock.py,sha256=MDMRNN46VhWqkHUIhYOMEDgZkFFCW_WjwRLTOjkFF-Q,46952
11
11
  tests/test_task_queue.py,sha256=Fh5naikFajfOvL6GngEy_TPfOYCYZolZfVwtR6T4dTY,31710
12
- redis_allocator-0.3.2.dist-info/METADATA,sha256=H_C0Um1MegEzuo6A_UhgWkIN0SsQA_CScHhfwqatfw0,21653
13
- redis_allocator-0.3.2.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
14
- redis_allocator-0.3.2.dist-info/top_level.txt,sha256=0hXzU7sK5FCeSolTEYxThOt3HOybnwaXv1FLRJvHVgI,22
15
- redis_allocator-0.3.2.dist-info/RECORD,,
12
+ redis_allocator-0.4.0.dist-info/METADATA,sha256=rXeA3Nsb6eie8H5G10HXLlLI8jCRs45I1C06seN-Sgs,21727
13
+ redis_allocator-0.4.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
14
+ redis_allocator-0.4.0.dist-info/top_level.txt,sha256=0hXzU7sK5FCeSolTEYxThOt3HOybnwaXv1FLRJvHVgI,22
15
+ redis_allocator-0.4.0.dist-info/RECORD,,
tests/conftest.py CHANGED
@@ -66,6 +66,7 @@ class _TestObject(RedisAllocatableClass):
66
66
  def open(self):
67
67
  """Open the object."""
68
68
  self.closed = False
69
+ return self
69
70
 
70
71
  def close(self):
71
72
  """Mark the object as closed."""