Repository: alexcasalboni/aws-lambda-power-tuning
Pull request #81 (2020-05-07 20:33:37)

Implement regional base price selection for AWS Step Functions
Hi,

Addressing #80 
Changes to calculate price for step function use.
Using env containing the price.
Hope this is inline with what was discussed.

Regards,
Eugene


Patch:
diff --git a/lambda/executor.js b/lambda/executor.js
index 4270d2e..4d067b6 100644
--- a/lambda/executor.js
+++ b/lambda/executor.js
@@ -33,7 +33,8 @@ module.exports.handler = async(event, context) => {
     }
 
     // get base cost
-    const baseCost = utils.baseCostForRegion(utils.regionFromARN(lambdaARN));
+    const prices = JSON.parse(process.env.baseCosts);
+    const baseCost = utils.baseCostForRegion(prices, utils.regionFromARN(lambdaARN));
 
     return computeStatistics(baseCost, results, value);
 };
diff --git a/lambda/utils.js b/lambda/utils.js
index dbf9e1f..fdb9850 100644
--- a/lambda/utils.js
+++ b/lambda/utils.js
@@ -6,8 +6,13 @@ const AWS = require('aws-sdk');
 const utils = module.exports;
 
 // cost of 6+N state transitions (AWS Step Functions)
-module.exports.stepFunctionsCost = (nPower) => +(0.000025 * (6 + nPower)).toFixed(5);
+module.exports.stepFunctionsCost = (nPower) => +(this.stepFunctionsBaseCost() * (6 + nPower)).toFixed(5);
 
