Skip to content

Commit 0071949

Browse files
authored
fix: parallel set state (#301)
* paralle set state * check is flushed after resolve so we flush remaining logs * move locks out to wasmresolverapi, remove retry strategy, handle isflushedexception * use read write lock, not wait for flush, handle isClosedException, change to isClosed * fixup! use read write lock, not wait for flush, handle isClosedException, change to isClosed * fixup! fixup! use read write lock, not wait for flush, handle isClosedException, change to isClosed
1 parent 9bc38b5 commit 0071949

File tree

11 files changed

+120
-275
lines changed

11 files changed

+120
-275
lines changed

openfeature-provider-local/src/main/java/com/spotify/confidence/ExponentialRetryStrategy.java

Lines changed: 0 additions & 65 deletions
This file was deleted.

openfeature-provider-local/src/main/java/com/spotify/confidence/GrpcWasmFlagLogger.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import com.spotify.confidence.shaded.flags.resolver.v1.InternalFlagLoggerServiceGrpc;
44
import com.spotify.confidence.shaded.flags.resolver.v1.WriteFlagLogsRequest;
5-
import com.spotify.confidence.shaded.flags.resolver.v1.WriteFlagLogsResponse;
65
import com.spotify.confidence.shaded.iam.v1.AuthServiceGrpc;
76
import io.grpc.Channel;
87
import io.grpc.ClientInterceptors;
@@ -16,7 +15,7 @@
1615
public class GrpcWasmFlagLogger implements WasmFlagLogger {
1716
private static final String CONFIDENCE_DOMAIN = "edge-grpc.spotify.com";
1817
private static final Logger logger = LoggerFactory.getLogger(GrpcWasmFlagLogger.class);
19-
private final InternalFlagLoggerServiceGrpc.InternalFlagLoggerServiceBlockingStub stub;
18+
private final InternalFlagLoggerServiceGrpc.InternalFlagLoggerServiceFutureStub stub;
2019

2120
public GrpcWasmFlagLogger(ApiSecret apiSecret) {
2221
final var channel = createConfidenceChannel();
@@ -27,12 +26,12 @@ public GrpcWasmFlagLogger(ApiSecret apiSecret) {
2726
final TokenHolder.Token token = tokenHolder.getToken();
2827
final Channel authenticatedChannel =
2928
ClientInterceptors.intercept(channel, new JwtAuthClientInterceptor(tokenHolder));
30-
this.stub = InternalFlagLoggerServiceGrpc.newBlockingStub(authenticatedChannel);
29+
this.stub = InternalFlagLoggerServiceGrpc.newFutureStub(authenticatedChannel);
3130
}
3231

3332
@Override
34-
public WriteFlagLogsResponse write(WriteFlagLogsRequest request) {
35-
return stub.writeFlagLogs(request);
33+
public void write(WriteFlagLogsRequest request) {
34+
final var ignore = stub.writeFlagLogs(request);
3635
}
3736

3837
private static ManagedChannel createConfidenceChannel() {

openfeature-provider-local/src/main/java/com/spotify/confidence/LocalResolverServiceFactory.java

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class LocalResolverServiceFactory implements ResolverServiceFactory {
4242
private static final MetricRegistry metricRegistry = new MetricRegistry();
4343
private static final String CONFIDENCE_DOMAIN = "edge-grpc.spotify.com";
4444
private static final Duration ASSIGN_LOG_INTERVAL = Duration.ofSeconds(10);
45+
private static final Duration POLL_LOG_INTERVAL = Duration.ofSeconds(10);
4546
private static final ScheduledExecutorService flagsFetcherExecutor =
4647
Executors.newScheduledThreadPool(1, new ThreadFactoryBuilder().setDaemon(true).build());
4748
private static final Duration RESOLVE_INFO_LOG_INTERVAL = Duration.ofMinutes(1);
@@ -67,27 +68,22 @@ static FlagResolverService from(
6768
ApiSecret apiSecret,
6869
String clientSecret,
6970
boolean isWasm,
70-
StickyResolveStrategy stickyResolveStrategy,
71-
RetryStrategy retryStrategy) {
72-
return createFlagResolverService(
73-
apiSecret, clientSecret, isWasm, stickyResolveStrategy, retryStrategy);
71+
StickyResolveStrategy stickyResolveStrategy) {
72+
return createFlagResolverService(apiSecret, clientSecret, isWasm, stickyResolveStrategy);
7473
}
7574

7675
static FlagResolverService from(
7776
AccountStateProvider accountStateProvider,
7877
String accountId,
79-
StickyResolveStrategy stickyResolveStrategy,
80-
RetryStrategy retryStrategy) {
81-
return createFlagResolverService(
82-
accountStateProvider, accountId, stickyResolveStrategy, retryStrategy);
78+
StickyResolveStrategy stickyResolveStrategy) {
79+
return createFlagResolverService(accountStateProvider, accountId, stickyResolveStrategy);
8380
}
8481

8582
private static FlagResolverService createFlagResolverService(
8683
ApiSecret apiSecret,
8784
String clientSecret,
8885
boolean isWasm,
89-
StickyResolveStrategy stickyResolveStrategy,
90-
RetryStrategy retryStrategy) {
86+
StickyResolveStrategy stickyResolveStrategy) {
9187
final var channel = createConfidenceChannel();
9288
final AuthServiceBlockingStub authService = AuthServiceGrpc.newBlockingStub(channel);
9389
final TokenHolder tokenHolder =
@@ -116,8 +112,7 @@ private static FlagResolverService createFlagResolverService(
116112
wasmFlagLogger,
117113
sidecarFlagsAdminFetcher.rawStateHolder().get().toByteArray(),
118114
sidecarFlagsAdminFetcher.accountId,
119-
stickyResolveStrategy,
120-
retryStrategy);
115+
stickyResolveStrategy);
121116
flagsFetcherExecutor.scheduleAtFixedRate(
122117
sidecarFlagsAdminFetcher::reload,
123118
pollIntervalSeconds,
@@ -130,8 +125,8 @@ private static FlagResolverService createFlagResolverService(
130125
sidecarFlagsAdminFetcher.rawStateHolder().get().toByteArray(),
131126
sidecarFlagsAdminFetcher.accountId);
132127
},
133-
10,
134-
10,
128+
POLL_LOG_INTERVAL.getSeconds(),
129+
POLL_LOG_INTERVAL.getSeconds(),
135130
TimeUnit.SECONDS);
136131

137132
return new WasmFlagResolverService(wasmResolverApi, stickyResolveStrategy);
@@ -173,8 +168,7 @@ private static boolean getFailFast(StickyResolveStrategy stickyResolveStrategy)
173168
private static FlagResolverService createFlagResolverService(
174169
AccountStateProvider accountStateProvider,
175170
String accountId,
176-
StickyResolveStrategy stickyResolveStrategy,
177-
RetryStrategy retryStrategy) {
171+
StickyResolveStrategy stickyResolveStrategy) {
178172
final var mode = System.getenv("LOCAL_RESOLVE_MODE");
179173
if (!(mode == null || mode.equals("WASM"))) {
180174
throw new RuntimeException("Only WASM mode supported with AccountStateProvider");
@@ -183,19 +177,22 @@ private static FlagResolverService createFlagResolverService(
183177
Optional.ofNullable(System.getenv("CONFIDENCE_RESOLVER_POLL_INTERVAL_SECONDS"))
184178
.map(Long::parseLong)
185179
.orElse(Duration.ofMinutes(5).toSeconds());
186-
final byte[] resolverStateProtobuf = accountStateProvider.provide();
180+
final AtomicReference<byte[]> resolverStateProtobuf =
181+
new AtomicReference<>(accountStateProvider.provide());
187182
final WasmFlagLogger flagLogger = request -> WriteFlagLogsResponse.getDefaultInstance();
188183
final ResolverApi wasmResolverApi =
189184
new ThreadLocalSwapWasmResolverApi(
190-
flagLogger, resolverStateProtobuf, accountId, stickyResolveStrategy, retryStrategy);
185+
flagLogger, resolverStateProtobuf.get(), accountId, stickyResolveStrategy);
191186
flagsFetcherExecutor.scheduleAtFixedRate(
192-
() -> {
193-
wasmResolverApi.updateStateAndFlushLogs(accountStateProvider.provide(), accountId);
194-
},
187+
() -> resolverStateProtobuf.set(accountStateProvider.provide()),
195188
pollIntervalSeconds,
196189
pollIntervalSeconds,
197190
TimeUnit.SECONDS);
198-
191+
logPollExecutor.scheduleAtFixedRate(
192+
() -> wasmResolverApi.updateStateAndFlushLogs(resolverStateProtobuf.get(), accountId),
193+
POLL_LOG_INTERVAL.getSeconds(),
194+
POLL_LOG_INTERVAL.getSeconds(),
195+
TimeUnit.SECONDS);
199196
return new WasmFlagResolverService(wasmResolverApi, stickyResolveStrategy);
200197
}
201198

openfeature-provider-local/src/main/java/com/spotify/confidence/NoRetryStrategy.java

Lines changed: 0 additions & 11 deletions
This file was deleted.

openfeature-provider-local/src/main/java/com/spotify/confidence/OpenFeatureLocalResolveProvider.java

Lines changed: 7 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -90,28 +90,7 @@ public class OpenFeatureLocalResolveProvider implements FeatureProvider {
9090
* @since 0.2.4
9191
*/
9292
public OpenFeatureLocalResolveProvider(ApiSecret apiSecret, String clientSecret) {
93-
this(apiSecret, clientSecret, new RemoteResolverFallback(), new NoRetryStrategy());
94-
}
95-
96-
/**
97-
* Creates a new OpenFeature provider for local flag resolution with configurable sticky resolve
98-
* strategy and no retry.
99-
*
100-
* <p>The provider will automatically determine the resolution mode (WASM or Java) based on the
101-
* {@code LOCAL_RESOLVE_MODE} environment variable, defaulting to WASM mode.
102-
*
103-
* @param apiSecret the API credentials containing client ID and client secret for authenticating
104-
* with the Confidence service. Create using {@code new ApiSecret("client-id",
105-
* "client-secret")}
106-
* @param clientSecret the client secret for your application, used for flag resolution
107-
* authentication. This is different from the API secret and is specific to your application
108-
* configuration
109-
* @param stickyResolveStrategy the strategy to use for handling sticky flag resolution
110-
* @since 0.2.4
111-
*/
112-
public OpenFeatureLocalResolveProvider(
113-
ApiSecret apiSecret, String clientSecret, StickyResolveStrategy stickyResolveStrategy) {
114-
this(apiSecret, clientSecret, stickyResolveStrategy, new NoRetryStrategy());
93+
this(apiSecret, clientSecret, new RemoteResolverFallback());
11594
}
11695

11796
/**
@@ -128,56 +107,25 @@ public OpenFeatureLocalResolveProvider(
128107
* authentication. This is different from the API secret and is specific to your application
129108
* configuration
130109
* @param stickyResolveStrategy the strategy to use for handling sticky flag resolution
131-
* @param retryStrategy the retry strategy for WASM operations (use {@link NoRetryStrategy} to
132-
* disable retries or {@link ExponentialRetryStrategy} for exponential backoff)
133110
* @since 0.2.4
134111
*/
135112
public OpenFeatureLocalResolveProvider(
136-
ApiSecret apiSecret,
137-
String clientSecret,
138-
StickyResolveStrategy stickyResolveStrategy,
139-
RetryStrategy retryStrategy) {
113+
ApiSecret apiSecret, String clientSecret, StickyResolveStrategy stickyResolveStrategy) {
140114
final var env = System.getenv("LOCAL_RESOLVE_MODE");
141115
if (env != null && env.equals("WASM")) {
142116
this.flagResolverService =
143-
LocalResolverServiceFactory.from(
144-
apiSecret, clientSecret, true, stickyResolveStrategy, retryStrategy);
117+
LocalResolverServiceFactory.from(apiSecret, clientSecret, true, stickyResolveStrategy);
145118
} else if (env != null && env.equals("JAVA")) {
146119
this.flagResolverService =
147-
LocalResolverServiceFactory.from(
148-
apiSecret, clientSecret, false, stickyResolveStrategy, retryStrategy);
120+
LocalResolverServiceFactory.from(apiSecret, clientSecret, false, stickyResolveStrategy);
149121
} else {
150122
this.flagResolverService =
151-
LocalResolverServiceFactory.from(
152-
apiSecret, clientSecret, true, stickyResolveStrategy, retryStrategy);
123+
LocalResolverServiceFactory.from(apiSecret, clientSecret, true, stickyResolveStrategy);
153124
}
154125
this.stickyResolveStrategy = stickyResolveStrategy;
155126
this.clientSecret = clientSecret;
156127
}
157128

158-
/**
159-
* To be used for testing purposes only! This constructor allows to inject flags state for testing
160-
* the WASM resolver (no Java supported) No resolve/assign logging is forwarded to production No
161-
* need to supply ApiSecret
162-
*
163-
* @param accountStateProvider a functional interface that provides AccountState instances
164-
* @param clientSecret the flag client key used to filter the flags
165-
* @since 0.2.4
166-
*/
167-
@VisibleForTesting
168-
public OpenFeatureLocalResolveProvider(
169-
AccountStateProvider accountStateProvider,
170-
String accountId,
171-
String clientSecret,
172-
StickyResolveStrategy stickyResolveStrategy) {
173-
this(
174-
accountStateProvider,
175-
accountId,
176-
clientSecret,
177-
stickyResolveStrategy,
178-
new NoRetryStrategy());
179-
}
180-
181129
/**
182130
* To be used for testing purposes only! This constructor allows to inject flags state for testing
183131
* the WASM resolver with full control over retry strategy.
@@ -186,21 +134,18 @@ public OpenFeatureLocalResolveProvider(
186134
* @param accountId the account ID
187135
* @param clientSecret the flag client key used to filter the flags
188136
* @param stickyResolveStrategy the strategy to use for handling sticky flag resolution
189-
* @param retryStrategy the retry strategy for WASM operations
190137
* @since 0.2.4
191138
*/
192139
@VisibleForTesting
193140
public OpenFeatureLocalResolveProvider(
194141
AccountStateProvider accountStateProvider,
195142
String accountId,
196143
String clientSecret,
197-
StickyResolveStrategy stickyResolveStrategy,
198-
RetryStrategy retryStrategy) {
144+
StickyResolveStrategy stickyResolveStrategy) {
199145
this.stickyResolveStrategy = stickyResolveStrategy;
200146
this.clientSecret = clientSecret;
201147
this.flagResolverService =
202-
LocalResolverServiceFactory.from(
203-
accountStateProvider, accountId, stickyResolveStrategy, retryStrategy);
148+
LocalResolverServiceFactory.from(accountStateProvider, accountId, stickyResolveStrategy);
204149
}
205150

206151
@Override

openfeature-provider-local/src/main/java/com/spotify/confidence/RetryStrategy.java

Lines changed: 0 additions & 17 deletions
This file was deleted.

0 commit comments

Comments
 (0)