Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Oversampling Mitigation #541

Merged
merged 12 commits into from
Nov 10, 2022
Merged

Conversation

carolabadeer
Copy link
Contributor

Issue #, if available: N/A

Description of changes: Implemented oversampling mitigation - added methods to create subsegments without sampling (control sampling decisions at the subsegment level) and an SQS message helper class

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@carolabadeer carolabadeer requested a review from a team as a code owner October 14, 2022 23:39
@codecov-commenter
Copy link

codecov-commenter commented Oct 14, 2022

Codecov Report

Merging #541 (e99c271) into master (a1f4c79) will increase coverage by 0.36%.
The diff coverage is n/a.

❗ Current head e99c271 differs from pull request most recent head 95df8a6. Consider uploading reports for the commit 95df8a6 to get more accurate results

@@            Coverage Diff             @@
##           master     #541      +/-   ##
==========================================
+ Coverage   82.36%   82.73%   +0.36%     
==========================================
  Files          36       37       +1     
  Lines        1758     1784      +26     
==========================================
+ Hits         1448     1476      +28     
+ Misses        310      308       -2     
Impacted Files Coverage Δ
lib/env/sqs_message_helper.js 83.33% <0.00%> (ø)
lib/patchers/aws_p.js 80.26% <0.00%> (+0.53%) ⬆️
lib/segments/segment.js 80.81% <0.00%> (+1.05%) ⬆️
lib/segments/attributes/subsegment.js 79.26% <0.00%> (+1.34%) ⬆️
lib/env/aws_lambda.js 91.52% <0.00%> (+1.69%) ⬆️
lib/patchers/http_p.js 94.31% <0.00%> (+2.18%) ⬆️

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

@@ -81,7 +81,8 @@ function captureAWSRequest(req) {

var buildListener = function(req) {
req.httpRequest.headers['X-Amzn-Trace-Id'] = 'Root=' + traceId + ';Parent=' + subsegment.id +
';Sampled=' + (subsegment.segment.notTraced ? '0' : '1');
';Sampled=' + (subsegment.isSampled ? '1' : '0');
Copy link
Contributor

@atshaw43 atshaw43 Oct 17, 2022

Choose a reason for hiding this comment

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

Should we stick with the variable name notTraced since customers are expecting that on segment?

Copy link
Contributor

Choose a reason for hiding this comment

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

+1

@@ -286,7 +304,7 @@ Subsegment.prototype.close = function close(err, remote) {
}

if (root && root.counter > SegmentUtils.getStreamingThreshold()) {
if (this.streamSubsegments() && this.parent) {
if (this.streamSubsegments() && this.parent) { // this.isSampled ??
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems this comment needs to be taken out?

]
}

it('testing sqsmessagehelper', function(){
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's also have a test for when it doesn't have the sampled flag present.

@@ -38,10 +39,24 @@ Subsegment.prototype.init = function init(name) {

Subsegment.prototype.addNewSubsegment = function addNewSubsegment(name) {
var subsegment = new Subsegment(name);
subsegment.isSampled = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

We want to set this to the parent's value, not a default true.
It is possible the upstream call was not sampled. This will create an orphaned segment since its parent was never sent to the backend.

Copy link
Contributor Author

@carolabadeer carolabadeer Oct 19, 2022

Choose a reason for hiding this comment

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

Great callout! I believe checking the value of subsegment.segment.notTraced would be more accurate in this case as to capture the parent segment's sampling decision not the parent subsegment (since it is possible to have subsegments of subsegments).

@@ -262,6 +262,57 @@ describe('Segment', function() {
});
});

describe('#addSubsegmentWithoutSampling', function (){
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice set of tests!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you!

})

it('should not sample subsegment or subsegment of subsegment - mix', function(){
var segment = new Segment('parent');
Copy link
Contributor

@atshaw43 atshaw43 Oct 17, 2022

Choose a reason for hiding this comment

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

Us needing to create the segment here signals that we re not running through the lambda specific code.

When the code thinks it is in lambda it will create a facade segment for us. The facade segment's trace data is filled out by what is in this variable. You can see an example in aws_lambda.test.js

process.env._X_AMZN_TRACE_ID
packages/core/test/unit/env/aws_lambda.test.js

I believe you need to call "init" here with that variable set.
packages/core/lib/env/aws_lambda.js

Let me know if you need more context here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for pointing that out! I've incorporated that change into the updated unit tests

Copy link
Contributor Author

@carolabadeer carolabadeer Oct 19, 2022

Choose a reason for hiding this comment

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

To answer the question above, yes, this was tested in Lambda using the sample sample JSON data used in the Java oversampling mitigation PR.

For trace header with sampled=1:
image (3)

For trace header with sampled=0:
image (4)

@atshaw43
Copy link
Contributor

Was this tested in Lambda directly?

assert.equal(child3.isSampled, false);
})
});

Copy link
Contributor

Choose a reason for hiding this comment

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

After we close the segment, is it possible to check if the emitter sent out the segment or not?
And can we examine the trace header of the subsegment that is sent downstream to see what the sample flag is set to?

@@ -81,7 +81,8 @@ function captureAWSRequest(req) {

var buildListener = function(req) {
req.httpRequest.headers['X-Amzn-Trace-Id'] = 'Root=' + traceId + ';Parent=' + subsegment.id +
';Sampled=' + (subsegment.segment.notTraced ? '0' : '1');
';Sampled=' + (subsegment.isSampled ? '1' : '0');
Copy link
Contributor

Choose a reason for hiding this comment

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

+1

};

Subsegment.prototype.addNewSubsegmentWithoutSampling = function addNewSubsegmentWithoutSampling(name){
var subsegment = new Subsegment(name);
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: use const

constructor() {
}

isSampled(message) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we make this static? Would need to update docs as well

@@ -42,11 +42,11 @@ var contextUtils = {
* @alias module:context_utils.resolveManualSegmentParams
*/

resolveManualSegmentParams: function resolveManualSegmentParams(params) {
resolveManualSegmentParams: function resolveManualSegmentParams (params) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Confirmed with Carol,
The linter has not been run in a long time which is causing the large code churn.

@@ -110,15 +110,21 @@ const getXRayMiddleware = (config: RegionResolvedConfig, manualSegment?: Segment
return next(args);
}

const subsegment: Subsegment = segment.addNewSubsegment(service);
let subsegment: Subsegment;
if (segment.notTraced == false || !segment.subsegments[segment.subsegments.length - 1].notTraced) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Would this also work?
if (!segment.subsegments[segment.subsegments.length - 1].notTraced)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is more of a design decision: check the parent sampling decision here for unsampled segments, or only check the subsegment and rely on the emitter not sending the entire segment if it is unsampled down the line. If the latter was used in the other SDKs, I am happy to incorporate the same design here as well.


var buildListener = function(req) {
let subsegment;
if (parent.notTraced === false || !parent.subsegments[parent.subsegments.length - 1].notTraced) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here. Can we just look at the subsegment and not have the segment check?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See above

awsClient.call(awsRequest);

setTimeout(function () {
logStub.should.have.been.calledOnce;
Copy link
Contributor

Choose a reason for hiding this comment

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

If it is not supposed to sample, then should this have emitted?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I believe this is unrelated - just checking if a log is produced. There is another test right below checking the trace header being sent out

@carolabadeer carolabadeer force-pushed the oversampling-mitigation branch from e99c271 to 95df8a6 Compare November 9, 2022 07:09
Copy link
Contributor

@willarmiros willarmiros left a comment

Choose a reason for hiding this comment

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

LGTM - please wait for @atshaw43 review too

Sign up for free to 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