akkudoktoreos.server.retentionmanager.RetentionManager
- class akkudoktoreos.server.retentionmanager.RetentionManager(config_getter: Callable[[str], Any], *, shutdown_timeout: float = 30.0)
Bases:
objectOrchestrates all periodic server-maintenance jobs.
The manager itself is driven by an external
make_repeated_taskheartbeat (the compaction tick). Aconfig_gettercallable — accepting a string key and returning the corresponding value — is supplied at initialisation and stored on every registered job, keeping the manager decoupled from any specific config implementation.Jobs are launched as independent
asyncio.Taskobjects so they run concurrently without blocking the tick. Call shutdown during application teardown to wait for any in-flight tasks to complete before the event loop closes. A configurable shutdown_timeout prevents the wait from blocking indefinitely; jobs still running after the timeout are reported by name but not cancelled.- __init__(config_getter: Callable[[str], Any], *, shutdown_timeout: float = 30.0) None
Initialise the manager with a configuration accessor.
- Parameters:
config_getter – Callable that accepts a string key and returns the corresponding configuration value. Used by each registered job to look up its interval in seconds.
shutdown_timeout – Maximum number of seconds to wait for in-flight jobs to finish during shutdown. If the timeout elapses before all tasks complete, an error is logged and the names of the still-running jobs are reported. The tasks are not cancelled so they may continue running until the event loop closes. Defaults to 30.0.
Example:
manager = RetentionManager(get_config().get_nested_value, shutdown_timeout=60.0)
Methods
__init__(config_getter, *[, shutdown_timeout])Initialise the manager with a configuration accessor.
register(name, func, *, interval_attr[, ...])Register a maintenance function with the manager.
shutdown()Wait for all currently running job tasks to complete.
status()Return a snapshot of every job's state for health or metrics endpoints.
tick()Single compaction tick: check every job and fire those that are due.
unregister(name)Remove a previously registered job from the manager.
- __init__(config_getter: Callable[[str], Any], *, shutdown_timeout: float = 30.0) None
Initialise the manager with a configuration accessor.
- Parameters:
config_getter – Callable that accepts a string key and returns the corresponding configuration value. Used by each registered job to look up its interval in seconds.
shutdown_timeout – Maximum number of seconds to wait for in-flight jobs to finish during shutdown. If the timeout elapses before all tasks complete, an error is logged and the names of the still-running jobs are reported. The tasks are not cancelled so they may continue running until the event loop closes. Defaults to 30.0.
Example:
manager = RetentionManager(get_config().get_nested_value, shutdown_timeout=60.0)
- register(name: str, func: Callable[[], None] | Callable[[], Coroutine[Any, Any, None]], *, interval_attr: str, fallback_interval: float = 300.0, on_exception: Callable[[Exception], None] | Callable[[Exception], Coroutine[Any, Any, None]] | None = None) None
Register a maintenance function with the manager.
- Parameters:
name – Unique human-readable job name used in logs and metrics.
func – The maintenance callable. Must accept no arguments.
interval_attr – Key passed to
config_getterto retrieve the interval in seconds for this job. When the config value isNonethe job is treated as disabled and will never fire.fallback_interval – Seconds to use when the config attribute is missing or zero. Defaults to
300.0.on_exception – Optional callable invoked with the raised exception whenever
funcfails. Useful for cleanup or alerting. May be sync or async.
- Raises:
ValueError – If a job with the given
nameis already registered.
- unregister(name: str) None
Remove a previously registered job from the manager.
If no job with the given name exists, this is a no-op.
- Parameters:
name – The name of the job to remove.
- async tick() None
Single compaction tick: check every job and fire those that are due.
Each job resolves its own interval via the
config_gettercaptured at registration time. Jobs whose interval isNoneare silently skipped (disabled). Due jobs are launched as independentasyncio.Taskobjects so they run concurrently without blocking the tick. Each task is tracked in_running_tasksand removed automatically on completion, allowing shutdown to await all of them gracefully.Jobs that are still running from a previous tick are skipped to prevent overlapping executions.
Note
This is the function you pass to
make_repeated_task.
- async shutdown() None
Wait for all currently running job tasks to complete.
Waits up to shutdown_timeout seconds (configured at initialisation) for in-flight tasks to finish. If the timeout elapses before all tasks complete, an error is logged listing the names of the jobs that are still running. Those tasks are not cancelled — they continue until the event loop closes — but shutdown returns so that application teardown is not blocked indefinitely.
Returns immediately if no tasks are running.
Example:
@asynccontextmanager async def lifespan(app: FastAPI): tick_task = make_repeated_task(manager.tick, seconds=5, wait_first=2) await tick_task()
- Yields:
await manager.shutdown()
- status() list[dict]
Return a snapshot of every job’s state for health or metrics endpoints.
- Returns:
A list of dictionaries, one per registered job, each produced by JobState.summary.