Repository: aws/aws-xray-sdk-node
Pull request #79 (2018-12-04 22:44:50)

fix: centralized sampling now respects fixed rate of 0
*Description of changes:*
This change allows for a fixed rate of `0` to be fetched from the centralized sampling. This enables setting up sampling rules where sampling can be disabled for some scenarios.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.



Patch:
diff --git a/packages/core/lib/middleware/sampling/service_connector.js b/packages/core/lib/middleware/sampling/service_connector.js
index 9343f4d..199ae8d 100644
--- a/packages/core/lib/middleware/sampling/service_connector.js
+++ b/packages/core/lib/middleware/sampling/service_connector.js
@@ -1,6 +1,5 @@
 var crypto = require('crypto');
-var AWS = require('aws-sdk/global');
-var Xray = require('aws-sdk/clients/xray');
+var AWS = require('aws-sdk');
 var logger = require('../../logger');
 var SamplingRule = require('./sampling_rule');
 var DaemonConfig = require('../../daemon_config')
@@ -17,7 +16,7 @@ var ServiceConnector = {
   // identifying the SDK instance and is generated during SDK initialization/
   // This is required when reporting sampling to X-Ray back-end.
   clientId: crypto.randomBytes(12).toString('hex'),
-  client: new Xray({endpoint: util.format('http://%s:%d', DaemonConfig.tcp_ip, DaemonConfig.tcp_port)}),
+  client: new AWS.XRay({endpoint: util.format('http://%s:%d', DaemonConfig.tcp_ip, DaemonConfig.tcp_port)}),
 
   fetchSamplingRules: function fetchSamplingRules(callback) {
 
@@ -116,7 +115,7 @@ var isRuleValid = function isRuleValid(record) {
     && record['ServiceType']
     && record['RuleName']
     && record['Priority']
-    && record['FixedRate'];
+    && typeof record['FixedRate'] == 'number';
 };
 
 var dateToEpoch = function dateToEpoch(date) {


Test patch:
diff --git a/packages/core/test/unit/sampling/service_connector.test.js b/packages/core/test/unit/sampling/service_connector.test.js
new file mode 100644
index 0000000..9e8fee1
--- /dev/null
+++ b/packages/core/test/unit/sampling/service_connector.test.js
@@ -0,0 +1,153 @@
+var assert = require('chai').assert;
+var chai = require('chai');
+var sinon = require('sinon');
+
+var ServiceConnector = require('../../../lib/middleware/sampling/service_connector');
+
+chai.should();
+chai.use(require('sinon-chai'));
+
+function generateMockClient(samplingRules) {
+  return {
+    makeUnauthenticatedRequest: function(_, _, callback) {
+      callback(null, {
+        "SamplingRuleRecords": samplingRules,
+        "NextToken": null
+      });
+    }
+  };
+}
+
+describe('ServiceConnector', function() {
+  var sandbox;
+  this.beforeEach(function() {
+    sandbox = sinon.sandbox.create();
+  });
+
+  this.afterEach(function() {
+    sandbox.restore();
+  });
+
+  describe('fetchSamplingRules', function() {
+    var defaultSamplingRule = {
+      "SamplingRule": {
+        "RuleName": "Default",
+        "RuleARN": "arn:aws:xray:us-west-2:0000000000:sampling-rule/Default",
+        "ResourceARN": "*",
+        "Priority": 10000,
+        "FixedRate": 0.05,
+        "ReservoirSize": 1,
+        "ServiceName": "*",
+        "ServiceType": "*",
+        "Host": "*",
+        "HTTPMethod": "*",
+        "URLPath": "*",
+        "Version": 1,
+        "Attributes": {}
+      },
+      "CreatedAt": "1970-01-01T00:00:00.000Z",
+      "ModifiedAt": "1970-01-01T00:00:00.000Z"
+    };
+
+    var noSamplingRule = {
+      "SamplingRule": {
+        "RuleName": "no-sampling",
+        "RuleARN": "arn:aws:xray:us-west-2:00000000:sampling-rule/no-sampling",
+        "ResourceARN": "*",
+        "Priority": 1,
+        "FixedRate": 0,
+        "ReservoirSize": 0,
+        "ServiceName": "*",
+        "ServiceType": "*",
+        "Host": "*",
+        "HTTPMethod": "*",
+        "URLPath": "/foo",
+        "Version": 1,
+        "Attributes": {}
+      },
+      "CreatedAt": "1970-01-01T00:00:00.000Z",
+      "ModifiedAt": "1970-01-01T00:00:00.000Z"
+    };
+
+    var invalidRule = {
+      "SamplingRule": {
+        "RuleName": "invalid-rule",
+        "RuleARN": "arn:aws:xray:us-west-2:00000000:sampling-rule/invalid-rule",
+        "ResourceARN": "*",
+        "Priority": 1,
+        "FixedRate": 0,
+        "ReservoirSize": 0,
+        "ServiceName": "*",
+        "Host": "*",
+        "HTTPMethod": "*",
+        "URLPath": "/foo",
+        "Version": 1,
+        "Attributes": {}
+      },
+      "CreatedAt": "1970-01-01T00:00:00.000Z",
+      "ModifiedAt": "1970-01-01T00:00:00.000Z"
+    };
+
+    it('filters invalid rules', function(done) {
+      sandbox.stub(ServiceConnector, 'client').value(generateMockClient([
+        noSamplingRule,
+        invalidRule,
+        defaultSamplingRule
+      ]));
+
+      ServiceConnector.fetchSamplingRules(function(rules) {
+        // should contain 2 rules
+        assert.include(rules[0], {
+          name: noSamplingRule.SamplingRule.RuleName,
+          priority: noSamplingRule.SamplingRule.Priority,
+          rate: noSamplingRule.SamplingRule.FixedRate,
+          host: noSamplingRule.SamplingRule.Host,
+          httpMethod: noSamplingRule.SamplingRule.HTTPMethod,
+          urlPath: noSamplingRule.SamplingRule.URLPath,
+          serviceName: noSamplingRule.SamplingRule.ServiceName,
+          serviceType: noSamplingRule.SamplingRule.ServiceType,
+          borrow: false
+        });
+
+        // default rule should be last
+        assert.include(rules[1], {
+          name: defaultSamplingRule.SamplingRule.RuleName,
+          priority: defaultSamplingRule.SamplingRule.Priority,
+          rate: defaultSamplingRule.SamplingRule.FixedRate,
+          host: defaultSamplingRule.SamplingRule.Host,
+          httpMethod: defaultSamplingRule.SamplingRule.HTTPMethod,
+          urlPath: defaultSamplingRule.SamplingRule.URLPath,
+          serviceName: defaultSamplingRule.SamplingRule.ServiceName,
+          serviceType: defaultSamplingRule.SamplingRule.ServiceType,
+          borrow: true
+        });
+
+        done();
+      });
+    });
+
+    it('respects a fixed rate of 0', function(done) {
+      sandbox.stub(ServiceConnector, 'client').value(generateMockClient([
+        noSamplingRule,
+        defaultSamplingRule
+      ]));
+
+      ServiceConnector.fetchSamplingRules(function(rules) {
+        assert.deepEqual(rules.length, 2);
+        assert.include(rules[0], {
+          name: noSamplingRule.SamplingRule.RuleName,
+          priority: noSamplingRule.SamplingRule.Priority,
+          rate: 0,
+          host: noSamplingRule.SamplingRule.Host,
+          httpMethod: noSamplingRule.SamplingRule.HTTPMethod,
+          urlPath: noSamplingRule.SamplingRule.URLPath,
+          serviceName: noSamplingRule.SamplingRule.ServiceName,
+          serviceType: noSamplingRule.SamplingRule.ServiceType,
+          borrow: false
+        });
+
+        done();
+      });
+    });
+  });
+});
\ No newline at end of file