Skip to content

Commit 1738ca1

Browse files
committed
test(ffe): cover span-enrichment recording against real root spans
Add .phpt tests driving SpanEnrichmentRegistry::record() through real root spans: aggregation onto root_span()->meta (flags + hashed subjects + runtime defaults), per-root isolation across concurrent stacks (the case the old single-active-root design couldn't handle), and gate-off no-op.
1 parent dd06af9 commit 1738ca1

3 files changed

Lines changed: 173 additions & 0 deletions

File tree

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
--TEST--
2+
FFE span enrichment: concurrently-open root spans each keep their own tags
3+
--INI--
4+
datadog.trace.generate_root_span=0
5+
datadog.experimental_flagging_provider_span_enrichment_enabled=1
6+
--FILE--
7+
<?php
8+
9+
use DDTrace\FeatureFlags\EvaluationDetails;
10+
use DDTrace\FeatureFlags\EvaluationReason;
11+
use DDTrace\FeatureFlags\EvaluationType;
12+
use DDTrace\FeatureFlags\SpanEnrichmentAccumulator;
13+
use DDTrace\FeatureFlags\SpanEnrichmentRegistry;
14+
15+
$root = getenv('TEST_PHP_SRCDIR');
16+
if (!is_string($root) || $root === '') {
17+
$root = dirname(dirname(dirname(__DIR__)));
18+
}
19+
require_once $root . '/src/DDTrace/Util/ObjectKVStore.php';
20+
foreach (array(
21+
'EvaluationType',
22+
'EvaluationReason',
23+
'EvaluationErrorCode',
24+
'EvaluationDetails',
25+
'SpanEnrichmentAccumulator',
26+
'SpanEnrichmentRegistry',
27+
) as $classFile) {
28+
require_once $root . '/src/api/FeatureFlags/' . $classFile . '.php';
29+
}
30+
31+
function show($label, $value) {
32+
echo $label . '=' . json_encode($value, JSON_UNESCAPED_SLASHES) . "\n";
33+
}
34+
35+
$codec = new SpanEnrichmentAccumulator();
36+
37+
// Root A on the initial stack.
38+
\DDTrace\start_span();
39+
$rootA = \DDTrace\root_span();
40+
$a = new EvaluationDetails('on', EvaluationType::STRING, EvaluationReason::SPLIT, 'a', null, null, array(), array('serialId' => 100, 'doLog' => false));
41+
SpanEnrichmentRegistry::record('flag.a', $a, null);
42+
43+
// Root B on a second, independent stack (models concurrent roots / fibers).
44+
\DDTrace\create_stack();
45+
\DDTrace\start_span();
46+
$rootB = \DDTrace\root_span();
47+
$b = new EvaluationDetails('off', EvaluationType::STRING, EvaluationReason::SPLIT, 'b', null, null, array(), array('serialId' => 200, 'doLog' => false));
48+
SpanEnrichmentRegistry::record('flag.b', $b, null);
49+
50+
// Each root carries only its own evaluation; keying the accumulator on the span
51+
// object makes this correct without any manual root tracking.
52+
show('rootA_flags', $codec->decodeDeltaVarint($rootA->meta['ffe_flags_enc']));
53+
show('rootB_flags', $codec->decodeDeltaVarint($rootB->meta['ffe_flags_enc']));
54+
show('rootA_has_b', in_array(200, $codec->decodeDeltaVarint($rootA->meta['ffe_flags_enc']), true));
55+
show('rootB_has_a', in_array(100, $codec->decodeDeltaVarint($rootB->meta['ffe_flags_enc']), true));
56+
?>
57+
--EXPECT--
58+
rootA_flags=[100]
59+
rootB_flags=[200]
60+
rootA_has_b=false
61+
rootB_has_a=false
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
--TEST--
2+
FFE span enrichment: gate off writes no ffe_* tags onto the root span
3+
--INI--
4+
datadog.experimental_flagging_provider_span_enrichment_enabled=0
5+
--FILE--
6+
<?php
7+
8+
use DDTrace\FeatureFlags\EvaluationDetails;
9+
use DDTrace\FeatureFlags\EvaluationReason;
10+
use DDTrace\FeatureFlags\EvaluationType;
11+
use DDTrace\FeatureFlags\SpanEnrichmentRegistry;
12+
13+
$root = getenv('TEST_PHP_SRCDIR');
14+
if (!is_string($root) || $root === '') {
15+
$root = dirname(dirname(dirname(__DIR__)));
16+
}
17+
require_once $root . '/src/DDTrace/Util/ObjectKVStore.php';
18+
foreach (array(
19+
'EvaluationType',
20+
'EvaluationReason',
21+
'EvaluationErrorCode',
22+
'EvaluationDetails',
23+
'SpanEnrichmentAccumulator',
24+
'SpanEnrichmentRegistry',
25+
) as $classFile) {
26+
require_once $root . '/src/api/FeatureFlags/' . $classFile . '.php';
27+
}
28+
29+
function show($label, $value) {
30+
echo $label . '=' . json_encode($value, JSON_UNESCAPED_SLASHES) . "\n";
31+
}
32+
33+
// With the gate off, record() is a no-op even for a normal split evaluation.
34+
$a = new EvaluationDetails('on', EvaluationType::STRING, EvaluationReason::SPLIT, 'treatment', null, null, array(), array('serialId' => 100, 'doLog' => true));
35+
SpanEnrichmentRegistry::record('flag.a', $a, 'user-1');
36+
37+
$meta = \DDTrace\root_span()->meta;
38+
show('has_flags', isset($meta['ffe_flags_enc']));
39+
show('has_subjects', isset($meta['ffe_subjects_enc']));
40+
show('has_defaults', isset($meta['ffe_runtime_defaults']));
41+
?>
42+
--EXPECT--
43+
has_flags=false
44+
has_subjects=false
45+
has_defaults=false
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
--TEST--
2+
FFE span enrichment: evaluations aggregate onto the active root span's meta
3+
--INI--
4+
datadog.experimental_flagging_provider_span_enrichment_enabled=1
5+
--FILE--
6+
<?php
7+
8+
use DDTrace\FeatureFlags\EvaluationDetails;
9+
use DDTrace\FeatureFlags\EvaluationReason;
10+
use DDTrace\FeatureFlags\EvaluationType;
11+
use DDTrace\FeatureFlags\SpanEnrichmentAccumulator;
12+
use DDTrace\FeatureFlags\SpanEnrichmentRegistry;
13+
14+
$root = getenv('TEST_PHP_SRCDIR');
15+
if (!is_string($root) || $root === '') {
16+
$root = dirname(dirname(dirname(__DIR__)));
17+
}
18+
require_once $root . '/src/DDTrace/Util/ObjectKVStore.php';
19+
foreach (array(
20+
'EvaluationType',
21+
'EvaluationReason',
22+
'EvaluationErrorCode',
23+
'EvaluationDetails',
24+
'SpanEnrichmentAccumulator',
25+
'SpanEnrichmentRegistry',
26+
) as $classFile) {
27+
require_once $root . '/src/api/FeatureFlags/' . $classFile . '.php';
28+
}
29+
30+
function show($label, $value) {
31+
echo $label . '=' . json_encode($value, JSON_UNESCAPED_SLASHES) . "\n";
32+
}
33+
34+
// Three evaluations under the same root span feed the SAME accumulator (keyed on
35+
// the root via ObjectKVStore), so the tags aggregate rather than overwrite:
36+
// - flag.a: split, serial 100, logged subject "user-1"
37+
// - flag.b: split, serial 108, NOT logged (serial recorded, no subject)
38+
// - flag.c: runtime default (no serial, no variant)
39+
$a = new EvaluationDetails('on', EvaluationType::STRING, EvaluationReason::SPLIT, 'treatment', null, null, array(), array('serialId' => 100, 'doLog' => true));
40+
SpanEnrichmentRegistry::record('flag.a', $a, 'user-1');
41+
42+
$b = new EvaluationDetails('blue', EvaluationType::STRING, EvaluationReason::SPLIT, 'blue', null, null, array(), array('serialId' => 108, 'doLog' => false));
43+
SpanEnrichmentRegistry::record('flag.b', $b, 'user-1');
44+
45+
$c = new EvaluationDetails('fallback', EvaluationType::STRING, EvaluationReason::DEFAULT_REASON, null, null, null, array(), array());
46+
SpanEnrichmentRegistry::record('flag.c', $c, null);
47+
48+
$meta = \DDTrace\root_span()->meta;
49+
$codec = new SpanEnrichmentAccumulator();
50+
51+
show('flags', $codec->decodeDeltaVarint($meta['ffe_flags_enc']));
52+
53+
$subjects = json_decode($meta['ffe_subjects_enc'], true);
54+
$subjectKeys = array_keys($subjects);
55+
$subjectVals = array_values($subjects);
56+
show('subject_count', count($subjects));
57+
show('subject_key_is_sha256', (bool) preg_match('/^[0-9a-f]{64}$/', (string) $subjectKeys[0]));
58+
show('subject_ids', $codec->decodeDeltaVarint($subjectVals[0]));
59+
60+
show('defaults', json_decode($meta['ffe_runtime_defaults'], true));
61+
?>
62+
--EXPECT--
63+
flags=[100,108]
64+
subject_count=1
65+
subject_key_is_sha256=true
66+
subject_ids=[100]
67+
defaults={"flag.c":"fallback"}

0 commit comments

Comments
 (0)