Skip to content

Add queue diagnostics page#48

Merged
salvageop merged 11 commits into
CommunityMaintained:masterfrom
profesorabbott:queue-diagnostics
Jun 18, 2026
Merged

Add queue diagnostics page#48
salvageop merged 11 commits into
CommunityMaintained:masterfrom
profesorabbott:queue-diagnostics

Conversation

@profesorabbott

Copy link
Copy Markdown

What's new?

Adds a new diagnostics page under Config menu to monitor and manage Oban job queues. Helps troubleshoot stuck or failed jobs without needing to query the database directly.

What's changed?

Features:

  • System overview (pending downloads, total media, sources, db size)
  • Queue health status with running/available/scheduled/retryable counts
  • Stuck jobs detection (executing > 30 min) with reset/cancel actions
  • Failed jobs list with error details and bulk reset option

What's fixed?

N/A

Any other comments?

Tested for more than a month but generated by claude code

Copied from PR Add queue diagnostics page from the original branch

ddacunha and others added 3 commits January 18, 2026 21:29
Adds a new diagnostics page under Config menu to monitor and manage
Oban job queues. Helps troubleshoot stuck or failed jobs without
needing to query the database directly.

Features:
- System overview (pending downloads, total media, sources, db size)
- Queue health status with running/available/scheduled/retryable counts
- Stuck jobs detection (executing > 30 min) with reset/cancel actions
- Failed jobs list with error details and bulk reset option
Use new HEEx syntax with curly braces for expressions and fix line
length issues with multi-line attribute formatting.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@profesorabbott profesorabbott marked this pull request as ready for review June 18, 2026 01:40
@salvageop

Copy link
Copy Markdown
Collaborator

I like this idea. I'll try to get this tomorrow

The diagnostics page counted every un-downloaded item that wasn't
explicitly prevented, ignoring source cutoff dates, shorts/livestream
preferences, title regex and duration limits. This massively inflated
the figure (10k+ on real systems) versus what the app actually
schedules. Reuse MediaQuery.pending/0 so the count matches reality.
Split the failed-jobs card into Retryable and Discarded tabs so jobs
that exhausted their retries (previously invisible) are surfaced.
Retryable stays the default tab and remains the basis for queue health
status. Reset now also works on discarded jobs to re-queue them.

UI: drop the red exclamation-circle icon (made a healthy page look
alarming) and use a plain heading consistent with the cards above.
reset_job/cancel_job called String.to_integer/1 on the :id path param,
raising (500) on any non-numeric input. Parse with Integer.parse/1 and
flash a clear error instead.
SQLite keeps a -wal and -shm sidecar alongside the main db file. The
diagnostics page only stat'd the main file, under-reporting actual
on-disk usage. Sum all three.
The queue names were hardcoded, so any change to the Oban queues config
would silently omit queues from the diagnostics page. Read them from
the configured Oban queues instead.
The App Info page held only two buttons (Copy Diagnostic Info, Download
Logs). Move them into the Diagnostics page header and remove the now-empty
App Info page, route, and sidebar entry. diagnostic_info_string/0 moves to
DiagnosticsHTML; download-logs error redirect and the 500 page link now
point at /diagnostics.
Resetting an executing job to available via raw update_all could start a
second copy of a still-running job concurrently (duplicate yt-dlp
downloads, file races, higher IP-ban risk). Genuine orphans are already
recovered safely at boot by PreJobStartupTasks (runs before Oban starts,
so every executing row is provably orphaned), so the on-demand reset was
both redundant and dangerous.

- Drop the 'Reset All Stuck Jobs' bulk action, its route, controller
  action and QueueDiagnostics.reset_stuck_jobs/1
- Remove the per-row Reset link from the long-running jobs table; keep
  Cancel, which goes through Oban.cancel_job/1 and stops a live process
- Exclude 'executing' from reset_job/1 so no reset path can touch a live job
- Reframe the section as 'Long-Running Jobs' with accurate copy
@salvageop

Copy link
Copy Markdown
Collaborator

Hi @profesorabbott so there turned out to be a few gotchas in this PR:

  1. the Pending Jobs was showing incorrect number - it included videos that were disqualified based on user's preferences (eg. length, quality ...) and those just should not be counted as Pending.
  2. the Failed Jobs area was only considering Failed but retry-able jobs. I added a second tab that shows discarded (ie. not-retryable) and unified the look to match the upper part of the diag page.
  3. minor updates to DB size reporting
  4. I actually removed the Reset stuck jobs because it was not safe and was not resetting stuck jobs but all jobs. There is a queue pre-start mechanism to clear stuck jobs.
    I'll do more testing on this but I did not want to merge in something that is unsafe.
  5. I also removed the App Info page with only two buttons and moved them to the Diagnostics page. Imo it makes sense more in that area.

@salvageop salvageop merged commit e9fe757 into CommunityMaintained:master Jun 18, 2026
3 checks passed
salvageop added a commit that referenced this pull request Jun 18, 2026
* Add queue diagnostics page

Adds a new diagnostics page under Config menu to monitor and manage
Oban job queues. Helps troubleshoot stuck or failed jobs without
needing to query the database directly.

Features:
- System overview (pending downloads, total media, sources, db size)
- Queue health status with running/available/scheduled/retryable counts
- Stuck jobs detection (executing > 30 min) with reset/cancel actions
- Failed jobs list with error details and bulk reset option

* Fix heex template formatting

Use new HEEx syntax with curly braces for expressions and fix line
length issues with multi-line attribute formatting.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Use Enum.map_join instead of Enum.map |> Enum.join

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: count pending downloads using canonical MediaQuery.pending

The diagnostics page counted every un-downloaded item that wasn't
explicitly prevented, ignoring source cutoff dates, shorts/livestream
preferences, title regex and duration limits. This massively inflated
the figure (10k+ on real systems) versus what the app actually
schedules. Reuse MediaQuery.pending/0 so the count matches reality.

* feat: show discarded jobs in diagnostics and align failed-jobs UI

Split the failed-jobs card into Retryable and Discarded tabs so jobs
that exhausted their retries (previously invisible) are surfaced.
Retryable stays the default tab and remains the basis for queue health
status. Reset now also works on discarded jobs to re-queue them.

UI: drop the red exclamation-circle icon (made a healthy page look
alarming) and use a plain heading consistent with the cards above.

* fix: guard diagnostics job actions against non-integer ids

reset_job/cancel_job called String.to_integer/1 on the :id path param,
raising (500) on any non-numeric input. Parse with Integer.parse/1 and
flash a clear error instead.

* fix: include WAL/SHM files in reported database size

SQLite keeps a -wal and -shm sidecar alongside the main db file. The
diagnostics page only stat'd the main file, under-reporting actual
on-disk usage. Sum all three.

* fix: derive diagnostics queue list from Oban config

The queue names were hardcoded, so any change to the Oban queues config
would silently omit queues from the diagnostics page. Read them from
the configured Oban queues instead.

* refactor: fold App Info buttons into Diagnostics, drop App Info page

The App Info page held only two buttons (Copy Diagnostic Info, Download
Logs). Move them into the Diagnostics page header and remove the now-empty
App Info page, route, and sidebar entry. diagnostic_info_string/0 moves to
DiagnosticsHTML; download-logs error redirect and the 500 page link now
point at /diagnostics.

* fix: remove unsafe stuck-job reset to prevent double execution

Resetting an executing job to available via raw update_all could start a
second copy of a still-running job concurrently (duplicate yt-dlp
downloads, file races, higher IP-ban risk). Genuine orphans are already
recovered safely at boot by PreJobStartupTasks (runs before Oban starts,
so every executing row is provably orphaned), so the on-demand reset was
both redundant and dangerous.

- Drop the 'Reset All Stuck Jobs' bulk action, its route, controller
  action and QueueDiagnostics.reset_stuck_jobs/1
- Remove the per-row Reset link from the long-running jobs table; keep
  Cancel, which goes through Oban.cancel_job/1 and stops a live process
- Exclude 'executing' from reset_job/1 so no reset path can touch a live job
- Reframe the section as 'Long-Running Jobs' with accurate copy

---------

