Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions ddtrace/internal/openfeature/_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@
from ddtrace.internal.openfeature._exposure import build_exposure_event
from ddtrace.internal.openfeature._native import VariationType
from ddtrace.internal.openfeature._native import resolve_flag
from ddtrace.internal.openfeature._remoteconfiguration import disable_featureflags_rc
from ddtrace.internal.openfeature._remoteconfiguration import enable_featureflags_rc
from ddtrace.internal.openfeature.writer import get_exposure_writer
from ddtrace.internal.openfeature.writer import start_exposure_writer
from ddtrace.internal.openfeature.writer import stop_exposure_writer
Expand Down Expand Up @@ -119,7 +117,7 @@ def get_metadata(self) -> Metadata:

def initialize(self, evaluation_context: EvaluationContext) -> None:
"""
Initialize the provider and enable remote configuration.
Initialize the provider.

Called by the OpenFeature SDK when the provider is set.
Provider Creation → NOT_READY
Expand All @@ -135,8 +133,6 @@ def initialize(self, evaluation_context: EvaluationContext) -> None:
if not self._enabled:
return

enable_featureflags_rc()

try:
# Start the exposure writer for reporting
start_exposure_writer()
Expand All @@ -152,14 +148,13 @@ def initialize(self, evaluation_context: EvaluationContext) -> None:

def shutdown(self) -> None:
"""
Shutdown the provider and disable remote configuration.
Shutdown the provider.

Called by the OpenFeature SDK when the provider is being replaced or shutdown.
"""
if not self._enabled:
return

disable_featureflags_rc()
try:
# Stop the exposure writer
stop_exposure_writer()
Expand Down
6 changes: 5 additions & 1 deletion ddtrace/internal/openfeature/_remoteconfiguration.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,12 @@ def featureflag_rc_callback(payloads: t.Sequence[Payload]) -> None:
log.debug("Error processing FFE config payload: %s", e, exc_info=True)


def _forksafe_featureflags_rc() -> None:
remoteconfig_poller.start_subscribers_by_product({FFE_FLAGS_PRODUCT})


def enable_featureflags_rc() -> None:
log.debug("[%s][P: %s] Register ASM Remote Config Callback", os.getpid(), os.getppid())
log.debug("[%s][P: %s] Register FFE Remote Config Callback", os.getpid(), os.getppid())
feature_flag_rc = FFEAdapter(featureflag_rc_callback)
remoteconfig_poller.register(
FFE_FLAGS_PRODUCT,
Expand Down
33 changes: 33 additions & 0 deletions ddtrace/internal/openfeature/product.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from ddtrace.internal.settings.openfeature import config as ffe_config


requires = ["remote-configuration"]


def post_preload():
pass


def start():
if ffe_config.experimental_flagging_provider_enabled:
from ddtrace.internal.openfeature._remoteconfiguration import enable_featureflags_rc

enable_featureflags_rc()


def restart(join=False):
if ffe_config.experimental_flagging_provider_enabled:
from ddtrace.internal.openfeature._remoteconfiguration import _forksafe_featureflags_rc

_forksafe_featureflags_rc()


def stop(join=False):
if ffe_config.experimental_flagging_provider_enabled:
from ddtrace.internal.openfeature._remoteconfiguration import disable_featureflags_rc

disable_featureflags_rc()


def at_exit(join=False):
pass
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ ddtrace = "ddtrace.testing.internal.pytest.entry_point"
"exception-replay" = "ddtrace.debugging._products.exception_replay"
"live-debugger" = "ddtrace.debugging._products.live_debugger"
"error-tracking" = "ddtrace.errortracking.product"
"openfeature" = "ddtrace.internal.openfeature.product"
"remote-configuration" = "ddtrace.internal.remoteconfig.products.client"
"symbol-database" = "ddtrace.internal.symbol_db.product"
"appsec" = "ddtrace.internal.appsec.product"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
fixes:
- |
openfeature: This fix resolves an issue where Feature Flagging and Experimentation (FFE) was not receiving remote configuration in forking web server environments (gunicorn, uWSGI). This caused the OpenFeature provider to return default values instead of configured feature flags. FFE is now properly registered as a product during tracer initialization (when ``DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true``), ensuring remote configuration is received before process forking occurs.
Loading