Skip to content

fix(linstor): retry drbdadm secondary after mkfs on transient EBUSY - #2538

Merged
Andrei Kvapil (kvaps) merged 1 commit into
cozystack:mainfrom
IvanHunters:fix/linstor-retry-secondary-after-mkfs
Apr 30, 2026
Merged

Andrei Kvapil (kvaps) merged 1 commit into
cozystack:mainfrom
IvanHunters:fix/linstor-retry-secondary-after-mkfs

Conversation

@IvanHunters

@IvanHunters IvanHunters commented Apr 29, 2026 •

Copy link
Copy Markdown
Collaborator

What this PR does

Adds a fifth piraeus-server patch that retries drbdadm secondary after mkfs when DRBD reports the device as held open by an external process.

Problem

On Talos (and any system with active block-device probing — udev, multipathd, etc.) the kernel uevent for a freshly-promoted DRBD device causes an external probe to open() the device for superblock inspection. If LINSTOR satellite issues drbdadm secondary while the probe still holds the fd, DRBD returns (-12) Device is held open by someone and aborts initialization with StorageException: Failed to become secondary again after creating filesystem.

The satellite never reports the final UpToDate event to the controller, so the controller view stays in SyncTarget(N%) / Inconsistent / DELETING permanently — even though the DRBD kernel state on the satellite is Established/UpToDate. This blocks cleanup of orphan PVs in Released phase and accumulates with every PVC creation.

Observed in production on Talos 1.10 / LINSTOR 1.33.2: 4 occurrences in 24 hours across 3 nodes for different PVCs. Manual recovery requires drbdadm down/up on the Primary to force the satellite to re-report state.

Fix

New secondaryAfterMkfs() wrapper in DrbdAdm.java:

  • 7 attempts of drbdadm secondary with exponential backoff (250 ms → 2 s, capped)
  • Retry only triggers when stderr contains Device is held open by someone; any other failure fails fast as before
  • Existing secondary() contract is unchanged — only DrbdPrimary.close() (the post-mkfs demotion path) is rerouted to the new wrapper

The retry window covers ~10 s in the worst case, which comfortably exceeds typical probe durations (probes release the fd within milliseconds).

Related upstream

Validation

  • git apply *.diff on a fresh v1.33.2 checkout applies all five patches cleanly in alphabetical order
  • Java brace/paren/bracket balance verified on the patched file
  • helm template packages/system/linstor renders without errors

Release note

fix(linstor): retry `drbdadm secondary` after mkfs on transient `Device is held open by someone` errors. Prevents orphan PVs in `Released` phase from accumulating when an external block-device probe (Talos block-controller, udev, multipathd) wins a race against the post-mkfs demotion. Fail-fast behavior preserved for any other secondary failure.

Summary by CodeRabbit

  • New Features

    • Implemented automatic retry logic for device secondary state transitions after filesystem creation. System now gracefully handles scenarios where devices are temporarily held open by other processes.
  • Documentation

    • Added documentation for the new device state transition patch.

LINSTOR satellite aborts resource initialization with "Device is held open
by someone" (errno -12) when an external probe (Talos block-controller,
udev, multipathd) opens the freshly-promoted DRBD device just before the
post-mkfs demotion. The controller view then stays in SyncTarget /
Inconsistent / DELETING indefinitely, blocking cleanup of orphan PVs in
Released phase.

Add a retry wrapper secondaryAfterMkfs() with exponential backoff
(7 attempts, 250ms -> 2s) that triggers only on this specific error
message. DrbdPrimary.close() now uses the wrapper so the retry only
applies to the post-mkfs demotion path; the existing public secondary()
contract is untouched.

Related: LINBIT/linstor-server#268, LINBIT/drbd#74
Signed-off-by: IvanHunters <[email protected]>
@coderabbitai

coderabbitai Bot commented Apr 29, 2026 •

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 77725ce3-ef27-42da-a05b-881b2d08aa48

📥 Commits

Reviewing files that changed from the base of the PR and between f45facc and 90fbb16.

📒 Files selected for processing (2)
  • packages/system/linstor/images/piraeus-server/patches/README.md
  • packages/system/linstor/images/piraeus-server/patches/retry-secondary-after-mkfs.diff

📝 Walkthrough

Walkthrough

Adds a patch for piraeus-server that implements retry logic with exponential backoff for drbdadm secondary commands issued after mkfs, specifically handling scenarios where the device is held open by another process. Two new public methods are introduced to DrbdAdm, and the DrbdPrimary.close() implementation is updated to use the new retry mechanism.

Changes

Cohort / File(s) Summary
Documentation
packages/system/linstor/images/piraeus-server/patches/README.md
Documents the new retry-secondary-after-mkfs.diff patch with description of updated retry behavior and upstream issue links.
DRBD Patch Implementation
packages/system/linstor/images/piraeus-server/patches/retry-secondary-after-mkfs.diff
Adds secondaryAfterMkfs() method with exponential backoff retry logic and isDeviceHeldOpenError() error detection helper. Modifies DrbdPrimary.close() to invoke the new retrying secondary method instead of direct secondary call.

Sequence Diagram

sequenceDiagram
    actor Client as Client Code
    participant DrbdAdm as DrbdAdm Service
    participant Cmd as drbdadm Command
    participant Device as Device/FS

    Client->>DrbdAdm: secondaryAfterMkfs(drbdRscData)
    loop Retry with Exponential Backoff
        DrbdAdm->>Cmd: execute drbdadm secondary
        Cmd->>Device: attempt state change
        alt Device Available
            Device-->>Cmd: success
            Cmd-->>DrbdAdm: return 0
            DrbdAdm-->>Client: success
        else Device Held Open
            Device-->>Cmd: error (device held)
            Cmd-->>DrbdAdm: stderr with error message
            DrbdAdm->>DrbdAdm: detect device held open error
            DrbdAdm->>DrbdAdm: wait(exponential backoff)
            Note over DrbdAdm: Retry or fail after limit
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 A secondary waits with patience true,
When devices hold fast and won't break through—
Exponential backoff, a gentle dance,
Retrying, persisting, second and third chance!
For DRBD's heart beats steady and strong,
With clever backoff to right what went wrong. 🔄

🚥 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 accurately summarizes the main change: adding retry logic for drbdadm secondary after mkfs when encountering transient EBUSY errors.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

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

@github-actions github-actions Bot added area/storage Issues or PRs related to storage (linstor, seaweedfs, bucket, velero, harbor) kind/bug Categorizes issue or PR as related to a bug labels Apr 29, 2026
@IvanHunters
IvanHunters marked this pull request as ready for review April 29, 2026 15:57

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@dosubot dosubot Bot added the size/L This PR changes 100-499 lines, ignoring generated files label Apr 29, 2026
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a race condition in LINSTOR where DRBD device initialization fails because external system probes hold a file descriptor open during the post-mkfs demotion process. By implementing a targeted retry strategy with exponential backoff, the change ensures that transient 'EBUSY' errors do not prevent the satellite from reporting the correct state to the controller, thereby preventing the accumulation of orphan persistent volumes.

Highlights

  • DRBD Initialization Reliability: Introduced a retry mechanism for 'drbdadm secondary' commands that fail due to transient 'Device is held open by someone' errors, which commonly occur during post-mkfs operations on systems with active block-device probing.
  • Exponential Backoff Implementation: Added a new wrapper with exponential backoff (250ms to 2s) to handle race conditions between LINSTOR and external probes like udev or multipathd.
  • Documentation Update: Updated the patches README to include the new 'retry-secondary-after-mkfs.diff' and provide context on the related upstream issues.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Ignored Files
  • Ignored by pattern: **/*.diff (1)
    • packages/system/linstor/images/piraeus-server/patches/retry-secondary-after-mkfs.diff
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment Gemini (@gemini-code-assist) Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on Gemini (@gemini-code-assist) comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request updates the documentation for piraeus-server patches by adding a description for a new patch, retry-secondary-after-mkfs.diff, which addresses resource initialization failures caused by external probes. However, the actual patch file referenced in the README is missing from the pull request and must be added to the patches directory.

- **retry-adjust-after-stale-bitmap.diff** — Retry `drbdadm adjust` after detaching a stale local bitmap state
- Source PR: [#491](https://github.com/LINBIT/linstor-server/pull/491)
- Backported from commit: [`51ae50a84`](https://github.com/kvaps/linstor-server/commit/51ae50a84dcb98093f543b819652c750a94d96c9)
- **retry-secondary-after-mkfs.diff** — Retry `drbdadm secondary` after mkfs when DRBD reports the device as held open by an external probe (Talos block-controller, udev, multipathd, etc.). Without this retry, a transient `Device is held open by someone` aborts resource initialization, leaves the satellite in an intermediate state, and prevents the controller from receiving the final `UpToDate` event — orphan PVs in `Released` then cannot be cleaned up.

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.

critical

The pull request description mentions adding a fifth patch file named retry-secondary-after-mkfs.diff, and this README update references it. However, the actual .diff file is missing from the changes in this pull request. Please ensure the patch file is added to the packages/system/linstor/images/piraeus-server/patches/ directory so it can be applied during the image build.

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.

Patch is well-scoped: rerouting only DrbdPrimary.close() to a new wrapper keeps the existing secondary() contract intact, fail-fast behavior is preserved for any non-EBUSY error, and the retry window (~7.75s of cumulative sleep) is bounded. Stacking onto retry-adjust-after-stale-bitmap.diff works correctly — line numbers and required imports (StandardCharsets, File, Platform, ProcessBuilder.Redirect, IOException, ChildProcessTimeoutException) line up. README entry follows the established format and links the relevant upstream issues.

LGTM.

@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Apr 29, 2026

@kvaps Andrei Kvapil (kvaps) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM — patch is minimal and well-scoped.

  • Existing secondary() contract is preserved; only DrbdPrimary.close() (the post-mkfs demotion path) is rerouted to the retrying wrapper.
  • isDeviceHeldOpenError() gates the retry; any other failure throws fail-fast as before.
  • Backoff caps at 2 s, total worst case ≈ 7.75 s of sleep, comfortably above typical probe windows.
  • InterruptedException correctly restores the interrupt flag.

Holding off on merge until E2E goes green.

@kvaps
Andrei Kvapil (kvaps) merged commit 6c2dba9 into cozystack:main Apr 30, 2026
14 checks passed
@myasnikovdaniil myasnikovdaniil added the kind/backport Categorizes issue or PR as requiring a backport to the current release line label May 6, 2026
@github-actions

github-actions Bot commented May 6, 2026

Copy link
Copy Markdown

Successfully created backport PR for release-1.3:

myasnikovdaniil added a commit that referenced this pull request May 6, 2026
…fs on transient EBUSY (#2579)

# Description
Backport of #2538 to `release-1.3`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/storage Issues or PRs related to storage (linstor, seaweedfs, bucket, velero, harbor) kind/backport Categorizes issue or PR as requiring a backport to the current release line kind/bug Categorizes issue or PR as related to a bug lgtm This PR has been approved by a maintainer size/L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants