-
Notifications
You must be signed in to change notification settings - Fork 48
fix: Reduce locking and concurrency issues #1478
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
Conversation
Signed-off-by: christian.lutnik <[email protected]>
@@ -24,7 +26,7 @@ public class OpenFeatureAPI implements EventBus<OpenFeatureAPI> { | |||
private final List<Hook> apiHooks; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This ArrayList
could also be replaced with some concurrent data structure, further reducing the usage of locks and object churn, but it might be slower in some cases
@@ -396,18 +396,20 @@ public OpenFeatureAPI on(ProviderEvent event, Consumer<EventDetails> handler) { | |||
*/ | |||
@Override | |||
public OpenFeatureAPI removeHandler(ProviderEvent event, Consumer<EventDetails> handler) { | |||
this.eventSupport.removeGlobalHandler(event, handler); | |||
try (AutoCloseableLock ignored = lock.writeLockAutoCloseable()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be possible to remove locking on the eventSupport
entirely, now that it uses concurrent collections.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Upon further review, I'm not so sure. We might get into trouble in the runHandlersForProvider
, but it might also work just fine.
The problem lies rather with setting/getting the provider and its state from the providerRepository
than anything we do in here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean it's breaking in that the behavior changes, right (different references)? There's no associated compilation breakages here (or I'm missing something?).
If so, I don't think I would really qualify that as breaking... it's a bit of a grey area, but IMO if we don't make specific guarantees about references, we don't necessarily need to adhere to them. I think if we considered all behavior as completely unchangeable we couldn't even improve the locking 😅 .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's what I meant
@@ -49,8 +50,7 @@ public class OpenFeatureClient implements Client { | |||
private final List<Hook> clientHooks; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This ArrayList
could also be replaced with some concurrent data structure, further reducing the usage of locks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ya it's final unlike the evaluationContext
is, so we wouldn't need to even use an atomic reference.
Do you see any reason not to?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
Signed-off-by: christian.lutnik <[email protected]>
Signed-off-by: christian.lutnik <[email protected]>
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1478 +/- ##
============================================
+ Coverage 92.97% 93.24% +0.26%
Complexity 488 488
============================================
Files 46 46
Lines 1182 1169 -13
Branches 103 103
============================================
- Hits 1099 1090 -9
+ Misses 53 49 -4
Partials 30 30
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
if (this.clientHooks.isEmpty()) { | ||
return Collections.emptyList(); | ||
} else { | ||
return new ArrayList<>(this.clientHooks); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will create some additional object churn per evaluation, I guess you're worried about exposing this mutable state?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't want to expose a reference to the list, as this would effectively remove the lock entirely. At least the Collections.emptyList();
should not allocate anything.
I don't think it would add churn per evaluation, as I don't use the getter during the evaluation, but use the field directly.
Also consider that this and the corresponding change in OpenFeatureAPI
is technically a breaking change
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approved but consider https://github.com/open-feature/java-sdk/pull/1478/files#r2152272420
Signed-off-by: christian.lutnik <[email protected]>
* | ||
* @return The collection of {@link Hook}s. | ||
*/ | ||
Collection<Hook> getMutableHooks() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really don't like this method, but we need some Method where we can return a ConcurrentLinkedQueue
and don't have to convert it into a list to reduce memory churn during evaluations
try (AutoCloseableLock __ = this.hooksLock.readLockAutoCloseable()) { | ||
return this.clientHooks; | ||
} | ||
return new ArrayList<>(this.clientHooks); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't return the ConcurrentLinkedQueue
, as this method returns a List
.
|
||
mergedHooks = ObjectUtils.merge( | ||
provider.getProviderHooks(), flagOptions.getHooks(), clientHooks, openfeatureApi.getHooks()); | ||
provider.getProviderHooks(), flagOptions.getHooks(), clientHooks, openfeatureApi.getMutableHooks()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Directly access the openfeatureApi
's ConcurrentLinkedQueue
Signed-off-by: christian.lutnik <[email protected]>
I'll merge this tomorrow unless I hear objections. |
|
* Reduce locking and concurrency issues Signed-off-by: christian.lutnik <[email protected]> * Reduce locking and concurrency issues Signed-off-by: christian.lutnik <[email protected]> * formatting Signed-off-by: christian.lutnik <[email protected]> * use concurrent data structure for hooks Signed-off-by: christian.lutnik <[email protected]> * use concurrent data structure for hooks Signed-off-by: christian.lutnik <[email protected]> --------- Signed-off-by: christian.lutnik <[email protected]> Co-authored-by: Todd Baert <[email protected]> Signed-off-by: suvaidkhan <[email protected]>
This PR
Reduces locking and potential concurrency issues