Member Junction
    Preparing search index...

    Per-run ownership manager for durable CompanyIntegrationRun execution (GH tasks.md PR 1). One instance is created per sync run and owns:

    • Claim: a single atomic UPDATE...WHERE (unowned OR lease expired) via spClaimCompanyIntegrationRun — never select-then-update. Claiming bumps FenceToken, invalidating any prior holder's writes.
    • Renew: timer-driven lease extension (interval ≈ lease/3) via spRenewCompanyIntegrationRunLease, token+fence-checked. The renewal result doubles as the cross-process cancel signal (returns CancelRequestedAt).
    • Boundary fence: CheckBoundary — a cheap SELECT the sync loop calls before every batch's writes. Ownership lost ⇒ the loop throws RunOwnershipLostError and writes nothing further.
    • Release: terminal status write + owner clear, token-checked so a stale holder's release no-ops.

    All sproc calls use the dialect-portable positional-parameter convention (same pattern as ScheduledJobEngine's lock sprocs): SQL Server binds the sproc's named params to positional @pN placeholders via EXEC; PostgreSQL calls the plpgsql port via SELECT * FROM fn($1,...). The SAME value array serves both.

    Index

    Constructors

    Properties

    DEFAULT_LEASE_MINUTES: 10

    Default lease when the caller supplies no MaxRuntimeMinutes.

    Accessors

    Methods

    • Atomically claim the run row. Returns true iff WE now own it (the sproc's single UPDATE succeeded because the row was unowned or its lease had expired). On success the DB-assigned FenceToken (bumped by the claim) and lease expiry are cached for renewals and boundary checks.

      Returns Promise<boolean>

    • Terminal release: set the final status, clear OwnerToken/LeaseExpiresAt, stamp EndedAt if unset. Token- AND fence-checked — a stale holder (lease reclaimed) releasing late is a harmless no-op (returns false).

      The fence is sent for the same reason Renew() sends it: the owner token proves only that some context using this token owns the row, not that THIS context still does. Each instance mints its own token and claims once, so the two are equivalent today — but Claim() is re-callable and overwrites the fence, so passing it keeps the guarantee in the procedure rather than in call-site discipline.

      Parameters

      Returns Promise<boolean>

    • Renew the lease (token+fence-checked). Zero rows back ⇒ ownership lost. Optionally piggybacks a progress snapshot onto the same write, and always surfaces the row's CancelRequestedAt so the heartbeat doubles as the cross-process cancel poll.

      Parameters

      • OptionalprogressJSON: string

      Returns Promise<RenewResult>

    • Start the background renewal timer at interval ≈ lease/3 (so a renewal must fail ~3 consecutive times before the lease can lapse). The timer body is best-effort and never throws; renewal failures surface through opts.onLost exactly once. Idempotent — restarting replaces the timer.

      Parameters

      Returns void

    • Sync the entity's in-memory ownership columns to the service's last-known authoritative values before ANY full-row run.Save(). The generated spUpdate writes every column from the entity's in-memory state, so a terminal Save without this sync would clobber the DB's live FenceToken/OwnerToken/LeaseExpiresAt with the stale values the entity was loaded with (typically pre-claim). Call this immediately before each Save on the run row; Release() then clears ownership atomically.

      Returns void

    • Persist a progress snapshot to the run row's ProgressJSON (PR 1 item 4 — progress lives in the database; readers query the row). Ownership-guarded (WHERE OwnerToken AND FenceToken) so a reclaimed run can never overwrite the new owner's progress. Throttled internally (default once per 5s) so progress never becomes its own hot path; best-effort — a progress write failure must never fault the sync.

      Parameters

      • progressJSON: string
      • minIntervalMs: number = 5_000

      Returns Promise<void>