Skip to content

fix(plugin-manager): parallel, non-blocking Plugins page update check#2667

Open
elibosley wants to merge 5 commits into
masterfrom
fix/plugins-page-nonblocking-update-check
Open

fix(plugin-manager): parallel, non-blocking Plugins page update check#2667
elibosley wants to merge 5 commits into
masterfrom
fix/plugins-page-nonblocking-update-check

Conversation

@elibosley

@elibosley elibosley commented Jun 18, 2026

Copy link
Copy Markdown
Member

Problem

The Installed Plugins page (Tools > Plugins) can sit with every row spinning checking... indefinitely, which makes the page look hung.

Root cause

The page loads in two passes:

  • Pass 1: ShowPlugins.php?init=true renders the table from local .plg data. Fast.
  • Pass 2: the old all-plugin check path walked every plugin serially and spawned the plugin script to wget each plugin's pluginURL.

One slow or unreachable plugin URL blocked the whole pass, so every row stayed at checking... until the slowest plugin resolved.

Fix

  • ShowPlugins.php: add single-plugin mode with ?one=<name> so one request checks one plugin and returns just that row's update/status fragment.
  • Plugins.page: replace the automatic post-init sweep with bounded concurrency fan-out, one ?one= request per installed plugin, each with a 20s AJAX timeout.
  • Plugins.page: route manual Check For Updates through the same row-check flow. It resets rows to checking..., disables the button while active, and re-enables when all row checks complete.
  • Fail-safe: timeout/error resolves only that row to cannot check; the page is never gated on one plugin host.
  • scripts/plugin: harden validation downloads by using a per-process temp file, reporting zero-length downloads as explicit errors, and clearing PHP's stat cache before filesize().

A slow/unreachable plugin now affects only its own row, bounded by one timeout. Automatic and manual checks use the same visible row behavior.

Scope / follow-up

  • Phase 2 in OS-457, a single-parse plugin_attributes() path to reduce remaining per-plugin exec spawns for changes / alert, remains a follow-up.
  • External plugin asset hosts can still fail or throttle downloads. Those should now surface as download errors or per-row cannot check, not as false hash/blank temp-file races.

Testing

  • php -l clean on Plugins.page and scripts/plugin.
  • Deployed to devgen.local with backups at /tmp/Plugins.page.before-manual-row-check.1783609619 and /tmp/plugin.before-manual-row-check.1783609619.
  • Verified devgen syntax for both files.
  • Verified manual button now renders onclick='checkPlugins(true)' instead of openPlugin("checkall", ...).
  • Verified serial Apprise validation on devgen returns valid, rc=0, and leaves no /tmp/validate-plugin-* files behind.

Ref: OS-457 · prior art: #2352 (closed), #2421 (merged)

@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 35 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 007644b8-02b0-4a87-8d62-6787f2fa1041

📥 Commits

Reviewing files that changed from the base of the PR and between ffb286f and 5851f14.

📒 Files selected for processing (2)
  • emhttp/plugins/dynamix.plugin.manager/Plugins.page
  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin

Walkthrough

The PR changes plugin checking to run one scoped request per installed plugin with bounded concurrency, updating each row independently. It also adjusts plugin download and validation handling to report zero-length downloads and use unique temporary files.

Changes

Per-plugin parallel update check

Layer / File(s) Summary
ShowPlugins.php single-plugin scope
emhttp/plugins/dynamix.plugin.manager/include/ShowPlugins.php
Parses $_GET['one'] and rewrites the plugin scan target to a single .plg file when set.
checkPlugin / checkPlugins orchestration
emhttp/plugins/dynamix.plugin.manager/Plugins.page
Adds pluginUpdates, checkPlugin(plg), and checkPlugins(); the page now fans out per-plugin AJAX checks, updates rows individually, refreshes the table cache in place, and re-enables #checkall after completion.
Download and validation error handling
emhttp/plugins/dynamix.plugin.manager/scripts/plugin
Returns a dedicated zero-length download error and switches validation temp storage to tempnam() with an early failure path.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Poem

🐇 I hop through plugins, one by one,
Six at a time, the checks get done.
A zero-length file gets its own small cheer,
And temp files now vanish from fear.
The table updates as rows appear,
Hoppy, tidy, and crystal clear.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR speeds update checks, but #2352 is about plugin result caching and cache invalidation, which are not implemented here. Add the caching, bulk attribute retrieval, and cache-clearing behavior from #2352, or update the linked issue to match this work.
✅ Passed checks (4 passed)
Check name Status Explanation
Out of Scope Changes check ✅ Passed The extra script changes support concurrent plugin requests and validation, so no unrelated feature appears to be added.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: parallel, non-blocking update checks for the Plugins page.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/plugins-page-nonblocking-update-check

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown

🔧 PR Test Plugin Available

A test plugin has been generated for this PR that includes the modified files.

Version: 2026.07.09.1507
Build: View Workflow Run

📥 Installation Instructions:

Install via Unraid Web UI:

  1. Go to Plugins → Install Plugin
  2. Copy and paste this URL:
