Skip to content

feat: lambda support for DSM - #622

Merged
michael-zhao459 merged 60 commits into
mainfrom
michael.zhao/dsm-lambda
Jul 9, 2025
Merged

feat: lambda support for DSM#622
michael-zhao459 merged 60 commits into
mainfrom
michael.zhao/dsm-lambda

Conversation

@michael-zhao459

@michael-zhao459michael-zhao459 commented Jun 20, 2025

Copy link
Copy Markdown
Contributor

What does this PR do?

This PR adds lambda support for Data Streams Monitoring (DSM) and reworks the original implementation.

  1. DSM context is passed through the trace propagation headers, code is refactored to use existing extraction logic (deleted dsm.py, reinventing the wheel here).

  2. If DSM is enabled, add custom DSM logic to the extracted context afterwards

Motivation

Remove redundant code. DSM customers wanted to have Lambda support, currently context is not propagated correctly with lambdas.

Testing Guidelines

Test caseExpected outcomeSQSSNSSNS -> SQS (arn from SQS is used)Kinesis
Datadog Context propagated properly through stringValueData streams context propagated & data streams checkpoint setDNE
Datadog Context propagated properly through binaryValueData streams context propagated & data streams checkpoint set
No _datadog message attributeCheckpoint set, no context propagation
Empty datadog message attributeCheckpoint set, no context propagation
No data streams context in _datadog message attributeCheckpoint set, no context propagation
Invalid datadog message attributeCheckpoint set, no context propagation, debug logger called
source_arn is not foundNo checkpoint set
Data streams disabledNo checkpoint set

The tests go through all of the SQS case first, then all of the SNS case, then all of the SNS->SQS case, then all of the Kinesis case

Additional Notes

Types of Changes

  • Bug fix
  • New feature
  • Breaking change
  • Misc (docs, refactoring, dependency upgrade, etc.)

Check all that apply

  • This PR's description is comprehensive
  • This PR contains breaking changes that are documented in the description
  • This PR introduces new APIs or parameters that are documented and unlikely to change in the foreseeable future
  • This PR impacts documentation, and it has been updated (or a ticket has been logged)
  • This PR's changes are covered by the automated tests
  • This PR collects user input/sensitive content into Datadog
  • This PR passes the integration tests (ask a Datadog member to run the tests)

Comment threaddatadog_lambda/tracing.py Outdated
Comment threaddatadog_lambda/tracing.py Outdated
Comment threaddatadog_lambda/tracing.py Outdated
Comment threaddatadog_lambda/tracing.py Outdated
Comment threaddatadog_lambda/tracing.py Outdated
Comment threaddatadog_lambda/tracing.py Outdated
Comment threaddatadog_lambda/tracing.py Outdated
Comment threaddatadog_lambda/tracing.py
Comment threadtests/test_dsm.py Outdated
Comment threadtests/test_dsm.py Outdated
Comment threaddatadog_lambda/tracing.py Outdated
Comment threaddatadog_lambda/tracing.py Outdated
Comment threaddatadog_lambda/tracing.py Outdated
Comment threaddatadog_lambda/wrapper.py Outdated
Comment threaddatadog_lambda/tracing.py Outdated
Comment threaddatadog_lambda/tracing.py Outdated
Comment threaddatadog_lambda/tracing.py Outdated
Comment threaddatadog_lambda/tracing.py Outdated
Comment threaddatadog_lambda/tracing.py Outdated
Comment threaddatadog_lambda/tracing.py Outdated
Comment threaddatadog_lambda/tracing.py Outdated
Comment threaddatadog_lambda/tracing.py Outdated
if config.data_streams_enabled:
from ddtrace.data_streams import PROPAGATION_KEY_BASE_64

