diff --git a/openfeature-provider-local/src/main/java/com/spotify/confidence/ThreadLocalSwapWasmResolverApi.java b/openfeature-provider-local/src/main/java/com/spotify/confidence/ConcurrentRotatingResolverApi.java similarity index 73% rename from openfeature-provider-local/src/main/java/com/spotify/confidence/ThreadLocalSwapWasmResolverApi.java rename to openfeature-provider-local/src/main/java/com/spotify/confidence/ConcurrentRotatingResolverApi.java index dd10bbf7..fe262bc0 100644 --- a/openfeature-provider-local/src/main/java/com/spotify/confidence/ThreadLocalSwapWasmResolverApi.java +++ b/openfeature-provider-local/src/main/java/com/spotify/confidence/ConcurrentRotatingResolverApi.java @@ -1,7 +1,6 @@ package com.spotify.confidence; import com.spotify.confidence.flags.resolver.v1.ResolveWithStickyRequest; -import com.spotify.confidence.shaded.flags.resolver.v1.ResolveFlagsRequest; import com.spotify.confidence.shaded.flags.resolver.v1.ResolveFlagsResponse; import com.spotify.futures.CompletableFutures; import java.util.ArrayList; @@ -17,16 +16,16 @@ * Pre-initialized resolver instances mapped by thread ID to CPU core count. This eliminates both * lock contention and lazy initialization overhead. */ -class ThreadLocalSwapWasmResolverApi implements ResolverApi { - private static final Logger logger = - LoggerFactory.getLogger(ThreadLocalSwapWasmResolverApi.class); +class ConcurrentRotatingResolverApi implements RotatingWasmResolverApi { + private static final Logger logger = LoggerFactory.getLogger(ConcurrentRotatingResolverApi.class); private final WasmFlagLogger flagLogger; private final StickyResolveStrategy stickyResolveStrategy; private volatile byte[] currentState; private volatile String currentAccountId; // Pre-initialized resolver instances mapped by core index - private final Map resolverInstances = new ConcurrentHashMap<>(); + private final Map resolverInstances = + new ConcurrentHashMap<>(); private final int numInstances; private final AtomicInteger nextInstanceIndex = new AtomicInteger(0); private final ThreadLocal threadInstanceIndex = @@ -37,7 +36,7 @@ protected Integer initialValue() { } }; - public ThreadLocalSwapWasmResolverApi( + public ConcurrentRotatingResolverApi( WasmFlagLogger flagLogger, byte[] initialState, String accountId, @@ -60,7 +59,7 @@ public ThreadLocalSwapWasmResolverApi( CompletableFuture.runAsync( () -> { final var instance = - new SwapWasmResolverApi( + new SingleRotatingWasmResolverApi( this.flagLogger, this.currentState, this.currentAccountId, @@ -75,13 +74,13 @@ public ThreadLocalSwapWasmResolverApi( * typically called by a scheduled task to refresh the resolver state. */ @Override - public void updateStateAndFlushLogs(byte[] state, String accountId) { + public void rotate(byte[] state, String accountId) { this.currentState = state; this.currentAccountId = accountId; final var futures = resolverInstances.values().stream() - .map(v -> CompletableFuture.runAsync(() -> v.updateStateAndFlushLogs(state, accountId))) + .map(v -> CompletableFuture.runAsync(() -> v.rotate(state, accountId))) .toList(); CompletableFutures.allAsList(futures).join(); } @@ -91,36 +90,21 @@ public void updateStateAndFlushLogs(byte[] state, String accountId) { * assigned to an instance index when first accessed, ensuring even distribution across available * instances. */ - private SwapWasmResolverApi getResolverForCurrentThread() { + private SingleRotatingWasmResolverApi getResolverForCurrentThread() { final int instanceIndex = threadInstanceIndex.get(); return resolverInstances.get(instanceIndex); } /** Delegates resolveWithSticky to the assigned SwapWasmResolverApi instance. */ @Override - public CompletableFuture resolveWithSticky( - ResolveWithStickyRequest request) { - return getResolverForCurrentThread().resolveWithSticky(request); - } - - /** Delegates resolve to the assigned SwapWasmResolverApi instance. */ - @Override - public ResolveFlagsResponse resolve(ResolveFlagsRequest request) { + public CompletableFuture resolve(ResolveWithStickyRequest request) { return getResolverForCurrentThread().resolve(request); } - /** - * Returns the number of pre-initialized resolver instances. This is primarily for debugging and - * monitoring purposes. - */ - public int getInstanceCount() { - return resolverInstances.size(); - } - /** Closes all pre-initialized resolver instances and clears the map. */ @Override public void close() { - resolverInstances.values().forEach(SwapWasmResolverApi::close); + resolverInstances.values().forEach(SingleRotatingWasmResolverApi::close); resolverInstances.clear(); } } diff --git a/openfeature-provider-local/src/main/java/com/spotify/confidence/LocalResolverServiceFactory.java b/openfeature-provider-local/src/main/java/com/spotify/confidence/LocalResolverServiceFactory.java index 802507ce..50aad993 100644 --- a/openfeature-provider-local/src/main/java/com/spotify/confidence/LocalResolverServiceFactory.java +++ b/openfeature-provider-local/src/main/java/com/spotify/confidence/LocalResolverServiceFactory.java @@ -35,7 +35,7 @@ class LocalResolverServiceFactory implements ResolverServiceFactory { private final AtomicReference resolverStateHolder; private final ResolveTokenConverter resolveTokenConverter; - private final ResolverApi wasmResolveApi; + private final RotatingWasmResolverApi wasmResolveApi; private final Supplier timeSupplier; private final Supplier resolveIdSupplier; private final FlagLogger flagLogger; @@ -107,8 +107,8 @@ private static FlagResolverService createFlagResolverService( final var wasmFlagLogger = new GrpcWasmFlagLogger(apiSecret); if (isWasm) { - final ResolverApi wasmResolverApi = - new ThreadLocalSwapWasmResolverApi( + final RotatingWasmResolverApi wasmResolverApi = + new ConcurrentRotatingResolverApi( wasmFlagLogger, sidecarFlagsAdminFetcher.rawStateHolder().get().toByteArray(), sidecarFlagsAdminFetcher.accountId, @@ -121,7 +121,7 @@ private static FlagResolverService createFlagResolverService( logPollExecutor.scheduleAtFixedRate( () -> { - wasmResolverApi.updateStateAndFlushLogs( + wasmResolverApi.rotate( sidecarFlagsAdminFetcher.rawStateHolder().get().toByteArray(), sidecarFlagsAdminFetcher.accountId); }, @@ -180,8 +180,8 @@ private static FlagResolverService createFlagResolverService( final AtomicReference resolverStateProtobuf = new AtomicReference<>(accountStateProvider.provide()); final WasmFlagLogger flagLogger = request -> WriteFlagLogsResponse.getDefaultInstance(); - final ResolverApi wasmResolverApi = - new ThreadLocalSwapWasmResolverApi( + final RotatingWasmResolverApi wasmResolverApi = + new ConcurrentRotatingResolverApi( flagLogger, resolverStateProtobuf.get(), accountId, stickyResolveStrategy); flagsFetcherExecutor.scheduleAtFixedRate( () -> resolverStateProtobuf.set(accountStateProvider.provide()), @@ -189,7 +189,7 @@ private static FlagResolverService createFlagResolverService( pollIntervalSeconds, TimeUnit.SECONDS); logPollExecutor.scheduleAtFixedRate( - () -> wasmResolverApi.updateStateAndFlushLogs(resolverStateProtobuf.get(), accountId), + () -> wasmResolverApi.rotate(resolverStateProtobuf.get(), accountId), POLL_LOG_INTERVAL.getSeconds(), POLL_LOG_INTERVAL.getSeconds(), TimeUnit.SECONDS); @@ -212,7 +212,7 @@ private static FlagResolverService createFlagResolverService( } LocalResolverServiceFactory( - ResolverApi wasmResolveApi, + RotatingWasmResolverApi wasmResolveApi, AtomicReference resolverStateHolder, ResolveTokenConverter resolveTokenConverter, FlagLogger flagLogger, @@ -228,7 +228,7 @@ private static FlagResolverService createFlagResolverService( } LocalResolverServiceFactory( - ResolverApi wasmResolveApi, + RotatingWasmResolverApi wasmResolveApi, AtomicReference resolverStateHolder, ResolveTokenConverter resolveTokenConverter, Supplier timeSupplier, @@ -247,7 +247,7 @@ private static FlagResolverService createFlagResolverService( @VisibleForTesting public void setState(byte[] state, String accountId) { if (this.wasmResolveApi != null) { - wasmResolveApi.updateStateAndFlushLogs(state, accountId); + wasmResolveApi.rotate(state, accountId); } } diff --git a/openfeature-provider-local/src/main/java/com/spotify/confidence/LockedWasmResolverApi.java b/openfeature-provider-local/src/main/java/com/spotify/confidence/LockedWasmResolverApi.java new file mode 100644 index 00000000..65ad1c14 --- /dev/null +++ b/openfeature-provider-local/src/main/java/com/spotify/confidence/LockedWasmResolverApi.java @@ -0,0 +1,43 @@ +package com.spotify.confidence; + +import com.spotify.confidence.flags.resolver.v1.ResolveWithStickyRequest; +import com.spotify.confidence.shaded.flags.resolver.v1.ResolveFlagsResponse; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +class IsClosedException extends Exception {} + +class LockedWasmResolverApi implements ResolverApi { + private final ResolverApi resolverApi; + private boolean isConsumed = false; + private final ReadWriteLock wasmLock = new ReentrantReadWriteLock(); + + public LockedWasmResolverApi(ResolverApi resolverApi) { + this.resolverApi = resolverApi; + } + + @Override + public CompletableFuture resolve(ResolveWithStickyRequest request) + throws IsClosedException { + if (!wasmLock.writeLock().tryLock() || isConsumed) { + throw new IsClosedException(); + } + try { + return resolverApi.resolve(request); + } finally { + wasmLock.writeLock().unlock(); + } + } + + @Override + public void flush() { + wasmLock.readLock().lock(); + try { + resolverApi.flush(); + isConsumed = true; + } finally { + wasmLock.readLock().unlock(); + } + } +} diff --git a/openfeature-provider-local/src/main/java/com/spotify/confidence/ResolverApi.java b/openfeature-provider-local/src/main/java/com/spotify/confidence/ResolverApi.java index 03ecc389..1cdab4fd 100644 --- a/openfeature-provider-local/src/main/java/com/spotify/confidence/ResolverApi.java +++ b/openfeature-provider-local/src/main/java/com/spotify/confidence/ResolverApi.java @@ -1,37 +1,12 @@ package com.spotify.confidence; import com.spotify.confidence.flags.resolver.v1.ResolveWithStickyRequest; -import com.spotify.confidence.shaded.flags.resolver.v1.ResolveFlagsRequest; import com.spotify.confidence.shaded.flags.resolver.v1.ResolveFlagsResponse; import java.util.concurrent.CompletableFuture; -/** Common interface for WASM-based flag resolver implementations. */ interface ResolverApi { + CompletableFuture resolve(ResolveWithStickyRequest request) + throws IsClosedException; - /** - * Resolves flags with sticky assignment support. - * - * @param request The resolve request with sticky context - * @return A future containing the resolve response - */ - CompletableFuture resolveWithSticky(ResolveWithStickyRequest request); - - /** - * Resolves flags without sticky assignment support. - * - * @param request The resolve request - * @return The resolve response - */ - ResolveFlagsResponse resolve(ResolveFlagsRequest request); - - /** - * Updates the resolver state and flushes any pending logs. - * - * @param state The new resolver state - * @param accountId The account ID - */ - void updateStateAndFlushLogs(byte[] state, String accountId); - - /** Closes the resolver and releases any resources. */ - void close(); + void flush(); } diff --git a/openfeature-provider-local/src/main/java/com/spotify/confidence/RotatingWasmResolverApi.java b/openfeature-provider-local/src/main/java/com/spotify/confidence/RotatingWasmResolverApi.java new file mode 100644 index 00000000..337a1b03 --- /dev/null +++ b/openfeature-provider-local/src/main/java/com/spotify/confidence/RotatingWasmResolverApi.java @@ -0,0 +1,27 @@ +package com.spotify.confidence; + +import com.spotify.confidence.flags.resolver.v1.ResolveWithStickyRequest; +import com.spotify.confidence.shaded.flags.resolver.v1.ResolveFlagsResponse; +import java.util.concurrent.CompletableFuture; + +/** Common interface for WASM-based flag resolver implementations. */ +interface RotatingWasmResolverApi { + /** + * Resolves flags with sticky assignment support. + * + * @param request The resolve request + * @return The resolve response + */ + CompletableFuture resolve(ResolveWithStickyRequest request); + + /** + * Updates the resolver state and flushes any pending logs. + * + * @param state The new resolver state + * @param accountId The account ID + */ + void rotate(byte[] state, String accountId); + + /** Closes the resolver and releases any resources. */ + void close(); +} diff --git a/openfeature-provider-local/src/main/java/com/spotify/confidence/SingleRotatingWasmResolverApi.java b/openfeature-provider-local/src/main/java/com/spotify/confidence/SingleRotatingWasmResolverApi.java new file mode 100644 index 00000000..ef237437 --- /dev/null +++ b/openfeature-provider-local/src/main/java/com/spotify/confidence/SingleRotatingWasmResolverApi.java @@ -0,0 +1,53 @@ +package com.spotify.confidence; + +import com.spotify.confidence.flags.resolver.v1.ResolveWithStickyRequest; +import com.spotify.confidence.shaded.flags.resolver.v1.ResolveFlagsResponse; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; + +class SingleRotatingWasmResolverApi implements RotatingWasmResolverApi { + private final AtomicReference lockedResolverApi = new AtomicReference<>(); + private final StickyResolveStrategy stickyResolveStrategy; + private final WasmFlagLogger flagLogger; + + public SingleRotatingWasmResolverApi( + WasmFlagLogger flagLogger, + byte[] initialState, + String accountId, + StickyResolveStrategy stickyResolveStrategy) { + this.stickyResolveStrategy = stickyResolveStrategy; + this.flagLogger = flagLogger; + + // Create initial instance + final WasmResolveApi initialInstance = new WasmResolveApi(flagLogger); + initialInstance.setResolverState(initialState, accountId); + final var stickyResolverApi = new StickyResolverApi(initialInstance, stickyResolveStrategy); + this.lockedResolverApi.set(new LockedWasmResolverApi(stickyResolverApi)); + } + + @Override + public void rotate(byte[] state, String accountId) { + // Create new instance with updated state + final WasmResolveApi newInstance = new WasmResolveApi(flagLogger); + newInstance.setResolverState(state, accountId); + + final var stickyResolverApi = new StickyResolverApi(newInstance, stickyResolveStrategy); + final LockedWasmResolverApi oldInstance = + lockedResolverApi.getAndSet(new LockedWasmResolverApi(stickyResolverApi)); + if (oldInstance != null) { + oldInstance.flush(); + } + } + + @Override + public void close() {} + + @Override + public CompletableFuture resolve(ResolveWithStickyRequest request) { + try { + return lockedResolverApi.get().resolve(request); + } catch (IsClosedException e) { + return resolve(request); + } + } +} diff --git a/openfeature-provider-local/src/main/java/com/spotify/confidence/SwapWasmResolverApi.java b/openfeature-provider-local/src/main/java/com/spotify/confidence/StickyResolverApi.java similarity index 76% rename from openfeature-provider-local/src/main/java/com/spotify/confidence/SwapWasmResolverApi.java rename to openfeature-provider-local/src/main/java/com/spotify/confidence/StickyResolverApi.java index 51ae0876..71652fda 100644 --- a/openfeature-provider-local/src/main/java/com/spotify/confidence/SwapWasmResolverApi.java +++ b/openfeature-provider-local/src/main/java/com/spotify/confidence/StickyResolverApi.java @@ -3,61 +3,28 @@ import com.spotify.confidence.flags.resolver.v1.MaterializationMap; import com.spotify.confidence.flags.resolver.v1.ResolveWithStickyRequest; import com.spotify.confidence.flags.resolver.v1.ResolveWithStickyResponse; -import com.spotify.confidence.shaded.flags.resolver.v1.ResolveFlagsRequest; import com.spotify.confidence.shaded.flags.resolver.v1.ResolveFlagsResponse; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; -class SwapWasmResolverApi implements ResolverApi { - private final AtomicReference wasmResolverApiRef = new AtomicReference<>(); +class StickyResolverApi implements ResolverApi { + private final WasmResolveApi wasmResolverApi; private final StickyResolveStrategy stickyResolveStrategy; - private final WasmFlagLogger flagLogger; - public SwapWasmResolverApi( - WasmFlagLogger flagLogger, - byte[] initialState, - String accountId, - StickyResolveStrategy stickyResolveStrategy) { + public StickyResolverApi( + WasmResolveApi wasmResolverApi, StickyResolveStrategy stickyResolveStrategy) { + this.wasmResolverApi = wasmResolverApi; this.stickyResolveStrategy = stickyResolveStrategy; - this.flagLogger = flagLogger; - - // Create initial instance - final WasmResolveApi initialInstance = new WasmResolveApi(flagLogger); - initialInstance.setResolverState(initialState, accountId); - this.wasmResolverApiRef.set(initialInstance); - } - - @Override - public void updateStateAndFlushLogs(byte[] state, String accountId) { - // Create new instance with updated state - final WasmResolveApi newInstance = new WasmResolveApi(flagLogger); - newInstance.setResolverState(state, accountId); - - // Get current instance before switching - final WasmResolveApi oldInstance = wasmResolverApiRef.getAndSet(newInstance); - if (oldInstance != null) { - oldInstance.close(); - } } @Override - public void close() {} - - @Override - public CompletableFuture resolveWithSticky( - ResolveWithStickyRequest request) { - final var instance = wasmResolverApiRef.get(); + public CompletableFuture resolve(ResolveWithStickyRequest request) { final ResolveWithStickyResponse response; - try { - response = instance.resolveWithSticky(request); - } catch (IsClosedException e) { - return resolveWithSticky(request); - } + response = wasmResolverApi.resolve(request); switch (response.getResolveResultCase()) { case SUCCESS -> { @@ -81,7 +48,7 @@ public CompletableFuture resolveWithSticky( final var currentRequest = handleMissingMaterializations( request, missingMaterializations.getItemsList(), repository); - return resolveWithSticky(currentRequest); + return resolve(currentRequest); } throw new RuntimeException( @@ -185,12 +152,7 @@ private ResolveWithStickyRequest handleMissingMaterializations( } @Override - public ResolveFlagsResponse resolve(ResolveFlagsRequest request) { - final var instance = wasmResolverApiRef.get(); - try { - return instance.resolve(request); - } catch (IsClosedException e) { - return resolve(request); - } + public void flush() { + wasmResolverApi.flush(); } } diff --git a/openfeature-provider-local/src/main/java/com/spotify/confidence/WasmFlagResolverService.java b/openfeature-provider-local/src/main/java/com/spotify/confidence/WasmFlagResolverService.java index 93fe19e9..80b16a57 100644 --- a/openfeature-provider-local/src/main/java/com/spotify/confidence/WasmFlagResolverService.java +++ b/openfeature-provider-local/src/main/java/com/spotify/confidence/WasmFlagResolverService.java @@ -6,11 +6,11 @@ import java.util.concurrent.CompletableFuture; record WasmFlagResolverService( - ResolverApi wasmResolveApi, StickyResolveStrategy stickyResolveStrategy) + RotatingWasmResolverApi wasmResolveApi, StickyResolveStrategy stickyResolveStrategy) implements FlagResolverService { @Override public CompletableFuture resolveFlags(ResolveFlagsRequest request) { - return wasmResolveApi.resolveWithSticky( + return wasmResolveApi.resolve( ResolveWithStickyRequest.newBuilder() .setResolveRequest(request) .setFailFastOnSticky(getFailFast(stickyResolveStrategy)) diff --git a/openfeature-provider-local/src/main/java/com/spotify/confidence/WasmResolveApi.java b/openfeature-provider-local/src/main/java/com/spotify/confidence/WasmResolveApi.java index 29686bac..f12700cb 100644 --- a/openfeature-provider-local/src/main/java/com/spotify/confidence/WasmResolveApi.java +++ b/openfeature-provider-local/src/main/java/com/spotify/confidence/WasmResolveApi.java @@ -17,20 +17,14 @@ import com.spotify.confidence.flags.resolver.v1.LogMessage; import com.spotify.confidence.flags.resolver.v1.ResolveWithStickyRequest; import com.spotify.confidence.flags.resolver.v1.ResolveWithStickyResponse; -import com.spotify.confidence.shaded.flags.resolver.v1.ResolveFlagsRequest; -import com.spotify.confidence.shaded.flags.resolver.v1.ResolveFlagsResponse; import com.spotify.confidence.shaded.flags.resolver.v1.WriteFlagLogsRequest; import com.spotify.confidence.wasm.Messages; import java.io.IOException; import java.io.InputStream; import java.time.Instant; import java.util.List; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Function; -class IsClosedException extends Exception {} - @FunctionalInterface interface WasmFlagLogger { void write(WriteFlagLogsRequest request); @@ -49,9 +43,7 @@ class WasmResolveApi { // api private final ExportFunction wasmMsgGuestSetResolverState; private final ExportFunction wasmMsgFlushLogs; - private final ExportFunction wasmMsgGuestResolve; private final ExportFunction wasmMsgGuestResolveWithSticky; - private final ReadWriteLock wasmLock = new ReentrantReadWriteLock(); public WasmResolveApi(WasmFlagLogger flagLogger) { this.writeFlagLogs = flagLogger; @@ -83,7 +75,6 @@ public WasmResolveApi(WasmFlagLogger flagLogger) { wasmMsgFree = instance.export("wasm_msg_free"); wasmMsgGuestSetResolverState = instance.export("wasm_msg_guest_set_resolver_state"); wasmMsgFlushLogs = instance.export("wasm_msg_guest_flush_logs"); - wasmMsgGuestResolve = instance.export("wasm_msg_guest_resolve"); wasmMsgGuestResolveWithSticky = instance.export("wasm_msg_guest_resolve_with_sticky"); } catch (IOException e) { throw new RuntimeException("Failed to load WASM module", e); @@ -119,44 +110,18 @@ public void setResolverState(byte[] state, String accountId) { consumeResponse(respPtr, Messages.Void::parseFrom); } - public void close() { - wasmLock.readLock().lock(); - try { - final var voidRequest = Messages.Void.getDefaultInstance(); - final var reqPtr = transferRequest(voidRequest); - final var respPtr = (int) wasmMsgFlushLogs.apply(reqPtr)[0]; - final var request = consumeResponse(respPtr, WriteFlagLogsRequest::parseFrom); - writeFlagLogs.write(request); - } finally { - wasmLock.readLock().unlock(); - } + public void flush() { + final var voidRequest = Messages.Void.getDefaultInstance(); + final var reqPtr = transferRequest(voidRequest); + final var respPtr = (int) wasmMsgFlushLogs.apply(reqPtr)[0]; + final var request = consumeResponse(respPtr, WriteFlagLogsRequest::parseFrom); + writeFlagLogs.write(request); } - public ResolveWithStickyResponse resolveWithSticky(ResolveWithStickyRequest request) - throws IsClosedException { - if (!wasmLock.writeLock().tryLock()) { - throw new IsClosedException(); - } - try { - final int reqPtr = transferRequest(request); - final int respPtr = (int) wasmMsgGuestResolveWithSticky.apply(reqPtr)[0]; - return consumeResponse(respPtr, ResolveWithStickyResponse::parseFrom); - } finally { - wasmLock.writeLock().unlock(); - } - } - - public ResolveFlagsResponse resolve(ResolveFlagsRequest request) throws IsClosedException { - if (!wasmLock.writeLock().tryLock()) { - throw new IsClosedException(); - } - try { - final int reqPtr = transferRequest(request); - final int respPtr = (int) wasmMsgGuestResolve.apply(reqPtr)[0]; - return consumeResponse(respPtr, ResolveFlagsResponse::parseFrom); - } finally { - wasmLock.writeLock().unlock(); - } + public ResolveWithStickyResponse resolve(ResolveWithStickyRequest request) { + final int reqPtr = transferRequest(request); + final int respPtr = (int) wasmMsgGuestResolveWithSticky.apply(reqPtr)[0]; + return consumeResponse(respPtr, ResolveWithStickyResponse::parseFrom); } private T consumeResponse(int addr, ParserFn codec) { diff --git a/openfeature-provider-local/src/test/java/com/spotify/confidence/TestBase.java b/openfeature-provider-local/src/test/java/com/spotify/confidence/TestBase.java index 9687f72c..c8a9671c 100644 --- a/openfeature-provider-local/src/test/java/com/spotify/confidence/TestBase.java +++ b/openfeature-provider-local/src/test/java/com/spotify/confidence/TestBase.java @@ -44,7 +44,7 @@ protected TestBase(ResolverState state, boolean isWasm) { final ResolveTokenConverter resolveTokenConverter = new PlainResolveTokenConverter(); if (isWasm) { final var wasmResolverApi = - new SwapWasmResolverApi( + new SingleRotatingWasmResolverApi( request -> {}, desiredState.toProto().toByteArray(), "", mockFallback); resolverServiceFactory = new LocalResolverServiceFactory(