Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Comments

feat(idempotency): add idempotency package#1997

Draft
xHeaven wants to merge 8 commits into3.x from
idempotency
Draft

feat(idempotency): add idempotency package #1997
xHeaven wants to merge 8 commits into3.x from
idempotency

Conversation

@xHeaven
Copy link
Member

@xHeaven xHeaven commented Feb 20, 2026
edited
Loading

Overview

This PR adds idempotency support for HTTP routes and command bus commands. It prevents duplicate side effects by storing the result of the first execution and replaying it for subsequent requests with the same idempotency key.

Payment processing, order creation, resource provisioning and any operation where retrying the same request should not produce duplicate side effects. Timeouts, retries by clients, and accidental double clicks all cause the same problem: the server can't distinguish a retry from a new request.

Routes

Add #[Idempotent] to a controller method. Clients send an Idempotency-Key header. The first request executes normally and caches the response. Subsequent requests with the same key replay the cached response.

Example:

use Tempest\Idempotency\Attributes\Idempotent;
final readonly class OrderController
{
 #[Post('/orders')]
 #[Idempotent]
 public function create(CreateOrderRequest $request): Response
 {
 // Only executes once per idempotency key.
 // Retries replay the original response.
 $order = $this->orderService->create($request);
 return new GenericResponse(
 status: Status::CREATED,
 body: ['id' => $order->id],
 );
 }
}

Commands

Mark a command with #[IdempotentCommand]. Duplicate dispatches with the same payload are silently skipped.

Example:

use Tempest\Idempotency\Attributes\IdempotentCommand;
#[IdempotentCommand]
final readonly class ImportInvoicesCommand
{
 public function __construct(
 public string $vendorId,
 public string $month,
 ) {}
}

Commands can also provide explicit keys via HasIdempotencyKey.

Example:

use Tempest\Idempotency\Attributes\IdempotentCommand;
use Tempest\Idempotency\Contracts\HasIdempotencyKey;
#[IdempotentCommand]
final readonly class ProcessPaymentCommand implements HasIdempotencyKey
{
 public function __construct(
 public string $paymentId,
 public int $amount,
 ) {}
 public function getIdempotencyKey(): string
 {
 return $this->paymentId;
 }
}

Known limitations at the opening of PR

  • (削除) No differentiation between in-progress/same key, different payload cases (削除ここまで)
  • (削除) Scoping is weak, we need an additional component (user-provided resolver) (削除ここまで)
  • (削除) Heartbeat renewal is not implemented yet (削除ここまで)
  • (削除) We need to target only POST and PATCH methods, disallow others (削除ここまで)
  • Possibly other things I didn't think of

Wonftixes (design decision)

  • Stored responses must be JsonSerializable (more of a design decision than an issue)
  • Windows incompatibility due to posix and pcntl usage

Copy link

github-actions bot commented Feb 20, 2026
edited
Loading

Benchmark Results

Comparison of idempotency against 3.x (092791b48782454545faf396cb9433b8afb22c8e).

Open to see the benchmark results
Benchmark Set Mem. Peak Time Variability
ViewRenderBench(benchExpressions) - 23.903mb +0.00% 519.755μs +5.06% ±1.25% -56.92%

Generated by phpbench against commit 2caea11

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@brendt brendt Awaiting requested review from brendt brendt will be requested when the pull request is marked ready for review brendt is a code owner

@innocenzi innocenzi Awaiting requested review from innocenzi innocenzi will be requested when the pull request is marked ready for review innocenzi is a code owner

@aidan-casey aidan-casey Awaiting requested review from aidan-casey aidan-casey will be requested when the pull request is marked ready for review aidan-casey is a code owner

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

1 participant

AltStyle によって変換されたページ (->オリジナル) /