Published on June 5, 2025
Go homeA reconciliation loop template
"""Reconciliation loop template."""
import logging
import signal
import time
import typing as t
from django.core.management.base import BaseCommand
from django.utils import timezone
from abacus.db import models as db_models
logger = logging.getLogger(__name__)
class LoopState:
"""Provides an interruptible sleeper."""
def __init__(self) -> None:
self.running = True
signal.signal(signal.SIGINT, self.stop_soon)
def stop_soon(self, *args: t.Any) -> None:
logger.info("Loop will stop soon.")
self.running = False
def sleep(self, secs: float) -> None:
"""Sleep for the provided number of seconds."""
downtime = 0.0
sleep_secs = 0.2
while downtime < secs and self.running:
downtime += sleep_secs
time.sleep(sleep_secs)
class Command(BaseCommand):
"""Event handler fired when a motion event ends."""
def handle(self, **options: t.Any) -> None:
"""Run the loop."""
tzinfo = timezone.get_current_timezone()
sysuser = db_models.get_sysuser()
counter = 0
max_sleep_secs = 32
sleep_sec_mapping = {
0: 1,
1: 2,
2: 4,
3: 8,
4: 16,
5: max_sleep_secs,
}
state = LoopState()
while state.running:
tasks = []
task_len = 0
if task_len == 0:
counter += 1
else:
counter = 0
while tasks:
tasks.pop(0)
sleep_secs = sleep_sec_mapping.get(counter, max_sleep_secs)
logger.info(f"Processed {task_len} record(s), sleeping for {sleep_secs} secs")
state.sleep(sleep_secs)