Skip to content

fix(downgrade): block during protected array shrink#2698

Open
elibosley wants to merge 1 commit into
masterfrom
codex/parity-protected-shrink-downgrade-guard
Open

fix(downgrade): block during protected array shrink#2698
elibosley wants to merge 1 commit into
masterfrom
codex/parity-protected-shrink-downgrade-guard

Conversation

@elibosley

@elibosley elibosley commented Jul 16, 2026

Copy link
Copy Markdown
Member

Summary

An OS downgrade can strand a parity-protected array shrink on a release that cannot recover it; this adds an exclusive downgrade marker and blocks downgrade staging while any Core intent or daemon proof exists.

Why This Exists

The protected shrink protocol spans Core and emhttpd and remains recoverable across host restarts. Downgrading either side during that transaction could remove the code that understands the durable recovery records, so downgrade must participate in the same fail-closed transaction boundary.

Resolution

Downgrade.php now creates an exclusive runtime marker before it inspects persistent state. Core and emhttpd reject protected-shrink work while the marker exists. If the production Core intent or any daemon proof is present, WebGUI removes the marker and returns HTTP 409 without moving boot artifacts.

Reviewer Considerations

  • The Core intent path is intentionally /boot/config/unraid/storage/parity-protected-shrink.json; daemon proofs remain under /boot/config/storage.
  • Marker-first ordering closes the race with new work. emhttpd creates its preparing proof before destructive writes and rechecks the marker after that proof is durable.
  • Rollout order is unraidd PR https://github.com/unraid-internal/unraidd/pull/3, then this guard, then Core PR https://github.com/unraid/core/pull/290.
  • Reverse rollback is safe only after the Core intent and every daemon proof are absent.

Behavior Changes

  • A second downgrade request returns HTTP 409 while one is pending.
  • Downgrade returns HTTP 409 while a protected data-disk removal is active or recoverable.
  • Successful downgrade staging leaves the transient marker until reboot, preventing new protected work.

Implementation Summary

  • Add the exclusive /var/run/unraid-os-downgrade marker.
  • Centralize the production intent and proof path contract.
  • Add an executable PHP contract test for exact paths and active-file detection.

Verification

  • PHP syntax checks passed for Downgrade.php, the helper, and the test.
  • parity_protected_shrink_guard_test.php passed.
  • git diff --check passed.
  • Independent cross-repository race and rollout review passed with no blocker.

Risk

Low code volume but safety-critical ordering. The marker is transient and fail-closed; deployment must follow the documented daemon-first sequence.

Summary by CodeRabbit

  • Bug Fixes

    • Prevented concurrent or overlapping system downgrades.
    • Downgrades are now blocked while a parity-protected shrink operation is active.
    • Added clearer conflict handling when downgrade safeguards are triggered.
  • Tests

    • Added coverage for detecting active parity-protected shrink operations and verifying safe downgrade behavior.

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The downgrade flow now prevents overlapping downgrade requests and blocks downgrades while parity-protected shrink activity is detected. A reusable guard module constructs and checks proof-file paths, with tests covering idle and active states.

Changes

Downgrade protection

Layer / File(s) Summary
Parity-protected shrink guard
emhttp/plugins/dynamix.plugin.manager/include/ParityProtectedShrinkGuard.php, emhttp/plugins/dynamix.plugin.manager/tests/parity_protected_shrink_guard_test.php
Adds filesystem path construction and active-state detection, with tests for idle and proof-file-present states.
Downgrade runtime gating
emhttp/plugins/dynamix.plugin.manager/include/Downgrade.php
Creates an exclusive downgrade marker and returns HTTP 409 for concurrent downgrades or active parity-protected shrink operations.

Estimated code review effort: 3 (Moderate) | ~15–30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Downgrade
  participant Marker
  participant ShrinkGuard
  Downgrade->>Marker: create exclusive marker
  Marker-->>Downgrade: allow or report existing marker
  Downgrade->>ShrinkGuard: check protected shrink files
  ShrinkGuard-->>Downgrade: report active or idle state
Loading

Poem

A rabbit checks the boot-time trail,
While markers guard the downgrade rail.
Proof files whisper, “Wait your turn,”
And busy paths now safely learn.
With tests that hop from green to gray,
The downgrade finds a safer way.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: blocking downgrades during protected array shrink.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/parity-protected-shrink-downgrade-guard

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

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.16.0253
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-2698/webgui-pr-2698.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/include/Downgrade.php
emhttp/plugins/dynamix.plugin.manager/include/ParityProtectedShrinkGuard.php
emhttp/plugins/dynamix.plugin.manager/tests/parity_protected_shrink_guard_test.php

🔄 To Remove:

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

plugin remove webgui-pr-2698

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

@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.

🧹 Nitpick comments (1)
emhttp/plugins/dynamix.plugin.manager/tests/parity_protected_shrink_guard_test.php (1)

28-28: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use double quotes for newline escape sequences.

The string 'stage=prepared\n' uses single quotes, which means \n is interpreted as literal characters \ and n rather than a newline character. While this doesn't impact the is_file() check in this test, it's best to use double quotes if a true newline was intended.

🧽 Proposed nitpick
-file_put_contents($testFiles[2], 'stage=prepared\n');
+file_put_contents($testFiles[2], "stage=prepared\n");
🤖 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/tests/parity_protected_shrink_guard_test.php`
at line 28, Update the file_put_contents call in
parity_protected_shrink_guard_test to use a double-quoted string so \n is
written as an actual newline, preserving the existing content and test behavior.
🤖 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.

Nitpick comments:
In
`@emhttp/plugins/dynamix.plugin.manager/tests/parity_protected_shrink_guard_test.php`:
- Line 28: Update the file_put_contents call in
parity_protected_shrink_guard_test to use a double-quoted string so \n is
written as an actual newline, preserving the existing content and test behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8e288f21-1229-4663-b6f8-e20811c64a73

📥 Commits

Reviewing files that changed from the base of the PR and between 369f0b2 and ff17ec2.

📒 Files selected for processing (3)
  • emhttp/plugins/dynamix.plugin.manager/include/Downgrade.php
  • emhttp/plugins/dynamix.plugin.manager/include/ParityProtectedShrinkGuard.php
  • emhttp/plugins/dynamix.plugin.manager/tests/parity_protected_shrink_guard_test.php

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.

1 participant