Skip to content

Record spinlock holder identity for datafile/journal deadlock fatals - #22725

Merged
stelfrag merged 2 commits into
netdata:masterfrom
stelfrag:dbengine-spinlock-holder-identity
Jun 16, 2026
Merged

Record spinlock holder identity for datafile/journal deadlock fatals#22725
stelfrag merged 2 commits into
netdata:masterfrom
stelfrag:dbengine-spinlock-holder-identity

Conversation

@stelfrag

@stelfrag stelfrag commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator
Summary
  • Record additional information to better track deadlock detection and holder identification.
  • Updated accompanying locking logic and introduced tracked spinlock helpers in spinlock.h.

Summary by cubic

Add holder-aware spinlocks to improve deadlock diagnostics in the DB engine. Datafile and journal locks now report the blocking thread (tid, function, held-for), and tracked info persists across unlock; works under the mutex-backed build too.

  • New Features

    • Added SPINLOCK_TRACKED with holder tracking and spinlock_tracked_* APIs; holder fields persist after unlock for accurate reporting.
    • Deadlock detector now names the holder and hold duration; shared lock core keeps plain SPINLOCK cost unchanged.
    • Added a mutex-backed SPINLOCK_TRACKED so holder recording works when SPINLOCK_IMPL_WITH_MUTEX is enabled.
  • Refactors

    • Switched datafile users.spinlock and journal data_spinlock to SPINLOCK_TRACKED, updated call sites in datafile.c, journalfile.c, and rrdengine.c, and fixed the spinlock_init macro typo in spinlock.h.
    • No change to normal behavior; only clearer diagnostics for long waits.

Written for commit 59055de. Summary will update on new commits.

Review in cubic

Replaced SPINLOCK with SPINLOCK_TRACKED in critical database and journalfile code paths to enable deadlock detection and holder identification. Updated accompanying locking logic and introduced tracked spinlock helpers in `spinlock.h`.
@stelfrag stelfrag changed the title ρrecord spinlock holder identity for datafile/journal deadlock fatals Record spinlock holder identity for datafile/journal deadlock fatals Jun 15, 2026

@cubic-dev-ai cubic-dev-ai 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.

No issues found across 7 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.
Architecture diagram
sequenceDiagram
    participant DC as Deadlock Checker (spinlock_core)
    participant ST as SPINLOCK_TRACKED struct
    participant DF as Datafile (users.spinlock)
    participant JF as Journalfile (data_spinlock)
    participant FL as Flush Thread (rrdengine.c)

    Note over DC,FL: Tracked spinlock flow on contention

    DF->>ST: spinlock_tracked_lock()
    activate ST
    ST->>DC: spinlock_lock_core (tracked=non-NULL)
    activate DC

    loop Spin-acquire loop
        DC->>DC: Test __atomic_try_lock
        alt Lock acquired
            DC->>ST: spinlock_tracked_record_holder()
            ST->>ST: __atomic_store holder_tid, holder_func, holder_since_ut
            DC-->>DF: lock owned
            DF->>DF: (critical section: acquire/release datafile refs)
            DF->>ST: spinlock_tracked_unlock()
            ST->>ST: __atomic_clear holder_tid
            ST->>DC: spinlock_unlock (plain)
            deactivate DC
            deactivate ST
        else Lock busy (contention)
            DC->>DC: spins++
            alt Every SPINS_BEFORE_DEADLOCK_CHECK iteration
                DC->>ST: spinlock_tracked_deadlock_detect()
                activate ST
                alt Timeout exceeded (3600s)
                    DC->>ST: __atomic_load holder_tid, holder_func, holder_since_ut
                    alt holder_tid != 0
                        ST-->>DC: holder identity known
                        DC->>DC: fatal("... holder: tid=%d func='%s' held_for=%"PRIi64"s ...")
                    else holder_tid == 0 (stuck/corrupted)
                        ST-->>DC: no holder recorded
                        DC->>DC: fatal("... holder: NONE - possible corruption/stuck byte ...")
                    end
                else No timeout yet
                    ST-->>DC: continue waiting
                end
                deactivate ST
            end
            DC->>DC: microsleep (exponential backoff)
        end
    end

    Note over JF,DC: Journalfile parallel example (same pattern)

    JF->>ST: spinlock_tracked_lock()
    ST->>DC: spinlock_lock_core (tracked=non-NULL)
    activate DC
    DC-->>JF: lock owned
    JF->>JF: (critical section: mount/unmount v2 data, update metrics)
    JF->>ST: spinlock_tracked_unlock()
    deactivate DC

    Note over FL,DC: Flush-thread update via tracked lock

    FL->>ST: spinlock_tracked_lock()
    ST->>DC: spinlock_lock_core (tracked=non-NULL)
    activate DC
    DC-->>FL: lock owned
    FL->>FL: update last_time_s on journalfile
    FL->>ST: spinlock_tracked_unlock()
    deactivate DC

    Note over ST: Plain SPINLOCK path via spinlock_lock_with_trace<br/>passes tracked=NULL, constant-folded away<br/>— no overhead, no holder stores
Loading

Re-trigger cubic

@stelfrag
stelfrag marked this pull request as ready for review June 15, 2026 13:28
- Updated SPINLOCK_TRACKED to retain holder fields after unlock for consistent deadlock diagnostics.
- Added mutex-backed SPINLOCK_TRACKED implementation to align functionality across spinlock types.
- Refactored and clarified comments to explain holder field handling and deadlock detection behavior.
@sonarqubecloud

Copy link
Copy Markdown

@stelfrag

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review PR

@cubic-dev-ai

cubic-dev-ai Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review PR

@stelfrag I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai 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.

No issues found across 7 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.
Architecture diagram
sequenceDiagram
    participant DF as datafile.c (users.*)
    participant JF as journalfile.c (data_spinlock)
    participant RE as rrdengine.c (extent_flush)
    participant SL as SPINLOCK (plain)
    participant ST as SPINLOCK_TRACKED
    participant DET as Deadlock Detector

    Note over DF,RE: NEW: SPINLOCK_TRACKED replaces SPINLOCK for these locks

    DF->>ST: spinlock_tracked_lock(&df->users.spinlock)
    ST->>SL: spinlock_lock_core(spinlock, func, tracked)
    alt Lock contested (spinning)
        loop Exponential backoff + deadlock check
            ST->>ST: spins++
            alt spins % SPINS_BEFORE_DEADLOCK_CHECK == 0
                ST->>DET: spinlock_tracked_deadlock_detect()
                DET->>DET: Read holder_tid, holder_func, holder_since_ut
                alt held >= SPINLOCK_DEADLOCK_TIMEOUT_SEC
                    DET-->>ST: fatal("DEADLOCK DETECTED [holder: tid=N func='...' held_for=N]")
                end
            end
            ST->>ST: microsleep(usec), usec *= 2
        end
    end
    ST->>ST: Acquired: __atomic_add_fetch(&locked, 1)
    ST->>ST: spinlock_tracked_record_holder()
    ST-->>DF: lock acquired

    DF->>DF: Critical section (check users.available, lockers, etc.)

    DF->>ST: spinlock_tracked_unlock(&df->users.spinlock)
    ST->>SL: spinlock_unlock(spinlock)
    Note over ST: Holder fields NOT cleared (advisory - overwritten by next acquirer)
    ST-->>DF: lock released

    Note over DF,RE: Same pattern for all call sites in datafile.c/journalfile.c/rrdengine.c

    JF->>ST: spinlock_tracked_lock(&journalfile->data_spinlock)
    ST->>SL: spinlock_lock_core(spinlock, func, tracked)
    alt Contested
        ST->>DET: spinlock_tracked_deadlock_detect()
        DET-->>ST: Holder identity in fatal
    end
    ST->>ST: Record holder
    ST-->>JF: lock acquired

    JF->>JF: Critical section (check JOURNALFILE flags, refcount, mmap)

    JF->>ST: spinlock_tracked_unlock(&journalfile->data_spinlock)
    ST-->>JF: lock released

    RE->>ST: spinlock_tracked_lock(&datafile->journalfile->data_spinlock)
    RE->>RE: Update last_time_s
    RE->>ST: spinlock_tracked_unlock(&datafile->journalfile->data_spinlock)

    alt SPINLOCK_IMPL_WITH_MUTEX build
        Note over ST: Mutex-backed implementation (no spin loop)
        ST->>SL: spinlock_lock(&spinlock->spinlock) (calls netdata_mutex_lock)
        ST->>ST: spinlock_tracked_record_holder()
        ST-->>DF: lock acquired
        DF->>ST: spinlock_tracked_unlock()
        ST->>SL: spinlock_unlock(&spinlock->spinlock) (calls netdata_mutex_unlock)
        Note over ST: holder fields recorded but no deadlock detection
    end
Loading

Re-trigger cubic

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

No issues found during runtime after hours running. LGTM!

@stelfrag
stelfrag merged commit 0daffba into netdata:master Jun 16, 2026
284 of 289 checks passed
@stelfrag
stelfrag deleted the dbengine-spinlock-holder-identity branch June 16, 2026 07:13
@stelfrag stelfrag mentioned this pull request Jun 22, 2026
Ferroin pushed a commit that referenced this pull request Jul 15, 2026
…22725)

* Switch to SPINLOCK_TRACKED for enhanced deadlock diagnostics

Replaced SPINLOCK with SPINLOCK_TRACKED in critical database and journalfile code paths to enable deadlock detection and holder identification. Updated accompanying locking logic and introduced tracked spinlock helpers in `spinlock.h`.

* Improve SPINLOCK_TRACKED behavior and add mutex-backed implementation

- Updated SPINLOCK_TRACKED to retain holder fields after unlock for consistent deadlock diagnostics.
- Added mutex-backed SPINLOCK_TRACKED implementation to align functionality across spinlock types.
- Refactored and clarified comments to explain holder field handling and deadlock detection behavior.

(cherry picked from commit 0daffba)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants