Skip to content

feat: owner-verified session-lock release via ephpm_kv_del_if_eq - #4

Merged
luthermonson merged 1 commit into
mainfrom
feat/kv-v013
Oct 22, 2026
Merged

luthermonson merged 1 commit into
mainfrom
feat/kv-v013

Conversation

@luthermonson

Copy link
Copy Markdown
Contributor

What changed

The opt-in session lock (lockSessions: true) was released with a plain ephpm_kv_del(). If a request overran lockTtlSeconds, its lock lapsed and a second request could take it. When the first request then reached close(), it deleted the second request's lock, which let a third request in at the same time. Now the release uses the atomic compare-and-delete ephpm_kv_del_if_eq($lockKey, $token), so a lock is deleted only while it still holds this request's token.

  • New optional CompareAndDeleteKvOpsInterface with supportsDelIfEq() and delIfEq(). Both SapiKvOps and InMemoryKvOps implement it.
  • KvSessionHandler::releaseLock() uses delIfEq() when the backend supports it, and otherwise falls back to the plain del().
  • README: the "Best-effort unlock" caveat is now "Owner-verified unlock on ePHPm v0.11.x+", with advice to size lockTtlSeconds above the slowest request.

What was considered and not changed (v0.13.0 functions)

  • ephpm_kv_set_if_eq for lock extension: the handler has no extend or refresh step. The lock is taken in read() and released in close(), and a session handler gets no callback in between, so there is nowhere to extend it.
  • ephpm_kv_getex: updateTimestamp() is already a single ephpm_kv_expire() call (it has the payload in hand and falls back to write()), and read() doesn't touch the TTL. getex would not reduce round trips or close a race.

Feature detection / backward compatibility

  • SapiKvOps::supportsDelIfEq() is function_exists('ephpm_kv_del_if_eq'). On ePHPm runtimes without it, the release is the same plain del() as before.
  • The capability is a separate interface, and KvOpsInterface is unchanged. Third-party KvOpsInterface implementations keep working and keep the old release behaviour.
  • With locking disabled (the default), nothing changes.

Tests

  • The new path: a lock taken over by another holder survives the original holder's close(), and our own lock is still freed.
  • The fallback path, through a KvOpsInterface-only decorator (tests/Support/NoCompareAndDeleteKvOps.php): the unverified delete still happens.
  • InMemoryKvOps::delIfEq semantics.
  • Local run on PHP 8.5: 41 tests, 109 assertions, all OK.

The opt-in session lock was released with a plain del, so a request that
overran lockTtlSeconds deleted the lock a later request had since taken.
Release it with the atomic compare-and-delete ephpm_kv_del_if_eq (ePHPm
v0.11.x+) instead, feature-detected with function_exists(); older runtimes
keep the best-effort del.

The capability lives in a new optional CompareAndDeleteKvOpsInterface
(implemented by SapiKvOps and InMemoryKvOps) rather than on KvOpsInterface,
so custom KvOpsInterface backends keep working unchanged and fall back to
the plain del.
@luthermonson
luthermonson merged commit 9d578f1 into main Oct 22, 2026
3 checks passed
@luthermonson
luthermonson deleted the feat/kv-v013 branch October 22, 2026 09:52
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