https://preview.dl.unraid.net/pr-plugins/pr-2667/webgui-pr-2667.plg
  1. Click Install

Alternative: Direct Download

⚠️ Important Notes:

  • Testing only: This plugin is for testing PR changes
  • Backup included: Original files are automatically backed up
  • Easy removal: Files are restored when plugin is removed
  • Conflicts: Remove this plugin before installing production updates
  • Post-merge behavior: This preview stays available after merge until preview storage expires or it is manually cleaned up

📝 Modified Files:

Click to expand file list
emhttp/plugins/dynamix.plugin.manager/Plugins.page
emhttp/plugins/dynamix.plugin.manager/include/ShowPlugins.php
emhttp/plugins/dynamix.plugin.manager/scripts/plugin

🔄 To Remove:

Navigate to Plugins → Installed Plugins and remove webgui-pr-2667, or run:

plugin remove webgui-pr-2667

🤖 This comment is automatically generated and will be updated with each new push to this PR.

elibosley and others added 2 commits June 19, 2026 10:37
…blocking

The Installed Plugins page renders the list quickly from local .plg data,
but the per-plugin update check ran as a single serial request:
ShowPlugins.php looped every plugin and, for each, spawned the plugin
script to wget that plugin's pluginURL. The request returned all-or-nothing,
so one slow or unreachable plugin URL left every row stuck spinning
'checking...' — the page appears hung.

Take the network check off the critical path and parallelize it:
- ShowPlugins.php: add a single-plugin mode (?one=<name>) that restricts the
  glob to one plugin and returns just its vid-/sid- line.
- Plugins.page: replace the batched loadlist() post-init sweep with a
  bounded-concurrency (6) fan-out, one ?one= request per installed plugin,
  each with a 20s AJAX timeout. Rows resolve independently; the 'Update All'
  count is aggregated client-side.
- Fail-safe: on timeout/error a row resolves to 'cannot check' instead of
  spinning forever. The page is never gated on update checks.

A slow/unreachable plugin now only affects its own row, bounded by one
timeout, never the whole page. No new caching introduced.

Ref: OS-457

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
class 'remove' is on both the checkbox and the button of each plugin row,
so the fan-out queued every plugin twice. Dedupe by data attribute so each
plugin is checked once.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@elibosley elibosley force-pushed the fix/plugins-page-nonblocking-update-check branch from 23b4b19 to b3a1b4b Compare June 19, 2026 14:37
elibosley and others added 2 commits June 22, 2026 16:39
The Plugins page tablesorter sorts on the Status column first
(sortList [[4,0],[1,0]]). Each per-plugin check rewrites that cell's
data rank, and the post-check trigger('update') re-applied the sort,
so rows jumped around as results landed one by one.

Use trigger('updateCache') instead: it refreshes the parsed cache (so a
later header-click sort still uses current status) without re-sorting,
so rows fill in place and the list no longer reorders while checking.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
emhttp/plugins/dynamix.plugin.manager/scripts/plugin (1)

334-352: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Clear the stat cache before reusing the validation tempfile
download() reuses the same $name across loop iterations, and its filesize($name) zero-length check can read stale metadata after wget overwrites that path. Add clearstatcache(true, $name) before that check (or right after each download) so a previous iteration doesn’t mask a fresh file state.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@emhttp/plugins/dynamix.plugin.manager/scripts/plugin` around lines 334 - 352,
The validation flow in the plugin manager reuses the same temp file name across
multiple download attempts, so stale stat metadata can make the filesize check
in download() misread a freshly overwritten file. Update the validation path
around the temp file reuse in the plugin validation loop and the download()
zero-length check to clear the stat cache for that path before checking
filesize, so each iteration sees the current file state.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@emhttp/plugins/dynamix.plugin.manager/scripts/plugin`:
- Around line 334-352: The validation flow in the plugin manager reuses the same
temp file name across multiple download attempts, so stale stat metadata can
make the filesize check in download() misread a freshly overwritten file. Update
the validation path around the temp file reuse in the plugin validation loop and
the download() zero-length check to clear the stat cache for that path before
checking filesize, so each iteration sees the current file state.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: af951c46-743a-455d-82b0-a8af82a9d987

📥 Commits

Reviewing files that changed from the base of the PR and between 23b4b19 and ffb286f.

📒 Files selected for processing (3)
  • emhttp/plugins/dynamix.plugin.manager/Plugins.page
  • emhttp/plugins/dynamix.plugin.manager/include/ShowPlugins.php
  • emhttp/plugins/dynamix.plugin.manager/scripts/plugin
🚧 Files skipped from review as they are similar to previous changes (2)
  • emhttp/plugins/dynamix.plugin.manager/include/ShowPlugins.php
  • emhttp/plugins/dynamix.plugin.manager/Plugins.page

@unraid-bot unraid-bot added the 7.4.0 Approved for release 7.4.0 (auto-managed by notification-worker) label Jul 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

7.4.0 Approved for release 7.4.0 (auto-managed by notification-worker)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants