Repository: alexcasalboni/aws-lambda-power-tuning
Pull request #190 (2023-02-21 01:28:27)

add waiter to executor to make sure Alias is active
I noticed when benchmarking Lambda's new [SnapStart](https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html) feature that aliases take awhile to become active.  Without this change the executor step would fail due to something along the lines of "invalid function state pending".  In my testing it takes upwards of 2 minutes for a power factor alias to become active when SnapStart is enabled.  I'm guessing this is because Lambda needs to take a VM snapshot of every new execution-environment (power factor).

I tested this with unit tests and empirically against a SnapStart enabled Lambda function.


Patch:
diff --git a/lambda/executor.js b/lambda/executor.js
index c1d37af..e48a53c 100644
--- a/lambda/executor.js
+++ b/lambda/executor.js
@@ -41,8 +41,8 @@ module.exports.handler = async(event, context) => {
     let results;
 
     // fetch architecture from $LATEST
-    const architecture = await utils.getLambdaArchitecture(lambdaARN);
-    console.log(`Detected architecture type: ${architecture}`);
+    const {architecture, isPending} = await utils.getLambdaConfig(lambdaARN, lambdaAlias);
+    console.log(`Detected architecture type: ${architecture}, isPending: ${isPending}`);
 
     // pre-generate an array of N payloads
     const payloads = utils.generatePayloads(num, payload);
@@ -57,6 +57,12 @@ module.exports.handler = async(event, context) => {
         sleepBetweenRunsMs: sleepBetweenRunsMs,
     };
 
+    // wait if the function/alias state is Pending
+    if (isPending) {
+        await utils.waitForAliasActive(lambdaARN, lambdaAlias);
+        console.log('Alias active');
+    }
+
     if (enableParallel) {
         results = await runInParallel(runInput);
     } else {
diff --git a/lambda/utils.js b/lambda/utils.js
index 5e6e327..16fd841 100644
--- a/lambda/utils.js
+++ b/lambda/utils.js
@@ -111,6 +111,24 @@ module.exports.waitForFunctionUpdate = async(lambdaARN) => {
     return lambda.waitFor('functionUpdated', params).promise();
 };
 
+module.exports.waitForAliasActive = async(lambdaARN, alias) => {
+    console.log(`Waiting for alias ${alias} to be active`);
+    const params = {
+        FunctionName: lambdaARN,
+        Qualifier: alias,
+        $waiter: {
+            // https://aws.amazon.com/blogs/developer/waiters-in-modular-aws-sdk-for-javascript/
+            // "In v2, there is no direct way to provide maximum wait time for a waiter.
+            // You need to configure delay and maxAttempts to indirectly suggest the maximum time you want the waiter to run for."
+            // 10s * 90 is ~15 minutes (max invocation time)
+            delay: 10,
+            maxAttempts: 90,
+        },
+    };
+    const lambda = utils.lambdaClientFromARN(lambdaARN);
+    return lambda.waitFor('functionActive', params).promise();
+};
+
 /**
  * Retrieve a given Lambda Function's memory size (always $LATEST version)
  */
@@ -126,20 +144,32 @@ module.exports.getLambdaPower = async(lambdaARN) => {
 };
 
 /**
- * Retrieve a given Lambda Function's architecture (always $LATEST version)
+ * Retrieve a given Lambda Function's architecture and whether its state is Pending
  */
-module.exports.getLambdaArchitecture = async(lambdaARN) => {
-    console.log('Getting current architecture');
+module.exports.getLambdaConfig = async(lambdaARN, alias) => {
+    console.log(`Getting current function config for alias ${alias}`);
     const params = {
         FunctionName: lambdaARN,
-        Qualifier: '$LATEST',
+        Qualifier: alias,
     };
+    let architecture, isPending;
     const lambda = utils.lambdaClientFromARN(lambdaARN);
     const config = await lambda.getFunctionConfiguration(params).promise();
     if (typeof config.Architectures !== 'undefined') {
-        return config.Architectures[0];
-    };
-    return 'x86_64';
+        architecture = config.Architectures[0];
+    } else {
+        architecture = 'x86_64';
+    }
+    if (typeof config.State !== 'undefined') {
+        // see https://docs.aws.amazon.com/lambda/latest/dg/functions-states.html
+        // the most likely state here is Pending, but it could also be 
+        // - Failed: it means the version creation failed (can't do much about it, the invocation will fail anyway)
+        // - Inactive: it means the version hasn't been invoked for 14 days (can't happen because we always create new versions)
+        isPending = config.State === 'Pending';
+    } else {
+        isPending = false;
+    }
+    return {architecture, isPending};
 };
 
 /**


Test patch:
diff --git a/test/unit/test-lambda.js b/test/unit/test-lambda.js
index 181132f..2b1dfec 100644
--- a/test/unit/test-lambda.js
+++ b/test/unit/test-lambda.js
@@ -30,6 +30,7 @@ var setLambdaPowerCounter,
     createLambdaAliasCounter,
     updateLambdaAliasCounter,
     waitForFunctionUpdateCounter,
+    waitForAliasActiveCounter,
     sleepCounter;
 
 // utility to invoke handler (success case)
@@ -71,7 +72,7 @@ var getLambdaAliasStub,
     invokeLambdaStub,
     invokeLambdaProcessorStub,
     deleteLambdaAliasStub,
-    getLambdaArchitectureStub,
+    getLambdaConfigStub,
     fetchPayloadFromS3Stub;
 
 /** unit tests below **/
@@ -85,6 +86,7 @@ describe('Lambda Functions', async() => {
         createLambdaAliasCounter = 0;
         updateLambdaAliasCounter = 0;
         waitForFunctionUpdateCounter = 0;
+        waitForAliasActiveCounter = 0;
         sleepCounter = 0;
 
         sandBox.stub(utils, 'regionFromARN')
@@ -131,6 +133,11 @@ describe('Lambda Functions', async() => {
                 waitForFunctionUpdateCounter++;
                 return 'OK';
             });
+        sandBox.stub(utils, 'waitForAliasActive')
+            .callsFake(async() => {
+                waitForAliasActiveCounter++;
+                return 'OK';
+            });
         sandBox.stub(utils, 'sleep')
             .callsFake(async(_) => {
                 sleepCounter++;
@@ -350,14 +357,14 @@ describe('Lambda Functions', async() => {
             invokeLambdaPayloads,
             invokeProcessorCounter,
             fetchPayloadFromS3Counter,
-            getLambdaArchitectureCounter;
+            getLambdaConfigCounter;
 
         beforeEach('mock utilities', () => {
             invokeLambdaCounter = 0;
             invokeLambdaPayloads = [];
             invokeProcessorCounter = 0;
             fetchPayloadFromS3Counter = 0;
-            getLambdaArchitectureCounter = 0;
+            getLambdaConfigCounter = 0;
 
             invokeLambdaStub && invokeLambdaStub.restore();
             invokeLambdaStub = sandBox.stub(utils, 'invokeLambda')
@@ -381,12 +388,15 @@ describe('Lambda Functions', async() => {
                     return '{"Processed": true}';
                 });
 
-            getLambdaArchitectureStub && getLambdaArchitectureStub.restore();
-            getLambdaArchitectureStub = sandBox.stub(utils, 'getLambdaArchitecture')
-                .callsFake(async(_arn) => {
-                    getLambdaArchitectureCounter++;
-                    // return x86_64 or arm64 randomly
-                    return Math.floor(Math.random() * 2) === 0 ? 'x86_64' : 'arm64';
+            getLambdaConfigStub && getLambdaConfigStub.restore();
+            getLambdaConfigStub = sandBox.stub(utils, 'getLambdaConfig')
+                .callsFake(async(_arn, _alias) => {
+                    getLambdaConfigCounter++;
+                    return {
+                        // return x86_64 or arm64 randomly
+                        architecture: Math.floor(Math.random() * 2) === 0 ? 'x86_64' : 'arm64',
+                        isPending: false,
+                    };
                 });
 
             fetchPayloadFromS3Stub && fetchPayloadFromS3Stub.restore();
@@ -434,7 +444,26 @@ describe('Lambda Functions', async() => {
                     num: 10,
                 },
             });
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
+        });
+
+        it('should invoke the given cb, when done (isPending)', async() => {
+            getLambdaConfigStub && getLambdaConfigStub.restore();
+            getLambdaConfigStub = sandBox.stub(utils, 'getLambdaConfig')
+                .callsFake(async(_arn) => {
+                    getLambdaConfigCounter++;
+                    return {architecture: 'x86_64', isPending: true};
+                });
+            await invokeForSuccess(handler, {
+                value: '128',
+                input: {
+                    lambdaARN: 'arnOK',
+                    num: 10,
+                },
+            });
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(1);
         });
 
         it('should invoke the given cb, when done (parallelInvocation)', async() => {
@@ -446,7 +475,8 @@ describe('Lambda Functions', async() => {
                     parallelInvocation: true,
                 },
             });
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         it('should invoke the given cb, when done with between function sleep', async() => {
@@ -460,7 +490,8 @@ describe('Lambda Functions', async() => {
                 },
             });
 
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
             expect(sleepCounter).to.be(10);
 
         });
@@ -475,7 +506,8 @@ describe('Lambda Functions', async() => {
                     parallelInvocation: true,
                 },
             });
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
             expect(sleepCounter).to.be(0);
         });
 
@@ -488,7 +520,8 @@ describe('Lambda Functions', async() => {
                     sleepBetweenRunsMs: 'Not a number',
                 },
             });
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
             expect(sleepCounter).to.be(0);
         });
 
@@ -506,7 +539,8 @@ describe('Lambda Functions', async() => {
             expect(response.averageDuration).to.be.a('number');
             expect(response.totalCost).to.be.a('number');
             expect(parseFloat(response.totalCost.toPrecision(10))).to.be(2.1e-8); // 10ms in total
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         it('should return statistics (af-south-1)', async() => {
@@ -523,7 +557,8 @@ describe('Lambda Functions', async() => {
             expect(response.averageDuration).to.be.a('number');
             expect(response.totalCost).to.be.a('number');
             expect(parseFloat(response.totalCost.toPrecision(10))).to.be(2.8e-8); // 10ms in total
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         it('should invoke the given cb, when done (custom payload)', async() => {
@@ -541,7 +576,8 @@ describe('Lambda Functions', async() => {
             invokeLambdaPayloads.forEach(payload => {
                 expect(payload).to.be(JSON.stringify(expectedPayload));
             });
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         it('should invoke the given cb, when done (weighted payload)', async() => {
@@ -570,7 +606,8 @@ describe('Lambda Functions', async() => {
             expect(counters.B).to.be(3);
             expect(counters.C).to.be(6);
 
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         it('should invoke the given cb, when done (weighted payload 2)', async() => {
@@ -599,7 +636,8 @@ describe('Lambda Functions', async() => {
             expect(counters.B).to.be(30);
             expect(counters.C).to.be(60);
 
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         it('should invoke the given cb, when done (weighted payload 3)', async() => {
@@ -622,8 +660,8 @@ describe('Lambda Functions', async() => {
                 expect(payload).to.be.a('string');
             });
 
-            expect(getLambdaArchitectureCounter).to.be(1);
-
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         it('should explode if count(payloads) < num', async() => {
@@ -651,8 +689,8 @@ describe('Lambda Functions', async() => {
                 },
             });
 
-            expect(getLambdaArchitectureCounter).to.be(1);
-
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         it('should invoke the given cb, when done (not enough weight)', async() => {
@@ -671,7 +709,8 @@ describe('Lambda Functions', async() => {
                 },
             });
 
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         it('should explode if invalid weighted payloads', async() => {
@@ -700,7 +739,8 @@ describe('Lambda Functions', async() => {
                 });
             });
 
-            expect(getLambdaArchitectureCounter).to.be(0);
+            expect(getLambdaConfigCounter).to.be(0);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         [1, 10, 100].forEach(num => {
@@ -713,7 +753,8 @@ describe('Lambda Functions', async() => {
                     },
                 });
                 expect(invokeLambdaCounter).to.be(num);
-                expect(getLambdaArchitectureCounter).to.be(1);
+                expect(getLambdaConfigCounter).to.be(1);
+                expect(waitForAliasActiveCounter).to.be(0);
             });
             it('should invoke Lambda ' + num + ' time(s) in parallel', async() => {
                 await invokeForSuccess(handler, {
@@ -725,7 +766,8 @@ describe('Lambda Functions', async() => {
                     },
                 });
                 expect(invokeLambdaCounter).to.be(num);
-                expect(getLambdaArchitectureCounter).to.be(1);
+                expect(getLambdaConfigCounter).to.be(1);
+                expect(waitForAliasActiveCounter).to.be(0);
             });
         });
 
@@ -746,7 +788,8 @@ describe('Lambda Functions', async() => {
                 },
             });
 
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         it('should include payload in exception message if invocation fails (series)', async() => {
@@ -770,7 +813,8 @@ describe('Lambda Functions', async() => {
             expect(error.message).to.contain('SENTINEL');
             expect(error.message).to.contain('in series');
 
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         it('should include payload in exception message if invocation fails (parallel)', async() => {
@@ -795,7 +839,8 @@ describe('Lambda Functions', async() => {
             expect(error.message).to.contain('SENTINEL');
             expect(error.message).to.contain('in parallel');
 
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
 
@@ -823,7 +868,8 @@ describe('Lambda Functions', async() => {
             expect(error.message).to.contain('SENTINEL1');
             expect(error.message).to.contain('in series');
 
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         it('should include weighted payload in exception message if invocation fails (parallel)', async() => {
@@ -851,7 +897,8 @@ describe('Lambda Functions', async() => {
             expect(error.message).to.contain('SENTINEL1');
             expect(error.message).to.contain('in parallel');
 
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         it('should report an error if invocation fails (parallel)', async() => {
@@ -872,7 +919,8 @@ describe('Lambda Functions', async() => {
                 },
             });
 
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         it('should return price as output', async() => {
@@ -886,7 +934,8 @@ describe('Lambda Functions', async() => {
             expect(stats.averagePrice).to.be.a('number');
             expect(stats.averageDuration).to.be.a('number');
 
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         it('should run only once if dryRun', async() => {
@@ -899,7 +948,8 @@ describe('Lambda Functions', async() => {
                 },
             });
             expect(invokeLambdaCounter).to.be(1);
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         it('should invoke pre-processor', async() => {
@@ -914,7 +964,8 @@ describe('Lambda Functions', async() => {
             });
             expect(invokeLambdaCounter).to.be(num * 2);
             expect(invokeProcessorCounter).to.be(num);
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         it('should invoke post-processor', async() => {
@@ -929,7 +980,8 @@ describe('Lambda Functions', async() => {
             });
             expect(invokeLambdaCounter).to.be(num * 2);
             expect(invokeProcessorCounter).to.be(num);
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         it('should invoke pre-processor and post-processor', async() => {
@@ -945,7 +997,8 @@ describe('Lambda Functions', async() => {
             });
             expect(invokeLambdaCounter).to.be(num * 3);
             expect(invokeProcessorCounter).to.be(num * 2);
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         it('should invoke function with pre-processor output', async() => {
@@ -959,7 +1012,8 @@ describe('Lambda Functions', async() => {
                 },
             });
             expect(invokeLambdaPayloads[0].includes('Processed')).to.be(true);
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         it('should invoke function with original payload if pre-precessor does not return a new payload', async() => {
@@ -984,7 +1038,8 @@ describe('Lambda Functions', async() => {
             });
             expect(invokeLambdaPayloads[0].includes('Original')).to.be(true);
 
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         it('should explode if pre-processor fails', async() => {
@@ -1005,7 +1060,8 @@ describe('Lambda Functions', async() => {
                 },
             });
 
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         it('should explode if post-processor fails', async() => {
@@ -1026,7 +1082,8 @@ describe('Lambda Functions', async() => {
                 },
             });
 
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         it('should explode with processed payload in case of execution error (series)', async() => {
@@ -1061,7 +1118,8 @@ describe('Lambda Functions', async() => {
             expect(error.message).to.contain('in series');
             expect(error.message).to.contain(JSON.stringify({Processed: true}));
 
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         it('should explode with processed payload in case of execution error (parallel)', async() => {
@@ -1097,7 +1155,8 @@ describe('Lambda Functions', async() => {
             expect(error.message).to.contain('in parallel');
             expect(error.message).to.contain(JSON.stringify({Processed: true}));
 
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         it('should fetch payload from S3 if payloadS3 is given', async() => {
@@ -1115,7 +1174,8 @@ describe('Lambda Functions', async() => {
                 expect(payload).to.be('{"ValueFromS3":"OK"}');
             }
 
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         it('should fetch payload from S3 if both payload and payloadS3 are given', async() => {
@@ -1134,7 +1194,8 @@ describe('Lambda Functions', async() => {
                 expect(payload).to.be('{"ValueFromS3":"OK"}');
             }
 
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         it('should generate weighted payload from S3', async() => {
@@ -1170,7 +1231,8 @@ describe('Lambda Functions', async() => {
             expect(counters.B).to.be(3);
             expect(counters.C).to.be(6);
 
-            expect(getLambdaArchitectureCounter).to.be(1);
+            expect(getLambdaConfigCounter).to.be(1);
+            expect(waitForAliasActiveCounter).to.be(0);
         });
 
         const discardTopBottomValues = [
diff --git a/test/unit/test-utils.js b/test/unit/test-utils.js
index b5f6f87..89f9e39 100644
--- a/test/unit/test-utils.js
+++ b/test/unit/test-utils.js
@@ -144,6 +144,15 @@ describe('Lambda Utils', () => {
 
     });
 
+    describe('waitForAliasActive', () => {
+
+        it('should return if Status is Active', async() => {
+            // TODO: remove waitFor mock and test this properly
+            await utils.waitForAliasActive('arn:aws:lambda:us-east-1:XXX:function:YYY', 'aliasName');
+        });
+
+    });
+
     describe('extractDuration', () => {
         const log =
             'START RequestId: 55bc566d-1e2c-11e7-93e6-6705ceb4c1cc Version: $LATEST\n' +
@@ -373,26 +382,53 @@ describe('Lambda Utils', () => {
         });
     });
 
-    describe('getLambdaArchitecture', () => {
+    describe('getLambdaConfig', () => {
 
         it('should return a string representing the arch type', async() => {
             const ARN = 'arn:aws:lambda:eu-west-1:XXX:function:name';
-            const data = await utils.getLambdaArchitecture(ARN);
-            expect(data).to.be('x86_64');
+            const alias = 'aliasName';
+            const {architecture} = await utils.getLambdaConfig(ARN, alias);
+            expect(architecture).to.be('x86_64');
         });
 
         it('should return arm64 when Graviton is supported', async() => {
             AWS.remock('Lambda', 'getFunctionConfiguration', {MemorySize: 1024, State: 'Active', LastUpdateStatus: 'Successful', Architectures: ['arm64']});
             const ARN = 'arn:aws:lambda:eu-west-1:XXX:function:name';
-            const data = await utils.getLambdaArchitecture(ARN);
-            expect(data).to.be('arm64');
+            const alias = 'aliasName';
+            const {architecture} = await utils.getLambdaConfig(ARN, alias);
+            expect(architecture).to.be('arm64');
         });
 
         it('should always return x86_64 when Graviton is not supported', async() => {
             AWS.remock('Lambda', 'getFunctionConfiguration', {MemorySize: 1024, State: 'Active', LastUpdateStatus: 'Successful'});
             const ARN = 'arn:aws:lambda:eu-west-1:XXX:function:name';
-            const data = await utils.getLambdaArchitecture(ARN);
-            expect(data).to.be('x86_64');
+            const alias = 'aliasName';
+            const {architecture} = await utils.getLambdaConfig(ARN, alias);
+            expect(architecture).to.be('x86_64');
+        });
+
+        it('should return isPending true when function/alias state is Pending', async() => {
+            AWS.remock('Lambda', 'getFunctionConfiguration', {MemorySize: 1024, State: 'Pending', LastUpdateStatus: 'Successful'});
+            const ARN = 'arn:aws:lambda:eu-west-1:XXX:function:name';
+            const alias = 'aliasName';
+            const {isPending} = await utils.getLambdaConfig(ARN, alias);
+            expect(isPending).to.be(true);
+        });
+
+        it('should return isPending false when function/alias state is not Pending', async() => {
+            AWS.remock('Lambda', 'getFunctionConfiguration', {MemorySize: 1024, State: 'Active', LastUpdateStatus: 'Successful'});
+            const ARN = 'arn:aws:lambda:eu-west-1:XXX:function:name';
+            const alias = 'aliasName';
+            const {isPending} = await utils.getLambdaConfig(ARN, alias);
+            expect(isPending).to.be(false);
+        });
+
+        it('should return isPending false when function/alias state is missing', async() => {
+            AWS.remock('Lambda', 'getFunctionConfiguration', {MemorySize: 1024, LastUpdateStatus: 'Successful'});
+            const ARN = 'arn:aws:lambda:eu-west-1:XXX:function:name';
+            const alias = 'aliasName';
+            const {isPending} = await utils.getLambdaConfig(ARN, alias);
+            expect(isPending).to.be(false);
         });
     });