Skip to content

Commit 164b3bb

Browse files
authored
Merge pull request #429 from majanjua-amzn/master
[Lambda] Create dummy segment when trace header is incomplete
2 parents a6a3e86 + d174f8d commit 164b3bb

File tree

2 files changed

+49
-17
lines changed

2 files changed

+49
-17
lines changed

aws_xray_sdk/core/lambda_launcher.py

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import threading
44

55
from aws_xray_sdk import global_sdk_config
6+
from .models.dummy_entities import DummySegment
67
from .models.facade_segment import FacadeSegment
78
from .models.trace_header import TraceHeader
89
from .context import Context
@@ -44,7 +45,7 @@ class LambdaContext(Context):
4445
"""
4546
Lambda service will generate a segment for each function invocation which
4647
cannot be mutated. The context doesn't keep any manually created segment
47-
but instead every time ``get_trace_entity()`` gets called it refresh the facade
48+
but instead every time ``get_trace_entity()`` gets called it refresh the
4849
segment based on environment variables set by Lambda worker.
4950
"""
5051
def __init__(self):
@@ -65,12 +66,12 @@ def end_segment(self, end_time=None):
6566

6667
def put_subsegment(self, subsegment):
6768
"""
68-
Refresh the facade segment every time this function is invoked to prevent
69+
Refresh the segment every time this function is invoked to prevent
6970
a new subsegment from being attached to a leaked segment/subsegment.
7071
"""
7172
current_entity = self.get_trace_entity()
7273

73-
if not self._is_subsegment(current_entity) and current_entity.initializing:
74+
if not self._is_subsegment(current_entity) and (getattr(current_entity, 'initializing', None) or isinstance(current_entity, DummySegment)):
7475
if global_sdk_config.sdk_enabled():
7576
log.warning("Subsegment %s discarded due to Lambda worker still initializing" % subsegment.name)
7677
return
@@ -99,9 +100,9 @@ def get_trace_entity(self):
99100

100101
def _refresh_context(self):
101102
"""
102-
Get current facade segment. To prevent resource leaking in Lambda worker,
103+
Get current segment. To prevent resource leaking in Lambda worker,
103104
every time there is segment present, we compare its trace id to current
104-
environment variables. If it is different we create a new facade segment
105+
environment variables. If it is different we create a new segment
105106
and clean up subsegments stored.
106107
"""
107108
header_str = os.getenv(LAMBDA_TRACE_HEADER_KEY)
@@ -136,8 +137,8 @@ def handle_context_missing(self):
136137

137138
def _initialize_context(self, trace_header):
138139
"""
139-
Create a facade segment based on environment variables
140-
set by AWS Lambda and initialize storage for subsegments.
140+
Create a segment based on environment variables set by
141+
AWS Lambda and initialize storage for subsegments.
141142
"""
142143
sampled = None
143144
if not global_sdk_config.sdk_enabled():
@@ -148,12 +149,17 @@ def _initialize_context(self, trace_header):
148149
elif trace_header.sampled == 1:
149150
sampled = True
150151

151-
segment = FacadeSegment(
152-
name='facade',
153-
traceid=trace_header.root,
154-
entityid=trace_header.parent,
155-
sampled=sampled,
156-
)
152+
segment = None
153+
if not trace_header.root or not trace_header.parent or trace_header.sampled is None:
154+
segment = DummySegment()
155+
log.debug("Creating NoOp/Dummy parent segment")
156+
else:
157+
segment = FacadeSegment(
158+
name='facade',
159+
traceid=trace_header.root,
160+
entityid=trace_header.parent,
161+
sampled=sampled,
162+
)
157163
segment.save_origin_trace_header(trace_header)
158164
setattr(self._local, 'segment', segment)
159165
setattr(self._local, 'entities', [])

tests/test_lambda_context.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from aws_xray_sdk import global_sdk_config
44
import pytest
55
from aws_xray_sdk.core import lambda_launcher
6+
from aws_xray_sdk.core.models.dummy_entities import DummySegment
67
from aws_xray_sdk.core.models.subsegment import Subsegment
78

89

@@ -67,23 +68,48 @@ def test_disable():
6768

6869

6970
def test_non_initialized():
70-
# Context that hasn't been initialized by lambda container should not add subsegments to the facade segment.
71+
# Context that hasn't been initialized by lambda container should not add subsegments to the dummy segment.
7172
temp_header_var = os.environ[lambda_launcher.LAMBDA_TRACE_HEADER_KEY]
7273
del os.environ[lambda_launcher.LAMBDA_TRACE_HEADER_KEY]
7374

7475
temp_context = lambda_launcher.LambdaContext()
75-
facade_segment = temp_context.get_trace_entity()
76-
subsegment = Subsegment("TestSubsegment", "local", facade_segment)
76+
dummy_segment = temp_context.get_trace_entity()
77+
subsegment = Subsegment("TestSubsegment", "local", dummy_segment)
7778
temp_context.put_subsegment(subsegment)
7879

79-
assert temp_context.get_trace_entity() == facade_segment
80+
assert temp_context.get_trace_entity() == dummy_segment
8081

8182
# "Lambda" container added metadata now. Should see subsegment now.
83+
# The following put_segment call will overwrite the dummy segment in the context with an intialized facade segment that accepts a subsegment.
8284
os.environ[lambda_launcher.LAMBDA_TRACE_HEADER_KEY] = temp_header_var
8385
temp_context.put_subsegment(subsegment)
8486

8587
assert temp_context.get_trace_entity() == subsegment
8688

89+
def test_lambda_passthrough():
90+
# Hold previous environment value
91+
temp_header_var = os.environ[lambda_launcher.LAMBDA_TRACE_HEADER_KEY]
92+
del os.environ[lambda_launcher.LAMBDA_TRACE_HEADER_KEY]
93+
94+
# Set header to lambda passthrough style header
95+
os.environ[lambda_launcher.LAMBDA_TRACE_HEADER_KEY] = "Root=%s;Lineage=10:1234abcd:3" % TRACE_ID
96+
97+
temp_context = lambda_launcher.LambdaContext()
98+
dummy_segment = temp_context.get_trace_entity()
99+
subsegment = Subsegment("TestSubsegment", "local", dummy_segment)
100+
temp_context.put_subsegment(subsegment)
101+
102+
# Resulting entity is not the same dummy segment, so simply check that it is a dummy segment
103+
assert isinstance(temp_context.get_trace_entity(), DummySegment)
104+
105+
# Reset header value and ensure behaviour returns to normal
106+
del os.environ[lambda_launcher.LAMBDA_TRACE_HEADER_KEY]
107+
os.environ[lambda_launcher.LAMBDA_TRACE_HEADER_KEY] = temp_header_var
108+
temp_context.put_subsegment(subsegment)
109+
110+
assert temp_context.get_trace_entity() == subsegment
111+
112+
87113

88114
def test_set_trace_entity():
89115
segment = context.get_trace_entity()

0 commit comments

Comments
 (0)