+module.exports.stepFunctionsBaseCost = () => {
+    const prices = JSON.parse(process.env.sfCosts);
+    // assume the AWS_REGION variable is set for this function
+    return this.baseCostForRegion(prices, process.env.AWS_REGION);
+};
 
 module.exports.allPowerValues = () => {
     const increment = 64;
@@ -311,15 +316,14 @@ module.exports.buildVisualizationURL = (stats, baseURL) => {
 };
 
 /**
- * Using the prices supplied via prices.json,
+ * Using the prices supplied,
  * to figure what the base price is for the
  * supplied lambda's region
  */
-module.exports.baseCostForRegion = (region) => {
-    const prices = JSON.parse(process.env.baseCosts);
-    if (prices[region]) {
-        return prices[region];
+module.exports.baseCostForRegion = (priceMap, region) => {
+    if (priceMap[region]) {
+        return priceMap[region];
     }
-    console.log(region + ' not found in base price map, using default: ' + prices['default']);
-    return prices['default'];
+    console.log(region + ' not found in base price map, using default: ' + priceMap['default']);
+    return priceMap['default'];
 };
diff --git a/template.yml b/template.yml
index e563d23..f7f9d6c 100644
--- a/template.yml
+++ b/template.yml
@@ -40,6 +40,7 @@ Globals:
         defaultPowerValues: !Join [ ",", !Ref PowerValues ]
         minRAM: '128'
         baseCosts: '{"ap-east-1":2.865e-7,"af-south-1":2.763e-7,"me-south-1":2.583e-7,"eu-south-1":2.440e-7,"default":2.083e-7}'
+        sfCosts: '{"default": 0.000025,"us-gov-west-1": 0.00003,"ap-northeast-2": 0.0000271,"eu-south-1": 0.00002625,"af-south-1": 0.00002975,"us-west-1": 0.0000279,"eu-west-3": 0.0000297,"ap-east-1": 0.0000275,"me-south-1": 0.0000275,"ap-south-1": 0.0000285,"us-gov-east-1": 0.00003,"sa-east-1": 0.0000375}'
         visualizationURL: !Ref visualizationURL
 
 Resources:


Test patch:
diff --git a/test/unit/test-lambda.js b/test/unit/test-lambda.js
index 3d68b2b..f0a1193 100644
--- a/test/unit/test-lambda.js
+++ b/test/unit/test-lambda.js
@@ -20,6 +20,7 @@ AWS.mock('Lambda', 'invoke', {});
 const powerValues = [128, 256, 512, 1024];
 process.env.defaultPowerValues = powerValues.join(',');
 process.env.minRAM = 128;
+process.env.baseCosts = '{"eu-west-1":0.00000003, "af-south-1": 0.0000002763, "default": 0.00000028}';
 const fakeContext = {};
 
 // variables used during tests
@@ -84,7 +85,7 @@ describe('Lambda Functions', async() => {
                 return arn;
             });
         sandBox.stub(utils, 'baseCostForRegion')
-            .callsFake((region) => {
+            .callsFake((_priceMap, region) => {
                 return region === 'af-south-1' ? 0.0000002763 : 0.0000002083;
             });
         getLambdaAliasStub = sandBox.stub(utils, 'getLambdaAlias')
diff --git a/test/unit/test-utils.js b/test/unit/test-utils.js
index 2df44a6..b5a4c2f 100644
--- a/test/unit/test-utils.js
+++ b/test/unit/test-utils.js
@@ -4,6 +4,18 @@ const expect = require('expect.js');
 
 // const AWS = require('aws-sdk');
 var AWS = require('aws-sdk-mock');
+
+process.env.sfCosts = `{"us-gov-west-1": 0.00003,"eu-north-1": 0.000025,
+"eu-central-1": 0.000025,"us-east-1": 0.000025,"ap-northeast-1": 0.000025,
+"ap-northeast-2": 0.0000271,"eu-south-1": 0.00002625,"af-south-1": 0.00002975,
+"us-west-1": 0.0000279,"eu-west-3": 0.0000297,"ap-southeast-2": 0.000025,
+"ap-east-1": 0.0000275,"eu-west-2": 0.000025,"me-south-1": 0.0000275,
+"us-east-2": 0.000025,"ap-south-1": 0.0000285,"ap-southeast-1": 0.000025,
+"us-gov-east-1": 0.00003,"ca-central-1": 0.000025,"eu-west-1": 0.000025,
+"us-west-2": 0.000025,"sa-east-1": 0.0000375}`;
+
+process.env.AWS_REGION = 'af-south-1';
+
 const utils = require('../../lambda/utils');
 
 // AWS SDK mocks
@@ -54,6 +66,32 @@ describe('Lambda Utils', () => {
         });
     });
 
+    describe('stepFunctionsCost', () => {
+        it('should return expected step base cost', () => {
+            process.env.sfCosts = '{"us-gov-west-1": 0.00003, "default": 0.000025}';
+            process.env.AWS_REGION = 'us-gov-west-1';
+            const result = utils.stepFunctionsBaseCost();
+            expect(result).to.be.equal(0.00003);
+        });
+        it('should return default step base cost', () => {
+            process.env.sfCosts = '{"us-gov-west-1": 0.00003, "default": 0.000025}';
+            process.env.AWS_REGION = 'af-south-1';
+            const result = utils.stepFunctionsBaseCost();
+            expect(result).to.be.equal(0.000025);
+        });
+    });
+
+    describe('stepFunctionsBaseCost', () => {
+        it('should return expected step total cost', () => {
+            process.env.sfCosts = '{"us-gov-west-1": 0.00003, "default": 0.000025}';
+            process.env.AWS_REGION = 'us-gov-west-1';
+            const nPower = 10;
+            const expectedCost = +(0.00003 * (6 + nPower)).toFixed(5);
+            const result = utils.stepFunctionsCost(nPower);
+            expect(result).to.be.equal(expectedCost);
+        });
+    });
+
     describe('getLambdaPower', () => {
         it('should return the memory value', async() => {
             const value = await getLambdaPower('arn:aws:lambda:us-east-1:XXX:function:YYY');
@@ -262,19 +300,19 @@ describe('Lambda Utils', () => {
     });
 
     describe('baseCostForRegion', () => {
-        process.env.baseCosts = JSON.stringify({
+        const prices = {
             'ap-east-1': 0.0000002865,
             'af-south-1': 0.0000002763,
             'me-south-1': 0.0000002583,
             default: 0.0000002083,
-        });
+        };
 
         it('should return ap-east-1 base price', () => {
-            expect(utils.baseCostForRegion('ap-east-1')).to.be(0.0000002865);
+            expect(utils.baseCostForRegion(prices, 'ap-east-1')).to.be(0.0000002865);
         });
 
         it('should return default base price', () => {
-            expect(utils.baseCostForRegion('eu-west-1')).to.be(0.0000002083);
+            expect(utils.baseCostForRegion(prices, 'eu-west-1')).to.be(0.0000002083);
         });
     });
 });