data_streams_ctx = {

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I know the else is redundant but datadog gets mad if i just do the if too many indents

Comment threaddatadog_lambda/tracing.py Outdated
except Exception as e:
logger.debug("The trace extractor returned with error %s", e)
return extract_context_from_lambda_context(lambda_context)
return extract_context_from_lambda_context(lambda_context), None

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

we should not return None here

@michael-zhao459
michael-zhao459 marked this pull request as ready for review June 25, 2025 13:25
@michael-zhao459
michael-zhao459 requested review from a team as code ownersJune 25, 2025 13:25
Comment threaddatadog_lambda/tracing.py Outdated

data_streams_ctx = {}
if config.data_streams_enabled:
from ddtrace.data_streams import PROPAGATION_KEY_BASE_64

@joeyzhao2018joeyzhao2018Jun 26, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

My main concerns are

  1. Creating dictionary objects and bound methods for every invocation is inefficient.
  2. It is very hard to follow the logic and hard to maintain and may introduce unexpected behaviors that are hard to debug in the future.

May I suggest the following alternative implementation? Let me know what do you think.

def_create_dsm_carrier_func(dd_data):
"""Create a carrier function for DSM context extraction."""defcarrier_get(key):
returndd_data.get(key) ifdd_dataelseNonereturncarrier_get# then in In the extraction functions:ifconfig.data_streams_enabled:
dsm_carrier=_create_dsm_carrier_func(dd_data) # Pass the original dd_dataelse:
dsm_carrier=None

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I agree with the justifications you made for this change will change the code now!

Comment threaddatadog_lambda/tracing.py Outdated
@joeyzhao2018
joeyzhao2018 self-requested a review June 26, 2025 14:02

@joeyzhao2018joeyzhao2018 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@joeyzhao2018joeyzhao2018 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

Comment threaddatadog_lambda/wrapper.py Outdated
@michael-zhao459
michael-zhao459force-pushed the michael.zhao/dsm-lambda branch from 66fdbb3 to c4fa49bCompareJuly 3, 2025 19:43
Comment threadpyproject.toml Outdated
datadog = ">=0.51.0,<1.0.0"
wrapt = "^1.11.2"
ddtrace = ">=2.20.0,<4"
ddtrace = ">=3.10.0"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

this changes the major version. Is that what we want to do? Also, should we keep <4?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

My mistake on the <4. The code will break without ddtrace version 3.10.0.

Comment threadtests/test_tracing.py Outdated
)

@patch("datadog_lambda.tracing._dsm_set_checkpoint")
def test_sqs_incorrect_datadog_message_attribute(self, mock_dsm_set_checkpoint):

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nit: incorrect -> invalid

Comment threadtests/test_tracing.py Outdated

@patch("datadog_lambda.tracing._dsm_set_checkpoint")
@patch("datadog_lambda.tracing.logger")
def test_sqs_invalid_datadog_message_attribute_raises_exception(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nit: remove raises_exception from the test name. We care about what the test case tests, not the logic under the hood. Maybe in the future, the code won't raise an exception, and it's still OK because the function accepts invalid datadog message attributes.

Comment threadtests/test_tracing.py Outdated
event, self.lambda_context, parse_event_source(event)
)

mock_dsm_set_checkpoint.assert_called_once_with(None, "sqs", "")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I don't understand, are we testing mock_dsm_set_checkpoint, or self.mock_checkpoint. We should test only one of them, and it should be consistent across all tests. If possible, it's better to test the lower level one (self.mock_checkpoint)

Comment threadpyproject.toml Outdated
datadog = ">=0.51.0,<1.0.0"
wrapt = "^1.11.2"
ddtrace = ">=2.20.0,<4"
ddtrace = ">=3.10.0,<4"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We're currently working on a v3.10.2 release, once that's out, we should update this.

Comment threadpyproject.toml
datadog = ">=0.51.0,<1.0.0"
wrapt = "^1.11.2"
ddtrace = ">=2.20.0,<4"
ddtrace = ">=3.10.2,<4"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should be good to release this now that v3.10.2 is up in pypi.

@michael-zhao459
michael-zhao459 merged commit d2a195e into mainJul 9, 2025
61 checks passed
@michael-zhao459
michael-zhao459 deleted the michael.zhao/dsm-lambda branch July 9, 2025 21:31
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@michael-zhao459@purple4reina@joeyzhao2018@piochelepiotr