Co-authored-by: Daniel Da Cunha <daniel@ddc.im>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: salvageop <293462701+salvageop@users.noreply.github.com>
Co-authored-by: salvageop <293462701+CommunityMaintained@users.noreply.github.com>
salvageop added a commit that referenced this pull request Jun 19, 2026
* Add queue diagnostics page (#48)

* Add queue diagnostics page

Adds a new diagnostics page under Config menu to monitor and manage
Oban job queues. Helps troubleshoot stuck or failed jobs without
needing to query the database directly.

Features:
- System overview (pending downloads, total media, sources, db size)
- Queue health status with running/available/scheduled/retryable counts
- Stuck jobs detection (executing > 30 min) with reset/cancel actions
- Failed jobs list with error details and bulk reset option

* Fix heex template formatting

Use new HEEx syntax with curly braces for expressions and fix line
length issues with multi-line attribute formatting.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Use Enum.map_join instead of Enum.map |> Enum.join

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: count pending downloads using canonical MediaQuery.pending

The diagnostics page counted every un-downloaded item that wasn't
explicitly prevented, ignoring source cutoff dates, shorts/livestream
preferences, title regex and duration limits. This massively inflated
the figure (10k+ on real systems) versus what the app actually
schedules. Reuse MediaQuery.pending/0 so the count matches reality.

* feat: show discarded jobs in diagnostics and align failed-jobs UI

Split the failed-jobs card into Retryable and Discarded tabs so jobs
that exhausted their retries (previously invisible) are surfaced.
Retryable stays the default tab and remains the basis for queue health
status. Reset now also works on discarded jobs to re-queue them.

UI: drop the red exclamation-circle icon (made a healthy page look
alarming) and use a plain heading consistent with the cards above.

* fix: guard diagnostics job actions against non-integer ids

reset_job/cancel_job called String.to_integer/1 on the :id path param,
raising (500) on any non-numeric input. Parse with Integer.parse/1 and
flash a clear error instead.

* fix: include WAL/SHM files in reported database size

SQLite keeps a -wal and -shm sidecar alongside the main db file. The
diagnostics page only stat'd the main file, under-reporting actual
on-disk usage. Sum all three.

* fix: derive diagnostics queue list from Oban config

The queue names were hardcoded, so any change to the Oban queues config
would silently omit queues from the diagnostics page. Read them from
the configured Oban queues instead.

* refactor: fold App Info buttons into Diagnostics, drop App Info page

The App Info page held only two buttons (Copy Diagnostic Info, Download
Logs). Move them into the Diagnostics page header and remove the now-empty
App Info page, route, and sidebar entry. diagnostic_info_string/0 moves to
DiagnosticsHTML; download-logs error redirect and the 500 page link now
point at /diagnostics.

* fix: remove unsafe stuck-job reset to prevent double execution

Resetting an executing job to available via raw update_all could start a
second copy of a still-running job concurrently (duplicate yt-dlp
downloads, file races, higher IP-ban risk). Genuine orphans are already
recovered safely at boot by PreJobStartupTasks (runs before Oban starts,
so every executing row is provably orphaned), so the on-demand reset was
both redundant and dangerous.

- Drop the 'Reset All Stuck Jobs' bulk action, its route, controller
  action and QueueDiagnostics.reset_stuck_jobs/1
- Remove the per-row Reset link from the long-running jobs table; keep
  Cancel, which goes through Oban.cancel_job/1 and stops a live process
- Exclude 'executing' from reset_job/1 so no reset path can touch a live job
- Reframe the section as 'Long-Running Jobs' with accurate copy

---------

Co-authored-by: Daniel Da Cunha <daniel@ddc.im>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: salvageop <293462701+salvageop@users.noreply.github.com>
Co-authored-by: salvageop <293462701+CommunityMaintained@users.noreply.github.com>

* chore(deps): update github actions to v7

---------

Co-authored-by: profesorabbott <profesorabbott@gmail.com>
Co-authored-by: Daniel Da Cunha <daniel@ddc.im>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: salvageop <293462701+salvageop@users.noreply.github.com>
Co-authored-by: salvageop <293462701+CommunityMaintained@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
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.

3 participants