Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
053f9d8
feat(02-05): thread split serial_id Rust -> C -> PHP mapper + add spa…
leoromanovsky Jun 16, 2026
8027165
feat(02-05): inline span-enrichment accumulation (DG-004) + codec + r…
leoromanovsky Jun 16, 2026
cf47e8e
fix(02-05): reset FFE span-enrichment accumulator on root-span bounda…
leoromanovsky Jun 16, 2026
e5fa42f
test(ffe): add await_ffe_config internal fn to pump RC in CLI servers
leoromanovsky Jun 16, 2026
102d3be
feat(FeatureFlags): bind APM span enrichment to native Client path
leoromanovsky Jun 17, 2026
2cf4062
fix(config): regenerate supported-configurations.json for span-enrich…
leoromanovsky Jun 17, 2026
50c80d5
test(ffe): use PHP 7.0-compatible int assertion in ResultMapperTest
leoromanovsky Jun 17, 2026
5477477
fix(ffe): add non-creating root accessor + gate await_ffe_config test…
leoromanovsky Jun 17, 2026
f0a0351
fix(ffe): share one request-scoped span-enrichment accumulator across…
leoromanovsky Jun 17, 2026
5a037d1
test(ffe): cover shared-accumulator aggregation, gate-off inertness, …
leoromanovsky Jun 17, 2026
3229c80
fix(ffe): emit ffe_* JSON as raw UTF-8 with unescaped slashes (Node p…
leoromanovsky Jun 17, 2026
156726d
Fix PHP span enrichment CI compatibility
leoromanovsky Jun 17, 2026
30ff2d3
Merge branch 'master' into leo.romanovsky/ffe-apm-span-enrichment
pavlokhrebto Jul 10, 2026
89686f3
Merge branch 'master' of github.com:DataDog/dd-trace-php into leo.rom…
pavlokhrebto Jul 14, 2026
265422f
fix(ffe): fix span-enrichment tag leak, truncation, and reset-order b…
pavlokhrebto Jul 15, 2026
fcda2fa
fix(ffe): align root-drop test with proven refcount shape so it hits …
pavlokhrebto Jul 15, 2026
82ffc6a
Fix
pavlokhrebto Jul 15, 2026
92f1b6f
fix(ffe): skip staging without a root, use span_id for root identity,…
pavlokhrebto Jul 16, 2026
bedfcde
fix(ffe): drop redundant require_once in Client and correct misleadin…
pavlokhrebto Jul 18, 2026
b6dddb9
Merge branch 'master' into leo.romanovsky/ffe-apm-span-enrichment
pavlokhrebto Jul 21, 2026
f3a7178
Merge branch 'master' into leo.romanovsky/ffe-apm-span-enrichment
pavlokhrebto Jul 23, 2026
8096369
fix(config): bump ZAI_CONFIG_ENTRIES_COUNT_MAX to 320
pavlokhrebto Jul 23, 2026
fea9fd5
fix(ffe): narrow FfeResult.serial_id from i64 to i32
pavlokhrebto Jul 23, 2026
e2468b7
refactor(ffe): attach span enrichment to root span via ObjectKVStore
pavlokhrebto Jul 23, 2026
dd06af9
refactor(ffe): remove native span-enrichment staging layer
pavlokhrebto Jul 23, 2026
1738ca1
test(ffe): cover span-enrichment recording against real root spans
pavlokhrebto Jul 23, 2026
2b92832
docs(ffe): refresh span-enrichment comments for the ObjectKVStore design
pavlokhrebto Jul 23, 2026
2cb81e1
test(ffe): fix span-enrichment .phpt for pecl + concurrent roots
pavlokhrebto Jul 23, 2026
8e240c0
Merge branch 'master' into leo.romanovsky/ffe-apm-span-enrichment
pavlokhrebto Jul 23, 2026
92f0115
refactor(ffe): address review — typed args, dirty-guarded tag writes,…
pavlokhrebto Jul 24, 2026
c1e430a
Merge branch 'leo.romanovsky/ffe-apm-span-enrichment' of github.com:D…
pavlokhrebto Jul 24, 2026
bf321e6
test(ffe): run span-enrichment .phpt under the PHPUnit featureflags s…
pavlokhrebto Jul 24, 2026
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
2 changes: 2 additions & 0 deletions components-rs/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,8 @@ typedef struct ddog_FfeResult {
_zend_string * allocation_key;
int32_t reason;
int32_t error_code;
int32_t serial_id;
bool has_serial_id;
bool do_log;
bool valid;
} ddog_FfeResult;
Expand Down
14 changes: 14 additions & 0 deletions components-rs/ffe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ pub struct FfeResult {
pub allocation_key: MaybeOwnedZendString,
pub reason: i32,
pub error_code: i32,
// serial_id is the selected split's serial id, carried for APM span

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

had no idea the i32 would have such knock-on complications. :(

// enrichment (ffe_flags_enc). The source field is Option<i32>; since the
// C ABI cannot represent Option<i32> as a plain field, we surface the
// presence separately via has_serial_id. Consumers MUST gate on
// has_serial_id (the Pattern B "missing variant => default" semantic) and
// never treat serial_id == 0 as "absent".

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

pub serial_id: i32,
pub has_serial_id: bool,
pub do_log: bool,
pub valid: bool,
}
Expand Down Expand Up @@ -220,6 +228,8 @@ fn result_from_assignment(assignment: Result<ffe::Assignment, EvaluationError>)
AssignmentReason::Default => REASON_DEFAULT,
},
error_code: ERROR_NONE,
serial_id: assignment.serial_id.unwrap_or(0),
has_serial_id: assignment.serial_id.is_some(),
do_log: assignment.do_log,
valid: true,
}
Expand All @@ -244,6 +254,8 @@ fn result_from_assignment(assignment: Result<ffe::Assignment, EvaluationError>)
allocation_key: None,
reason,
error_code,
serial_id: 0,
has_serial_id: false,
do_log: false,
valid: true,
}
Expand All @@ -258,6 +270,8 @@ fn invalid_result() -> FfeResult {
allocation_key: None,
reason: REASON_ERROR,
error_code: ERROR_GENERAL,
serial_id: 0,
has_serial_id: false,
do_log: false,
valid: false,
}
Expand Down
3 changes: 2 additions & 1 deletion ext/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ enum datadog_sidecar_connection_mode {
CONFIG(DOUBLE, DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS, "5.0", .ini_change = zai_config_system_ini_change) \
CONFIG(BOOL, DD_REMOTE_CONFIG_ENABLED, "true", .ini_change = zai_config_system_ini_change) \
CONFIG(INT, DD_TRACE_RETRY_INTERVAL, "100", .ini_change = zai_config_system_ini_change) \
CONFIG(BOOL, DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED, "true")
CONFIG(BOOL, DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED, "true") \
CONFIG(BOOL, DD_EXPERIMENTAL_FLAGGING_PROVIDER_SPAN_ENRICHMENT_ENABLED, "false")

#define DD_CONFIGURATIONS_ONLY
#ifdef DDTRACE
Expand Down
7 changes: 7 additions & 0 deletions metadata/supported-configurations.json
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,13 @@
"default": "false"
}
],
"DD_EXPERIMENTAL_FLAGGING_PROVIDER_SPAN_ENRICHMENT_ENABLED": [
{
"implementation": "A",
"type": "boolean",
"default": "false"
}
],
"DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED": [
{
"implementation": "B",
Expand Down
5 changes: 4 additions & 1 deletion src/DDTrace/OpenFeature/DataDogProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,15 @@ private function resolve(
mixed $defaultValue,
?EvaluationContext $context
): ResolutionDetailsInterface {
$details = $this->evaluate($flagKey, $expectedType, $defaultValue, $this->normalizeContext($context));
$normalizedContext = $this->normalizeContext($context);
$details = $this->evaluate($flagKey, $expectedType, $defaultValue, $normalizedContext);
$this->warnIfNonProductionRuntime($details);
// The PHP OpenFeature SDK does not pass ResolutionDetails to finally
// hooks, so PHP records metrics here after native evaluation has the
// final provider result.
$this->recordEvaluationMetric($flagKey, $details);
// APM span enrichment is recorded inside NativeEvaluator::evaluate()
// (the shared choke point), so there is nothing to do here.

$builder = (new ResolutionDetailsBuilder())
->withValue($details->getValue())
Expand Down
13 changes: 12 additions & 1 deletion src/api/FeatureFlags/Internal/NativeEvaluator.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace DDTrace\FeatureFlags\Internal;

use DDTrace\FeatureFlags\EvaluationType;
use DDTrace\FeatureFlags\SpanEnrichmentRegistry;

final class NativeEvaluator implements Evaluator
{
Expand Down Expand Up @@ -50,7 +51,17 @@ public function evaluate(
$rawResult = $this->withProviderState($rawResult);
}

return $this->mapper->map($rawResult, $expectedType, $defaultValue);
$details = $this->mapper->map($rawResult, $expectedType, $defaultValue);

// APM feature-flag span enrichment. This is the single choke point both
// the native Client and the OpenFeature DataDogProvider evaluate through,
// and it is only reachable with the extension loaded (UnavailableEvaluator
// is returned otherwise), so enrichment is recorded here once rather than
// duplicated in each caller. No-op when the gate is off or there is no
// active root span; never throws into evaluation.
SpanEnrichmentRegistry::record($flagKey, $details, $targetingKey);

return $details;
}

private function typeId($expectedType)
Expand Down
13 changes: 13 additions & 0 deletions src/api/FeatureFlags/Internal/ResultMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,19 @@ private function exposureData($rawResult)
$exposureData['doLog'] = (bool) $this->read($rawResult, array('do_log', 'doLog'), false);
}

// serialId is the selected split's serial id, surfaced from the native
// bridge for APM span enrichment. It is only present when the native
// result actually carried one; a null/absent value must be left out
// entirely rather than defaulted to 0/false. Absence alone does NOT
// mean a runtime default was used -- a waterfall-assigned variant can
// also have no serial id. SpanEnrichmentRegistry::record() only
// treats an evaluation as a runtime default when BOTH serialId AND the
// variant are absent.
$serialId = $this->read($rawResult, array('serial_id', 'serialId'), null);
if ($serialId !== null) {
$exposureData['serialId'] = (int) $serialId;
}

return $exposureData;
}

Expand Down
Loading
Loading