open-feature / open-feature/java-sdk

[Multi-provider] Gaps identified relative to js-sdk reference implementation

Open
#1,882 0 comments 0 reactions 0 assignees View on GitHub
multi-provider
Dominant language
Java
Stars
128
Forks
61
Avg merge
5h 57m
Merged PRs (30d)
26

Description

## Context

We conducted a cross-SDK comparison of all MultiProvider implementations using the js-sdk as the reference. The Java MultiProvider was recently moved into the core SDK in #1765, and while functional for basic use cases, we identified several gaps relative to the reference implementation. Some of these were already noted during the original PR review.

## Gaps

### 1. Child provider event aggregation and status tracking (High)

The MultiProvider extends `EventProvider` but does not listen to or forward events from child providers. If a child provider emits `PROVIDER_ERROR`, `PROVIDER_STALE`, or `PROVIDER_CONFIGURATION_CHANGED` at runtime, those events are not surfaced. This was called out in the #1765 review by @guidobrei:
> "we effectively lose the Event features when using MultiProvider"

**Expected behavior:**
- Listen to each child provider's events
- Maintain a per-provider status map
- Compute an aggregate status using "worst-wins" precedence: `FATAL > NOT_READY > ERROR > STALE > READY`
- Emit the corresponding event when the aggregate status changes
- Always forward `PROVIDER_CONFIGURATION_CHANGED` events (pass-through)

**Reference:** js-sdk `status-tracker.ts`, dotnet-sdk `HandleProviderEventAsync` / `DetermineAggregateStatus`

### 2. Per-provider hook execution during evaluation (High)

The strategy calls provider evaluation methods directly (e.g. `provider.getBooleanEvaluation(...)`), bypassing the SDK's hook pipeline. If a child provider defines hooks via `getProviderHooks()`, those hooks are not executed.

**Expected behavior:**
- Before evaluating a child provider, run its `before` hooks with an isolated copy of the hook context
- On success: run `after` hooks
- On error: run `error` hooks
- Always: run `finally` hooks
- Hook context must be isolated per-provider to prevent cross-provider mutation

**Reference:** js-sdk `hook-executor.ts`, go-sdk `isolation.go`, dotnet-sdk `ProviderExtensions.EvaluateAsync`

### 3. Tracking event forwarding (High)

`track()` is not overridden. The default no-op implementation means tracking events are not forwarded to child providers.

**Expected behavior:**
- Iterate over child providers and forward `track()` calls
- The strategy should control which providers receive tracking (e.g. skip `NOT_READY` / `FATAL` providers)
- Errors from individual `track()` calls should be caught and logged, not propagated

**Reference:** js-sdk `multi-provider.ts` `track()`, dotnet-sdk `MultiProvider.cs` `Track()`

### 4. ComparisonStrategy (Medium)

Only `FirstMatchStrategy` and `FirstSuccessfulStrategy` exist. There is no `ComparisonStrategy` for evaluating all providers and comparing results (useful for migration validation and consistency checks).

**Expected behavior:**
- Evaluate all providers (ideally in parallel)
- If all providers agree on the value, return it
- If providers disagree, call an optional `onMismatch` callback and return the designated fallback provider's result
- If any provider errors, collect and report all errors
- Constructor accepts a `fallbackProvider` and optional `onMismatch` callback

**Reference:** js-sdk `comparison-strategy.ts`, go-sdk `comparison_strategy.go`, dotnet-sdk `ComparisonStrategy.cs`

### 5. Duplicate provider name handling (Medium)

When two child providers share the same `metadata.name`, the later provider silently overwrites the earlier one in the internal `LinkedHashMap`. The first provider is effectively lost. Related to #1792.

**Expected behavior:**
- If an explicit name conflicts with an existing name, throw (misconfiguration)
- If multiple providers share the same metadata-derived name, auto-deduplicate with a numeric suffix (`name-1`, `name-2`, etc.)
- All providers should be preserved

**Reference:** js-sdk `registerProviders()`, dotnet-sdk `RegisterProviders()`

## Spec Reference

https://openfeature.dev/specification/appendix-a/#multi-provider

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.