call_recorder: spool failed uploads to disk and retry instead of deleting (node-26#44)
upload_recording deleted the file in `finally` even when the POST failed, so
any C2 outage, 5xx or timeout lost the call's audio for good.
- Delete only after a 2xx that carries a URL (C2 answers 200 {url: null}
when its GCS write fails).
- Retryable failures (network, timeout, 5xx, 401/408/429, 2xx-no-url) move
the file plus a JSON sidecar (call_id, talkgroup, system_id, audio epochs)
into <recordings>/spool. A background task retries with exponential
backoff and drains the spool on startup. Other 4xx are deleted with an
ERROR naming the status.
- Bounded: UPLOAD_SPOOL_MAX_BYTES (512 MiB) and UPLOAD_SPOOL_MAX_AGE_HOURS
(72), oldest evicted first, since a full host disk stops recording (#41).
- Crash-safe: the sidecar is fsynced and written before the audio moves in;
a lost or corrupt sidecar is rebuilt from the filename instead of
deleting the audio.
- The inline attempt uses UPLOAD_INLINE_TIMEOUT_SECONDS (30), so a
black-holed C2 no longer stalls the segmenter for 120 s per call.
- call_end carries audio_upload=spooled|rejected|lost; a new API key wakes
the spool; /api/status and the checkin report spool depth and the last
upload outcome (toward #40).
Reviewed by drb-audio-review. Its two server-side findings (late uploads
correlated against now; no /upload idempotency) are filed separately.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
1b4fcefbf2
commit
6b884d5433
@@ -153,6 +153,23 @@ class Settings(BaseSettings):
|
||||
# Offline call buffer — how many call_end events to keep while disconnected
|
||||
offline_call_buffer_size: int = 35
|
||||
|
||||
# Upload spool (node-26#44) — recordings whose upload to C2 failed are kept
|
||||
# here and retried instead of deleted. Defaults to <recordings_path>/spool,
|
||||
# i.e. on the host disk via the /recordings bind mount, so it survives a
|
||||
# restart. Bounded both ways because a full host disk stops recording
|
||||
# outright (node-26#41): FLAC is ~1.3 MB per minute of audio, so 512 MiB is
|
||||
# roughly 6 h of continuous traffic. Oldest entries are evicted first.
|
||||
upload_spool_dir: Optional[str] = None
|
||||
upload_spool_max_bytes: int = 512 * 1024 * 1024
|
||||
upload_spool_max_age_hours: float = 72.0
|
||||
# Exponential backoff between retries of one spooled recording.
|
||||
upload_retry_min_seconds: float = 15.0
|
||||
upload_retry_max_seconds: float = 600.0
|
||||
# Timeout for the FIRST, inline upload attempt. metadata_watcher awaits the
|
||||
# call close, so a black-holed C2 used to stall segmentation for the full
|
||||
# 120 s; a failure here just lands in the spool.
|
||||
upload_inline_timeout_seconds: float = 30.0
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Local dashboard / API authentication
|
||||
#
|
||||
|
||||
Reference in New Issue
Block a user