Skip to content

Fix/concurrent requests#29

Merged
arun0009 merged 15 commits into
arun0009:mainfrom
burl21:fix/concurrent-requests
Mar 3, 2026
Merged

Fix/concurrent requests#29
arun0009 merged 15 commits into
arun0009:mainfrom
burl21:fix/concurrent-requests

Conversation

@burl21

@burl21 burl21 commented Feb 10, 2026

Copy link
Copy Markdown
Collaborator

This PR fixes an issue where concurrency was not correctly handled when requests shared the same idempotency key. Previously, both requests were executing the business logic because they were treated as new requests; this behavior was present across all store implementations. An upsert was being performed, overwriting the key instead of enforcing a strict insert.

Conflict handling is now managed in the core by catching the exception and, when it occurs, treating the request as already existing.

This PR also adds waitForCompletion handling to IdempotentService, centralizing the logic in a single place for both the aspect and the service.

Additionally, properties have been extracted from IdempotentAspect into a separate class. Unfortunately, this introduced a breaking change (see //FIXME in core.ConfigurationProcessorTest).

I tried to group the changes logically by commit 🙂

@burl21 burl21 added the bug Something isn't working label Feb 10, 2026
@burl21
burl21 requested a review from arun0009 February 10, 2026 22:32
"idempotent.key.header",
"idempotent.inprogress.max.retries",
"idempotent.inprogress.retry.initial.intervalMillis",
"idempotent.inprogress.retry.initial.intervalMillis", // FIXME -->

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we fix this?

@arun0009

Copy link
Copy Markdown
Owner

Since IdempotentService provides a useful programmatic API, we should probably add it to IdempotentCoreAutoConfiguration so users can easily inject it as a bean.

@burl21

burl21 commented Feb 12, 2026

Copy link
Copy Markdown
Collaborator Author

Hi @arun0009
Thanks. What do you think about the solution based on exceptions? Do you think it’s better to go in this direction, or should we change the store() method so that it returns a boolean instead?

Regarding the breaking change fix, do you mean implementing it now or reverting to the previous behavior? It might make more sense to handle it in a major version and refactor the properties across the modules.

For the IdempotentService, would it make sense to add an overloaded method that accepts a waitStrategy parameter as well?

@arun0009

Copy link
Copy Markdown
Owner

Thanks. What do you think about the solution based on exceptions? Do you think it’s better to go in this direction, or should we change the store() method so that it returns a boolean instead?

Regarding the breaking change fix, do you mean implementing it now or reverting to the previous behavior? It might make more sense to handle it in a major version and refactor the properties across the modules.

For the IdempotentService, would it make sense to add an overloaded method that accepts a waitStrategy parameter as well?

  1. Exceptions vs. Boolean: Stick with the exception-based logic; it’s more descriptive and fits the Spring context well.
  2. Breaking Change: Let’s fix the property naming now by reverting to the original camelCase. This avoids breaking existing user configs without having to wait for a major version.
  3. Overloaded execute: Yes, adding a WaitStrategy parameter to the execute methods is a great idea for custom timeout control.
  4. Type Safety (Important): Removing operation.get().getClass() fixed the redundant execution, but the raw cast
    (T) will likely cause ClassCastException with stores like NATS/Dynamo?
    • Fix: Add Class responseType to the execute methods so the store knows exactly how to deserialize the cached result safely.

@burl21

burl21 commented Feb 16, 2026

Copy link
Copy Markdown
Collaborator Author
  1. Exceptions vs. Boolean: Stick with the exception-based logic; it’s more descriptive and fits the Spring context well.

ok

  1. Breaking Change: Let’s fix the property naming now by reverting to the original camelCase. This avoids breaking existing user configs without having to wait for a major version.

ok

  1. Overloaded execute: Yes, adding a WaitStrategy parameter to the execute methods is a great idea for custom timeout control.

ok

  1. Type Safety (Important): Removing operation.get().getClass() fixed the redundant execution, but the raw cast
    (T) will likely cause ClassCastException with stores like NATS/Dynamo?

    • Fix: Add Class responseType to the execute methods so the store knows exactly how to deserialize the cached result safely.

Now it’s not a problem because the type is saved by Jackson in the various store implementations. See commit: DynamoIdempotentStore for DynamoDB. However, I can still add it if needed.

@arun0009

Copy link
Copy Markdown
Owner

@burl21 - you should have access to create branches on this repo now. Its easier for both of us to create PRs/review.

@burl21

burl21 commented Feb 16, 2026

Copy link
Copy Markdown
Collaborator Author

@arun0009
Unfortunately, I need to roll back on a couple of points:

2. Breaking Change: Let’s fix the property naming now by reverting to the original camelCase. This avoids breaking existing user configs without having to wait for a major version.

Currently, it is not a breaking change; in fact, it’s an improvement because the binding supports both camelCase and kebab-case. I added a binding test and removed the .json file because it is now auto-generated by the configuration processor.

3. Overloaded execute: Yes, adding a WaitStrategy parameter to the execute methods is a great idea for custom timeout control.

I’ll add it in a separate PR since it requires changes to both the service and the completion awaiter. For a custom WaitStrategy, it’s already possible to instantiate the service by providing a custom wait strategy.

…ration handling and remove reflection-based field setup
…figuration class and remove redundant bean declarations across modules
…introduce status check utility in `IdempotentStore`
…pendencies, and concurrent operation logic across modules
… to clarify strict insert behavior and exceptions
…apper`, simplify existing operation handling logic, and disable a test case
…nvention, remove deprecated test, and add new test cases for camel and kebab case bindings
@burl21
burl21 force-pushed the fix/concurrent-requests branch from 2316733 to e5d49ca Compare February 25, 2026 10:25
@burl21
burl21 requested a review from arun0009 February 25, 2026 10:41
@burl21

burl21 commented Feb 26, 2026

Copy link
Copy Markdown
Collaborator Author

@arun0009 gentle ping 🙂

@arun0009

arun0009 commented Mar 1, 2026

Copy link
Copy Markdown
Owner

Also we should bump the version to 2.2.0 as part of this PR

@burl21

burl21 commented Mar 2, 2026

Copy link
Copy Markdown
Collaborator Author

@arun0009

  • Polish code after review
  • Throw error in case of wait timeout in both aspect and service instead of returning null. Because the operation would still be executed multiple times
  • Updated to version 2.2.0

@arun0009 arun0009 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@arun0009
arun0009 merged commit 0faf77d into arun0009:main Mar 3, 2026
4 of 7 checks passed
@arun0009

arun0009 commented Mar 3, 2026

Copy link
Copy Markdown
Owner

@burl21 : 2.2.0 released successfully! Great work and nicely handled it all in core.

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

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants