Skip to content

Releases: rasuvaeff/bulkhead

v1.4.0

Choose a tag to compare

@github-actions github-actions released this 15 Oct 18:21
4bdb4e8
  • Add ContextAwareBulkhead and SharedBulkhead::withContext(Context): the same slots, with the wait capped by a rasuvaeff/context budget (#48). When the budget ends first the wait ends with ContextExpiredException/ContextCancelledException — not BulkheadFullException, and onRejected is not called: the caller's exhausted budget is not an overload signal. A terminated context takes no slot. rasuvaeff/context ^0.3 becomes a runtime dependency.
  • CI: experimental PHP 8.6 job (RC, tests only, not required).

v1.3.0

Choose a tag to compare

@github-actions github-actions released this 10 Oct 10:33
af4be1b
  • Add KeyedBulkhead: the same wait, lease and observer semantics as SharedBulkhead, with the name and limit passed per call (call(name, max, cb), acquire(), tryAcquire(), availableSlots()) — for one limit per proxy, tenant or browser context without dropping to the raw BulkheadStore and a hand-written try/finally (#43).
  • Add Slot, a held slot as a value with an idempotent release(), returned by the new SharedBulkhead::acquire()/tryAcquire() and their KeyedBulkhead twins: take a slot in one place, release it where the work ends (#43). acquire() waits up to maxWait and throws BulkheadFullException; tryAcquire() never waits and returns null. A throwing onAccepted releases the slot before rethrowing, as in call().
  • BulkheadFullException now carries waited (time spent waiting) and lease, and retryAfter(): ?Duration returns the lease as an upper bound on when a slot frees — a Retry-After ceiling (#44). Both are new optional constructor parameters; an exception built by hand still works and reports null.
  • Sleeper\FakeSleeper accepts an optional onSleep closure called after each recorded sleep, so a test can advance a fake clock without its own sleeper (#45, additive part).
  • CI: tolerate roave's all-SKIPPED BC report, split the mutation path filter, verify the tagged commit before publishing a release.

v1.2.0

Choose a tag to compare

@github-actions github-actions released this 16 Sep 20:50
6c1c634
  • Add BatchBulkheadStore::activeCounts(list $names): array — an active-load snapshot for many bulkheads at once, for least-loaded selection among hundreds of candidates without one store round trip per name (#38). All three bundled stores implement it: read-only for slots, 0 for names without active slots, duplicate names collapsed, first-seen order. RedisBulkheadStore does it in one Lua round trip that prunes expired leases and counts every key at the same server TIME (mutually consistent counts; any Redis error propagates, no partial result); APCu and in-memory read per name. Redis Cluster needs a hash tag in keyPrefix ({bulkhead}:) or the multi-key call fails with CROSSSLOT.
  • Add BulkheadMultiKeyScriptRunner::runMany(script, keys, args): list<int>, implemented by PredisScriptRunner and PhpRedisScriptRunner. RedisBulkheadStore uses it for activeCounts() and degrades to one activeCount() call per name with a custom single-key BulkheadScriptRunner. Existing interfaces are unchanged.
  • Switch the test suite's hand-rolled doubles (anonymous BulkheadStore scripted/throwing/poll-budget fakes and the predis ClientInterface fake) to rasuvaeff/understudy via the rasuvaeff/understudy-testo adapter: scriptedStore(n) is a ->returns(null × n, 'token') chain (last link repeats), the poll-budget fake is ->returns(...)->then()->throws(), an always-full store is a loose double with no stubs, and verification/reset run automatically after each test. Dev-only; no runtime changes.

v1.1.3

Choose a tag to compare

@github-actions github-actions released this 21 Aug 20:08
3f57dfc
  • Fix a slot leak: the onAccepted observer callback ran after acquire but outside the try/finally that guarantees release(), so a throwing observer (unreachable metrics backend, a TypeError in the closure) left the slot occupied — until lease expiry on Redis/APCu, and permanently with InMemoryBulkheadStore (which ignores the lease). The callback now runs inside the try.
  • Document the release-path failure semantics: a release() exception masks the callback's exception (reachable via getPrevious(), which PHP chains automatically) and loses a successful result — a store exception from call() means "outcome unknown", never "did not happen". Pinned by tests.
  • Docs: fix the name-pattern anchor shown in README.md/README.ru.md/llms.txt to the \z the code actually uses ($ matches before a trailing newline); document that keyPrefix is trusted configuration (not validated, unlike name) and the token trust model (CSPRNG tokens protect against accidents, not adversaries — the trust boundary is Redis access itself).

v1.1.2

Choose a tag to compare

@github-actions github-actions released this 21 Aug 19:22
f29e5c5
  • Document that the store is a hard dependency: if Redis/APCu is unreachable, call() fails closed (throws) rather than admitting the call unboundedly. Callers who only caught BulkheadFullException could be surprised by an uncaught Predis\Connection\ConnectionException (or equivalent) on a store outage.
  • Raise rasuvaeff/property-testing-testo to ^0.6.

v1.1.1

Choose a tag to compare

@rasuvaeff rasuvaeff released this 01 Aug 22:41
c34d205
  • Reject trailing newlines in bulkhead-name validation: anchor
    SharedBulkhead::NAME_PATTERN with \z instead of $ (PCRE $ matches
    before a trailing \n, which let "<name>\n" pass and become the
    Redis/APCu key).

v1.1.0

Choose a tag to compare

@rasuvaeff rasuvaeff released this 01 Aug 22:41
f240796
  • Ship an AI agent skill (resources/skills/rasuvaeff-bulkhead/SKILL.md +
    extra.skills in composer.json): projects using the llm/skills Composer
    plugin get the skill synced into .agents/skills/ automatically on install.
  • Bump rasuvaeff/property-testing dev dependency to ^2.6.
  • Property-generator methods in tests are now public static (private ones are
    removed by rector's RemoveUnusedPrivateMethodRector — they are only called
    via reflection).

v1.0.1

Choose a tag to compare

@rasuvaeff rasuvaeff released this 01 Aug 22:41
5c9a253
  • PredisScriptRunner issues EVALSHA/EVAL through the typed
    createCommand()/executeCommand() API instead of magic __call — the
    @method annotations for eval/evalsha are not resolvable by psalm across
    every supported predis release (broke the prefer-lowest CI job).

v1.0.0

Choose a tag to compare

@rasuvaeff rasuvaeff released this 01 Aug 22:41
  • Initial release: SharedBulkhead cross-process concurrency limiter with
    RedisBulkheadStore (multi-host, sorted set + Lua, EVALSHA-first script
    runners for predis and ext-redis), ApcuBulkheadStore (single-host,
    APCu spinlock) and InMemoryBulkheadStore (tests/CLI).
  • Optional pollJitter desynchronizes waiters polling for a freed slot.
  • onAccepted/onRejected callbacks receive the bulkhead name and the time
    waited for a slot.
  • All Redis clients are optional dependencies (suggest): install
    predis/predis or ext-redis only if you use RedisBulkheadStore.