Skip to content

Make worker startup logging visible, bound dataset downloads, persist blocked list - #139

Open
maximusunc wants to merge 1 commit into
mainfrom
claude/arax-pathfinder-deployment-187m9a
Open

Make worker startup logging visible, bound dataset downloads, persist blocked list#139
maximusunc wants to merge 1 commit into
mainfrom
claude/arax-pathfinder-deployment-187m9a

Conversation

@maximusunc

Copy link
Copy Markdown
Collaborator

Three fixes prompted by an arax_pathfinder deployment that picked up no tasks, emitted no logs at all, and was terminated after ~8 minutes.

Startup logging was silently discarded. setup_logging attached its handlers to the shepherd logger and left root unconfigured, but every worker logged its startup phase through logging.getLogger(STREAM) ("arax.pathfinder") or a bare logging.info. Those names sit outside the shepherd namespace, so records reached a handler-less root and were dropped by logging's lastResort fallback, which only emits WARNING+. Dataset downloads, pool sizing and poll-loop errors were invisible, and a worker wedged before get_tasks looked exactly like a healthy idle one.

Handlers now live on root, and shepherd sets only its level and propagates to them -- so our logging still emits at DEBUG, third-party libraries stay quiet below WARNING (httpx logs a line per request at INFO), nothing double-logs, and pytest's caplog keeps working. Workers get their logger from a new get_worker_logger helper that namespaces the stream name, applied across all 21 workers plus the two module-named loggers in data_download and db.

Dataset downloads had no timeout. urllib defaults to none, so a connection that opens and then stalls -- an egress proxy that swallows the request, a server that accepts and never responds -- hung worker startup indefinitely, before the poll loop and before any heartbeat, without ever crashing. They are now bounded by DATASET_DOWNLOAD_TIMEOUT_SEC (default 60), applied per socket operation so a multi-GB file still downloads for as long as it keeps making progress. A stall part-way through the body raises TimeoutError straight out of resp.read() rather than wrapped in URLError, so it gets its own handler and a message naming the likely cause.

The ARAX blocked-concept list was written to the worker's working directory, which is the container's writable layer -- discarded on every restart, so each new pod re-fetched it from GitHub during startup on that same invisible code path. It now lives alongside the pathfinder sqlite databases on the mounted volume, reached through an arax_blocked_list_path helper that keeps the download and read sites in agreement. It is only fetched when absent, so delete it from the volume to pick up an updated upstream list, and the volume must be writable for the first run (or the file preloaded).

Also documents the deployment trap behind the original incident: the "already present" check is an exact match on ARAX_PATHFINDER_DBS_DIR (a relative path by default, resolved against /app) plus the tier-versioned filenames, so a mount path or ARAX_PATHFINDER_TIER_VERSION that disagrees re-downloads the databases onto ephemeral storage instead of using the mounted copies -- which is what got the pod evicted.

Claude-Session: https://claude.ai/code/session_01VqmpAvRW688q7KqX9sQjGX

… blocked list

Three fixes prompted by an arax_pathfinder deployment that picked up no tasks,
emitted no logs at all, and was terminated after ~8 minutes.

Startup logging was silently discarded. setup_logging attached its handlers to
the `shepherd` logger and left root unconfigured, but every worker logged its
startup phase through `logging.getLogger(STREAM)` ("arax.pathfinder") or a bare
`logging.info`. Those names sit outside the `shepherd` namespace, so records
reached a handler-less root and were dropped by logging's lastResort fallback,
which only emits WARNING+. Dataset downloads, pool sizing and poll-loop errors
were invisible, and a worker wedged before `get_tasks` looked exactly like a
healthy idle one.

Handlers now live on root, and `shepherd` sets only its level and propagates to
them -- so our logging still emits at DEBUG, third-party libraries stay quiet
below WARNING (httpx logs a line per request at INFO), nothing double-logs, and
pytest's caplog keeps working. Workers get their logger from a new
`get_worker_logger` helper that namespaces the stream name, applied across all
21 workers plus the two module-named loggers in data_download and db.

Dataset downloads had no timeout. urllib defaults to none, so a connection that
opens and then stalls -- an egress proxy that swallows the request, a server
that accepts and never responds -- hung worker startup indefinitely, before the
poll loop and before any heartbeat, without ever crashing. They are now bounded
by DATASET_DOWNLOAD_TIMEOUT_SEC (default 60), applied per socket operation so a
multi-GB file still downloads for as long as it keeps making progress. A stall
part-way through the body raises TimeoutError straight out of resp.read()
rather than wrapped in URLError, so it gets its own handler and a message
naming the likely cause.

The ARAX blocked-concept list was written to the worker's working directory,
which is the container's writable layer -- discarded on every restart, so each
new pod re-fetched it from GitHub during startup on that same invisible code
path. It now lives alongside the pathfinder sqlite databases on the mounted
volume, reached through an `arax_blocked_list_path` helper that keeps the
download and read sites in agreement. It is only fetched when absent, so
delete it from the volume to pick up an updated upstream list, and the volume
must be writable for the first run (or the file preloaded).

Also documents the deployment trap behind the original incident: the
"already present" check is an exact match on ARAX_PATHFINDER_DBS_DIR (a
relative path by default, resolved against /app) plus the tier-versioned
filenames, so a mount path or ARAX_PATHFINDER_TIER_VERSION that disagrees
re-downloads the databases onto ephemeral storage instead of using the
mounted copies -- which is what got the pod evicted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VqmpAvRW688q7KqX9sQjGX
@codecov

codecov Bot commented Aug 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 45.52846% with 67 lines in your changes missing coverage. Please review.
✅ Project coverage is 53.48%. Comparing base (6fbd183) to head (e4b3587).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
workers/arax_pathfinder/worker.py 16.66% 10 Missing ⚠️
workers/arax_rank/worker.py 0.00% 6 Missing ⚠️
workers/score_paths/worker.py 0.00% 6 Missing ⚠️
workers/aragorn_omnicorp/worker.py 28.57% 5 Missing ⚠️
workers/aragorn_score/worker.py 33.33% 4 Missing ⚠️
workers/arax/worker.py 0.00% 4 Missing ⚠️
workers/merge_message/worker.py 42.85% 4 Missing ⚠️
workers/aragorn/worker.py 50.00% 2 Missing ⚠️
workers/aragorn_lookup/worker.py 50.00% 2 Missing ⚠️
workers/aragorn_pathfinder/worker.py 50.00% 2 Missing ⚠️
... and 11 more
Files with missing lines Coverage Δ
shepherd_utils/config.py 95.28% <100.00%> (+0.04%) ⬆️
shepherd_utils/data_download.py 78.62% <100.00%> (+22.88%) ⬆️
shepherd_utils/db.py 62.09% <100.00%> (ø)
shepherd_utils/logger.py 96.36% <100.00%> (+0.28%) ⬆️
workers/aragorn/worker.py 72.50% <50.00%> (+1.44%) ⬆️
workers/aragorn_lookup/worker.py 76.44% <50.00%> (+0.22%) ⬆️
workers/aragorn_pathfinder/worker.py 63.71% <50.00%> (+0.65%) ⬆️
workers/bte/worker.py 72.50% <50.00%> (+1.44%) ⬆️
workers/bte_lookup/worker.py 85.38% <50.00%> (+0.13%) ⬆️
workers/example_ara/worker.py 66.66% <50.00%> (+2.15%) ⬆️
... and 15 more

... and 1 file with indirect coverage changes


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update fd09b51...e4b3587. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants