Skip to content

feat(plugin): serialize lifecycle transitions and preserve reload failure state #86

Description

@fronzec

Pre-flight Checks

  • I searched open and closed issues and found no true duplicate.
  • I understand implementation requires maintainer approval before a PR is opened.

Problem Description

Plugin lifecycle operations are not yet serialized end to end for a single definition. Concurrent load, unload, and reload transitions can interleave, and reload failure paths do not yet provide explicit transactional semantics for preserving the previous runtime or persisting a recoverable failure after teardown has completed.

Proposed Solution

Implement PR-C slice 2 using the reclaimable per-definition lifecycle lock introduced by PR #85:

  • Use one per-definition lifecycle lock across load, unload, and reload.
  • Keep the same lock for the entire reload operation, including forced unload and replacement load.
  • If unload fails, abort replacement loading and preserve the previous runtime and persisted state.
  • If unload completes but replacement loading fails, persist FAILED and clean classloader/runtime ownership.
  • Allow a normal explicit load to recover a definition from FAILED.
  • Add focused, deterministic concurrency and failure-path tests for these guarantees.

Acceptance Criteria

  • Load, unload, and reload for the same definition cannot overlap; independent definitions remain able to progress independently.
  • Reload retains one per-definition lock from entry through forced unload and replacement load completion or failure.
  • An unload failure prevents replacement loading and leaves the prior runtime ownership and persisted lifecycle state intact.
  • After a completed unload, a replacement-load failure persists FAILED and leaves no stale classloader or runtime ownership.
  • A later normal explicit load can transition a FAILED definition through the existing load path and restore a valid loaded state.
  • Lock references are released on every success and failure path so idle holders remain reclaimable.

Test Expectations

  • Deterministic concurrency tests prove same-definition load, unload, and reload operations are serialized without timing-based sleeps.
  • A reload test proves the lock remains held across forced unload and replacement load.
  • Failure tests prove unload failure aborts replacement and preserves the previous runtime/state.
  • Failure tests prove completed unload plus replacement failure persists FAILED and cleans ownership.
  • Recovery tests prove an explicit load recovers FAILED.
  • Existing plugin lifecycle tests continue to pass.

Affected Area

Plugin engine lifecycle coordination

Out of Scope

  • DELETE safety.
  • Distributed locking.
  • A repair API.
  • The initial LOADING persistence follow-up.

Alternatives Considered

Using separate lock scopes for unload and replacement load was rejected because another lifecycle operation could interleave during reload. Distributed coordination was rejected because this slice addresses in-process lifecycle integrity only. Adding repair or DELETE behavior here was rejected to keep the stacked review boundary focused.

Additional Context

This is stacked PR 2 on PR #85. It consumes the lifecycle-lock and ownership infrastructure from that PR and intentionally limits this issue to end-to-end load/unload/reload transition integrity.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requeststatus:approvedApproved by a maintainer and ready for implementation

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions