From b7816ae45c939d2104dd57e9c284aa3caa68c747 Mon Sep 17 00:00:00 2001 From: "christian.lutnik" Date: Wed, 18 Jun 2025 10:42:35 +0200 Subject: [PATCH] use concurrent data structure for hooks Signed-off-by: christian.lutnik --- .../dev/openfeature/sdk/OpenFeatureAPI.java | 32 +++++++++---------- .../openfeature/sdk/OpenFeatureClient.java | 26 ++++----------- .../openfeature/sdk/internal/ObjectUtils.java | 5 +-- 3 files changed, 26 insertions(+), 37 deletions(-) diff --git a/src/main/java/dev/openfeature/sdk/OpenFeatureAPI.java b/src/main/java/dev/openfeature/sdk/OpenFeatureAPI.java index 6733c8bf..6d0d8feb 100644 --- a/src/main/java/dev/openfeature/sdk/OpenFeatureAPI.java +++ b/src/main/java/dev/openfeature/sdk/OpenFeatureAPI.java @@ -5,10 +5,11 @@ import dev.openfeature.sdk.internal.AutoCloseableLock; import dev.openfeature.sdk.internal.AutoCloseableReentrantReadWriteLock; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; +import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import lombok.extern.slf4j.Slf4j; @@ -23,14 +24,14 @@ import lombok.extern.slf4j.Slf4j; public class OpenFeatureAPI implements EventBus { // package-private multi-read/single-write lock static AutoCloseableReentrantReadWriteLock lock = new AutoCloseableReentrantReadWriteLock(); - private final List apiHooks; + private final ConcurrentLinkedQueue apiHooks; private ProviderRepository providerRepository; private EventSupport eventSupport; private final AtomicReference evaluationContext = new AtomicReference<>(); private TransactionContextPropagator transactionContextPropagator; protected OpenFeatureAPI() { - apiHooks = new ArrayList<>(); + apiHooks = new ConcurrentLinkedQueue<>(); providerRepository = new ProviderRepository(this); eventSupport = new EventSupport(); transactionContextPropagator = new NoOpTransactionContextPropagator(); @@ -303,9 +304,7 @@ public class OpenFeatureAPI implements EventBus { * @param hooks The hook to add. */ public void addHooks(Hook... hooks) { - try (AutoCloseableLock ignored = lock.writeLockAutoCloseable()) { - this.apiHooks.addAll(Arrays.asList(hooks)); - } + this.apiHooks.addAll(Arrays.asList(hooks)); } /** @@ -314,22 +313,23 @@ public class OpenFeatureAPI implements EventBus { * @return A list of {@link Hook}s. */ public List getHooks() { - try (AutoCloseableLock ignored = lock.readLockAutoCloseable()) { - if (this.apiHooks.isEmpty()) { - return Collections.emptyList(); - } else { - return new ArrayList<>(this.apiHooks); - } - } + return new ArrayList<>(this.apiHooks); + } + + /** + * Returns a reference to the collection of {@link Hook}s. + * + * @return The collection of {@link Hook}s. + */ + Collection getMutableHooks() { + return this.apiHooks; } /** * Removes all hooks. */ public void clearHooks() { - try (AutoCloseableLock ignored = lock.writeLockAutoCloseable()) { - this.apiHooks.clear(); - } + this.apiHooks.clear(); } /** diff --git a/src/main/java/dev/openfeature/sdk/OpenFeatureClient.java b/src/main/java/dev/openfeature/sdk/OpenFeatureClient.java index 89d6a39b..b5522b66 100644 --- a/src/main/java/dev/openfeature/sdk/OpenFeatureClient.java +++ b/src/main/java/dev/openfeature/sdk/OpenFeatureClient.java @@ -5,8 +5,6 @@ import dev.openfeature.sdk.exceptions.FatalError; import dev.openfeature.sdk.exceptions.GeneralError; import dev.openfeature.sdk.exceptions.OpenFeatureError; import dev.openfeature.sdk.exceptions.ProviderNotReadyError; -import dev.openfeature.sdk.internal.AutoCloseableLock; -import dev.openfeature.sdk.internal.AutoCloseableReentrantReadWriteLock; import dev.openfeature.sdk.internal.ObjectUtils; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.util.ArrayList; @@ -16,6 +14,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import lombok.Getter; @@ -48,9 +47,8 @@ public class OpenFeatureClient implements Client { @Getter private final String version; - private final List clientHooks; + private final ConcurrentLinkedQueue clientHooks; private final HookSupport hookSupport; - AutoCloseableReentrantReadWriteLock hooksLock = new AutoCloseableReentrantReadWriteLock(); private final AtomicReference evaluationContext = new AtomicReference<>(); /** @@ -69,7 +67,7 @@ public class OpenFeatureClient implements Client { this.openfeatureApi = openFeatureAPI; this.domain = domain; this.version = version; - this.clientHooks = new ArrayList<>(); + this.clientHooks = new ConcurrentLinkedQueue<>(); this.hookSupport = new HookSupport(); } @@ -126,9 +124,7 @@ public class OpenFeatureClient implements Client { */ @Override public OpenFeatureClient addHooks(Hook... hooks) { - try (AutoCloseableLock ignored = this.hooksLock.writeLockAutoCloseable()) { - this.clientHooks.addAll(Arrays.asList(hooks)); - } + this.clientHooks.addAll(Arrays.asList(hooks)); return this; } @@ -137,13 +133,7 @@ public class OpenFeatureClient implements Client { */ @Override public List getHooks() { - try (AutoCloseableLock ignored = this.hooksLock.readLockAutoCloseable()) { - if (this.clientHooks.isEmpty()) { - return Collections.emptyList(); - } else { - return new ArrayList<>(this.clientHooks); - } - } + return new ArrayList<>(this.clientHooks); } /** @@ -183,10 +173,8 @@ public class OpenFeatureClient implements Client { var provider = stateManager.getProvider(); var state = stateManager.getState(); - try (AutoCloseableLock ignored = this.hooksLock.readLockAutoCloseable()) { - mergedHooks = ObjectUtils.merge( - provider.getProviderHooks(), flagOptions.getHooks(), clientHooks, openfeatureApi.getHooks()); - } + mergedHooks = ObjectUtils.merge( + provider.getProviderHooks(), flagOptions.getHooks(), clientHooks, openfeatureApi.getMutableHooks()); var mergedCtx = hookSupport.beforeHooks( type, diff --git a/src/main/java/dev/openfeature/sdk/internal/ObjectUtils.java b/src/main/java/dev/openfeature/sdk/internal/ObjectUtils.java index b367820c..86a9ddd7 100644 --- a/src/main/java/dev/openfeature/sdk/internal/ObjectUtils.java +++ b/src/main/java/dev/openfeature/sdk/internal/ObjectUtils.java @@ -1,6 +1,7 @@ package dev.openfeature.sdk.internal; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.function.Supplier; @@ -64,9 +65,9 @@ public class ObjectUtils { * @return resulting object */ @SafeVarargs - public static List merge(List... sources) { + public static List merge(Collection... sources) { List merged = new ArrayList<>(); - for (List source : sources) { + for (Collection source : sources) { merged.addAll(source); } return merged;