Repository navigation
Releases: rasuvaeff/retry
Releases · rasuvaeff/retry
Release list
v1.6.2
v1.6.1
- Allow
rasuvaeff/context^0.2 (^0.1 || ^0.2), so the package installs next torasuvaeff/context-http0.2. No code changes: the package does not useContext::onTerminate(), the only API change in context 0.2.
v1.6.0
- Retry budgets (
stopAfterMs(),RetryingHttpClientbudgetMs) are measured with the monotonichrtime()when the clock is the defaultClock\SystemClock, so an NTP step or manual clock change no longer shrinks or inflates the budget;AttemptRecord::$elapsedMs/HttpAttemptRecord::$elapsedMsuse the same source. Any other clock (e.g.FakeClockviawithClock()) is still read as-is, andRetry-AfterHTTP-dates are still parsed with the PSR-20 clock (#32).
v1.5.0
- README requirements now match
composer.json(rasuvaeff/context^0.1,psr/http-message^2.0);README.ru.mdgains the missing cooperative-context section (#36). Http\RetryingHttpClientaccepts acontext:(rasuvaeff/context): checked before every attempt and between 50 ms sleep slices, so cancellation or an expired deadline interrupts a long backoff orRetry-Afterwait; itsContextExceptionis never retried and deliberately escapes PSR-18. A final response (not retryable, or from the last attempt) is always returned; with a retry pending, the ended context throws instead (#31).onRetry/onExhaustedhooks are now best-effort inRetryandRetryingHttpClient: a throwing hook no longer aborts the loop or replaces the operation's exception. Hook exceptions go to the newRetry::onHookError()/onHookError:callbacks; aContextExceptionfrom a hook still propagates. Behaviour change: code that threw from a hook to stop retrying must usestopIf()/ the retry predicates instead (#33).RetryingHttpClientgainsretryAfterJitter:— an opt-inJitterInterfacethat spreads aRetry-Afterdelay upwards only (never earlier than the server asks), still capped bymaxRetryAfterMs(#34).RetryingHttpClientgainsidempotentOnly:(defaultfalse): a request with a non-idempotent method passes through with a single call. NewRetryDecisions::isIdempotent(). The README andllms.txtnow warn that the defaults retry POST/PATCH (#35).
v1.4.0
v1.3.0
- Add
Retry::toPolicy(): RetryPolicy, so one builder configures both a closure runner (or arasuvaeff/resiliencepipeline) andHttp\RetryingHttpClient. Only max attempts, backoff, jitter, sleeper and randomizer carry over; predicates, hooks, thestopAfterMs()budget and the clock stay client-constructor arguments (#28). RetryPolicy::fixed()/exponential()/fixedFor()/exponentialFor()accept an optionaljitter:argument; default staysNoJitter(#28).Http\RetryingHttpClient:retryOnResponseis now optional and defaults toRetryDecisions::transient();retryOnResponseandretryOnExceptionmay declare a thirdint $attemptparameter (1-based), passed only to closures that declare it (#28).- Add
Sleeper\FakeSleeper::advancing(FakeClock $clock, ?\Closure $onSleep = null): a test sleeper that advances the fake clock by every delay and runs an optional hook, so time budgets and other clock-driven state observe the sleep without a hand-written double (#27). - Fix
examples/retry_after.php: its request threw ongetBody(), which the client has called since the body-rewind fix, so the example crashed after the first retry.
v1.2.3
- Fix a remote crash: a
Retry-Afterdelta-seconds of 16+ digits overflowedinton the* 1000and threw an uncaughtTypeError— escaping the PSR-18ClientExceptionInterfacecontract, so one hostile header could kill the consumer. Oversized values are now ignored like other unusable values (fall back to configured backoff). - Fix the retry loop crashing on a backwards clock step (NTP correction): a negative elapsed made
AttemptRecord's constructor throw from inside the catch block, losing the operation's own exception. Elapsed time is now clamped at zero in both the closure and the HTTP paths. - Rewind a seekable request body before every HTTP re-send: PSR-7 stream bodies are stateful, and attempt 2+ of a POST silently transmitted an empty body. Non-seekable bodies are documented as not safely retryable.
- Fix
Retry-AfterHTTP-date strictness:createFromFormat'sYaccepted 1-3 digit years, so a malformed two-digit-year date parsed as ancient history and clamped to an immediate-hammer 0ms delay. Only values that round-trip the IMF-fixdate format exactly are accepted; everything else falls back to backoff. - Replace deprecated
lcg_value()(PHP 8.4+) inSystemRandomizerwithRandom\Randomizer::getFloat()— every jittered retry emittedE_DEPRECATEDon PHP 8.4/8.5.
v1.2.2
- Migrate the property-based test suite from the frozen
rasuvaeff/property-testing2.x to the newrasuvaeff/property-testing-testoadapter (drop-in, no PHP code changes; same#[Property]attribute andGenAPI). - Adopt
rasuvaeff/rector-named-literalsand apply the named-argument rule to literal calls. - Raise
rasuvaeff/property-testing-testoto^0.6. - Fix the docblock on
JitterMode::Additive: it described a "symmetric factor... in (0, 1]", butAdditiveJitteris one-sided (spreads the delay downward only) and accepts a factor of0— this contradicted the class's own correct documentation everywhere else (README,AGENTS.md, llms.txt). - Give the "Backward compatibility" CI job the same SKIPPED-report/intentional-boundary tolerance the other packages in this monorepo already carry, so a future intentional major (or roave reporting a static-analysis-limitation SKIPPED finding) doesn't permanently block the required status check.
v1.2.1
- Reject trailing newlines in
Retry-Afterdelta-seconds parsing: anchor the
digit pattern inRetryAfterParserwith\zinstead of$(PCRE$
matches before a trailing\n, which let"120\n"parse as 120s instead of
falling through to HTTP-date parsing). Hygiene fix: the surrounding(int)
cast masked the smuggling for delta-seconds, but the trailing newline still
produced a different parse path than a clean value.
v1.2.0
- Ship an AI agent skill (
resources/skills/rasuvaeff-retry/SKILL.md+
extra.skillsin composer.json): projects using thellm/skillsComposer
plugin get the skill synced into.agents/skills/automatically on install. - Bump dev dependency
rasuvaeff/property-testingfrom^1.0to^2.6. - Make property-testing generator methods
public static(aprivate
generator is only reachable via reflection and gets removed by rector's
RemoveUnusedPrivateMethodRector).