Repository: alexcasalboni/aws-lambda-power-tuning
Pull request #116 (2020-12-01 17:30:20)

Align with new 1ms billing


Patch:
diff --git a/lambda/utils.js b/lambda/utils.js
index 1e0a038..767b0de 100644
--- a/lambda/utils.js
+++ b/lambda/utils.js
@@ -295,13 +295,11 @@ module.exports.convertPayload = (payload) => {
 };
 
 /**
- * Compute average price and returns with average duration.
+ * Compute average price, given average duration.
  */
 module.exports.computePrice = (minCost, minRAM, value, duration) => {
-    // compute official price per 100ms
-    const pricePer100ms = value * minCost / minRAM;
-    // quantize price to upper 100ms (billed duration) and compute avg price
-    return Math.ceil(duration / 100) * pricePer100ms;
+    // it's just proportional to ms (ceiled) and memory value
+    return Math.ceil(duration) * minCost * (value / minRAM);
 };
 
 module.exports.parseLogAndExtractDurations = (data) => {
@@ -312,7 +310,7 @@ module.exports.parseLogAndExtractDurations = (data) => {
 };
 
 /**
- * Copute average duration
+ * Compute total cost
  */
 module.exports.computeTotalCost = (minCost, minRAM, value, durations) => {
     if (!durations || !durations.length) {
diff --git a/template.yml b/template.yml
index 90a40fa..8118052 100644
--- a/template.yml
+++ b/template.yml
@@ -45,7 +45,7 @@ Globals:
       Variables:
         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}'
+        baseCosts: '{"ap-east-1":2.9e-9,"af-south-1":2.8e-9,"me-south-1":2.6e-9,"eu-south-1":2.4e-9,"default":2.1e-9}'
         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
 


Test patch:
diff --git a/test/unit/test-lambda.js b/test/unit/test-lambda.js
index c11221d..c7a21ca 100644
--- a/test/unit/test-lambda.js
+++ b/test/unit/test-lambda.js
@@ -20,7 +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}';
+process.env.baseCosts = '{"ap-east-1":2.9e-9,"af-south-1":2.8e-9,"me-south-1":2.6e-9,"eu-south-1":2.4e-9,"default":2.1e-9}';
 const fakeContext = {};
 
 // variables used during tests
@@ -87,7 +87,7 @@ describe('Lambda Functions', async() => {
             });
         sandBox.stub(utils, 'baseCostForRegion')
             .callsFake((_priceMap, region) => {
-                return region === 'af-south-1' ? 0.0000002763 : 0.0000002083;
+                return region === 'af-south-1' ? 2.8e-9 : 2.1e-9;
             });
         getLambdaAliasStub = sandBox.stub(utils, 'getLambdaAlias')
             .callsFake(async() => {
@@ -356,7 +356,7 @@ describe('Lambda Functions', async() => {
                     // logs will always return 1ms duration with 128MB
                     return {
                         StatusCode: 200,
-                        LogResult: 'U1RBUlQgUmVxdWVzdElkOiA0NzlmYjUxYy0xZTM4LTExZTctOTljYS02N2JmMTYzNjA4ZWQgVmVyc2lvbjogOTkKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTEgPSB1bmRlZmluZWQKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTIgPSB1bmRlZmluZWQKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTMgPSB1bmRlZmluZWQKRU5EIFJlcXVlc3RJZDogNDc5ZmI1MWMtMWUzOC0xMWU3LTk5Y2EtNjdiZjE2MzYwOGVkClJFUE9SVCBSZXF1ZXN0SWQ6IDQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAlEdXJhdGlvbjogMS4wIG1zCUJpbGxlZCBEdXJhdGlvbjogMTAwIG1zIAlNZW1vcnkgU2l6ZTogMTI4IE1CCU1heCBNZW1vcnkgVXNlZDogMTUgTUIJCg==',
+                        LogResult: 'U1RBUlQgUmVxdWVzdElkOiA0NzlmYjUxYy0xZTM4LTExZTctOTljYS02N2JmMTYzNjA4ZWQgVmVyc2lvbjogOTkKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTEgPSB1bmRlZmluZWQKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTIgPSB1bmRlZmluZWQKMjAxNy0wNC0xMFQyMTo1NDozMi42ODNaCTQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAl2YWx1ZTMgPSB1bmRlZmluZWQKRU5EIFJlcXVlc3RJZDogNDc5ZmI1MWMtMWUzOC0xMWU3LTk5Y2EtNjdiZjE2MzYwOGVkClJFUE9SVCBSZXF1ZXN0SWQ6IDQ3OWZiNTFjLTFlMzgtMTFlNy05OWNhLTY3YmYxNjM2MDhlZAlEdXJhdGlvbjogMS4wIG1zCUJpbGxlZCBEdXJhdGlvbjogMS4wIG1zIAlNZW1vcnkgU2l6ZTogMTI4IE1CCU1heCBNZW1vcnkgVXNlZDogMTUgTUIJCg==',
                         ExecutedVersion: '$LATEST',
                         Payload: '{}',
                     };
@@ -423,7 +423,7 @@ describe('Lambda Functions', async() => {
             });
         });
 
-        it('should return statistics, default', async() => {
+        it('should return statistics (default)', async() => {
             const response = await invokeForSuccess(handler, {
                 value: '128',
                 input: {
@@ -436,10 +436,10 @@ describe('Lambda Functions', async() => {
             expect(response.averagePrice).to.be.a('number');
             expect(response.averageDuration).to.be.a('number');
             expect(response.totalCost).to.be.a('number');
-            expect(parseFloat(response.totalCost.toPrecision(10))).to.be(parseFloat((0.0000002083 * 10).toPrecision(10)));
+            expect(parseFloat(response.totalCost.toPrecision(10))).to.be(2.1e-8);  // 10ms in total
         });
 
-        it('should return statistics', async() => {
+        it('should return statistics (af-south-1)', async() => {
             const response = await invokeForSuccess(handler, {
                 value: '128',
                 input: {
@@ -452,7 +452,7 @@ describe('Lambda Functions', async() => {
             expect(response.averagePrice).to.be.a('number');
             expect(response.averageDuration).to.be.a('number');
             expect(response.totalCost).to.be.a('number');
-            expect(parseFloat(response.totalCost.toPrecision(10))).to.be(parseFloat((0.0000002763 * 10).toPrecision(10)));
+            expect(parseFloat(response.totalCost.toPrecision(10))).to.be(2.8e-8); // 10ms in total
         });
 
         it('should invoke the given cb, when done (custom payload)', async() => {
diff --git a/test/unit/test-utils.js b/test/unit/test-utils.js
index f3fc978..afa6811 100644
--- a/test/unit/test-utils.js
+++ b/test/unit/test-utils.js
@@ -144,7 +144,7 @@ describe('Lambda Utils', () => {
     });
 
     describe('computePrice', () => {
-        const minCost = 0.000000208; // $
+        const minCost = 2.1e-9; // $ per ms
         const minRAM = 128; // MB
         const value = 1024; // MB
         const averageDuration = 300; // ms
@@ -152,7 +152,7 @@ describe('Lambda Utils', () => {
         it('should return the average price', () => {
             const avgPrice = utils.computePrice(minCost, minRAM, value, averageDuration);
             expect(avgPrice).to.be.a('number');
-            expect(avgPrice).to.be(minCost * 8 * 3);
+            expect(avgPrice).to.be(minCost * (value / minRAM) * averageDuration);
         });
     });
 
@@ -207,16 +207,20 @@ describe('Lambda Utils', () => {
     });
 
     describe('computeTotalCost', () => {
-        const minCost = 0.000000208; // $
+        const minCost = 2.1e-9; // $ per ms
         const minRAM = 128; // MB
         const value = 1024; // MB
         const durations = [
-            100, 200, 200, 300, 400,
+            100, 150, 200, 300, 400,
         ];
 
+        // sum all
+        const totDuration = durations.reduce((a, b) => a + b, 0);
+
+
         it('should return the total cost', () => {
             const duration = utils.computeTotalCost(minCost, minRAM, value, durations);
-            expect(duration).to.be(minCost * 8 * (1 + 2 + 2 + 3 + 4));
+            expect(duration).to.be(minCost * (value / minRAM) * totDuration);
         });
         it('should return 0 if empty durations', () => {
             const duration = utils.computeTotalCost(minCost, minRAM, value, []);