Skip to content

Commit 9fc5102

Browse files
author
Ryan Prayogo
committed
fix: make the flags field variable final
1 parent ebcb5ce commit 9fc5102

File tree

2 files changed

+38
-12
lines changed

2 files changed

+38
-12
lines changed

src/main/java/dev/openfeature/sdk/providers/memory/InMemoryProvider.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public class InMemoryProvider extends EventProvider {
3333
@Getter
3434
private static final String NAME = "InMemoryProvider";
3535

36-
private Map<String, Flag<?>> flags;
36+
private final Map<String, Flag<?>> flags;
3737

3838
@Getter
3939
private ProviderState state = ProviderState.NOT_READY;
@@ -61,13 +61,13 @@ public void initialize(EvaluationContext evaluationContext) throws Exception {
6161

6262
/**
6363
* Updating provider flags configuration, replacing existing flags.
64-
* @param flags the flags to use instead of the previous flags.
64+
* @param newFlags the flags to use instead of the previous flags.
6565
*/
66-
public void updateFlags(Map<String, Flag<?>> flags) {
67-
Set<String> flagsChanged = new HashSet<>();
68-
flagsChanged.addAll(this.flags.keySet());
69-
flagsChanged.addAll(flags.keySet());
70-
this.flags = new ConcurrentHashMap<>(flags);
66+
public void updateFlags(Map<String, Flag<?>> newFlags) {
67+
Set<String> flagsChanged = new HashSet<>(newFlags.keySet());
68+
flagsChanged.removeAll(this.flags.keySet());
69+
this.flags.putAll(newFlags);
70+
7171
ProviderEventDetails details = ProviderEventDetails.builder()
7272
.flagsChanged(new ArrayList<>(flagsChanged))
7373
.message("flags changed")

src/test/java/dev/openfeature/sdk/providers/memory/InMemoryProviderTest.java

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,34 @@
22

33
import com.google.common.collect.ImmutableMap;
44
import dev.openfeature.sdk.Client;
5+
import dev.openfeature.sdk.EventDetails;
56
import dev.openfeature.sdk.ImmutableContext;
67
import dev.openfeature.sdk.OpenFeatureAPI;
78
import dev.openfeature.sdk.Value;
89
import dev.openfeature.sdk.exceptions.FlagNotFoundError;
910
import dev.openfeature.sdk.exceptions.ProviderNotReadyError;
1011
import dev.openfeature.sdk.exceptions.TypeMismatchError;
1112
import lombok.SneakyThrows;
12-
import org.junit.jupiter.api.BeforeAll;
13+
import org.junit.jupiter.api.BeforeEach;
1314
import org.junit.jupiter.api.Test;
1415

16+
import java.util.ArrayList;
1517
import java.util.HashMap;
18+
import java.util.List;
1619
import java.util.Map;
20+
import java.util.concurrent.Callable;
1721
import java.util.concurrent.ExecutorService;
1822
import java.util.concurrent.Executors;
23+
import java.util.function.Consumer;
1924

2025
import static dev.openfeature.sdk.Structure.mapToStructure;
2126
import static dev.openfeature.sdk.testutils.TestFlagsUtils.buildFlags;
2227
import static org.junit.jupiter.api.Assertions.assertEquals;
2328
import static org.junit.jupiter.api.Assertions.assertThrows;
2429
import static org.junit.jupiter.api.Assertions.assertTrue;
2530
import static org.mockito.ArgumentMatchers.any;
31+
import static org.mockito.ArgumentMatchers.argThat;
32+
import static org.mockito.Mockito.mock;
2633
import static org.mockito.Mockito.spy;
2734
import static org.mockito.Mockito.times;
2835
import static org.mockito.Mockito.verify;
@@ -34,8 +41,8 @@ class InMemoryProviderTest {
3441
private static InMemoryProvider provider;
3542

3643
@SneakyThrows
37-
@BeforeAll
38-
static void beforeAll() {
44+
@BeforeEach
45+
void beforeEach() {
3946
Map<String, Flag<?>> flags = buildFlags();
4047
provider = spy(new InMemoryProvider(flags));
4148
OpenFeatureAPI.getInstance().onProviderConfigurationChanged(eventDetails -> {});
@@ -108,20 +115,39 @@ void shouldThrowIfNotInitialized() {
108115
assertThrows(ProviderNotReadyError.class, ()-> inMemoryProvider.getBooleanEvaluation("fail_not_initialized", false, new ImmutableContext()));
109116
}
110117

118+
@SuppressWarnings("unchecked")
111119
@Test
112-
void multithreadedTest() {
120+
void emitChangedFlagsOnlyIfThereAreChangedFlags() {
121+
Consumer<EventDetails> handler = mock(Consumer.class);
122+
Map<String, Flag<?>> flags = buildFlags();
123+
124+
OpenFeatureAPI.getInstance().onProviderConfigurationChanged(handler);
125+
OpenFeatureAPI.getInstance().setProviderAndWait(provider);
126+
127+
provider.updateFlags(flags);
128+
129+
verify(handler, times(1))
130+
.accept(argThat(details -> details.getFlagsChanged().isEmpty()));
131+
}
132+
133+
@Test
134+
void multithreadedTest() throws InterruptedException {
113135
ExecutorService executorService = Executors.newFixedThreadPool(100);
136+
List<Callable<Void>> updates = new ArrayList<>();
114137
for (int i = 0; i < 10000; i++) {
115138
String flagKey = "multithreadedFlag" + i;
116-
executorService.submit(() -> {
139+
updates.add(() -> {
117140
provider.updateFlag(flagKey, Flag.builder()
118141
.variant("on", true)
119142
.variant("off", false)
120143
.defaultVariant("on")
121144
.build());
145+
return null;
122146
});
123147
}
124148

149+
executorService.invokeAll(updates);
150+
125151
for (int i = 0; i < 10000; i++) {
126152
assertTrue(client.getBooleanValue("multithreadedFlag" + i, false));
127153
}

0 commit comments

Comments
 (0)