Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is it concurrency safe? #2

Open
jitendra-1217 opened this issue Mar 14, 2019 · 5 comments
Open

Is it concurrency safe? #2

jitendra-1217 opened this issue Mar 14, 2019 · 5 comments

Comments

@jitendra-1217
Copy link

if (Cache::has($requestId)) {
return Cache::get($requestId);
}
$response = $next($request);
$response->header(self::IDEMPOTENCY_HEADER, $requestId);
Cache::put($requestId, $response, self::EXPIRATION_IN_MINUTES);

^ This does not look concurrency safe. I.e. multiple requests at once with same request id will actually execute it twice.

I wanted to understand if you think that is not a practical case at all and hence intentionally not handled.

@javidalpe
Copy link
Owner

Hi! Thanks for reviewing the code. You are totally right! If the $response = $next($request); takes 3 secs, for example, any request within 3 sec with the same idempotency key with will execute twice.

Do you have any suggestion to fix this issue? What about:
Given 2 almost concurrent requests A and B with same idempotency key X.

  • Request A arrives.
  • Request A put a flag 'work in progress' on cache for key X.
  • Request A handler begins.
  • Request B arrives.
  • Request B read 'work in progress' from the cache using key X.
  • Request B waits until the cache changes.
  • Request A handler ends.
  • Request A put the response on the cache for key X and releases.
  • Request B read the response from cache and releases.

@jitendra-1217
Copy link
Author

jitendra-1217 commented Mar 18, 2019

The suggested approach basically makes application single threaded i.e. can only serve one request at a time even if we have multiple servers/app instances/threads etc. Which does not seem like issue because it locks on given idempotency key.

But same approach can be extended to reduce the single-threadedness (can't think of other term :) ), e.g. that 'work in progress' lock could be route_name + user_id(or any client identifier). Not needed.

Updated per below comments to avoid mis read.

@javidalpe
Copy link
Owner

Referring Brandur blog post about idempotency

When performing a request, a client generates a unique ID to identify just that operation and sends it up to the server along with the normal payload.

So yes, it makes the app single threaded for each different idempotency key, which seems ok to me!

@jitendra-1217
Copy link
Author

Actually yeah, you're right.
This should work. I must have been thinking something else and mixed up.

@Mdhesari
Copy link

@javidalpe

I can fix this with a pr if you are ok with it

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

No branches or pull requests

3 participants