Thursday, December 31, 2020

JavaScript - Understanding well


Let's understand JavaScript well

  • Avoid 'var' to declare variable
What's the output?
1:  var a = "X";  
2:  console.log(a+" - "+b);  
3:  var b = "Y";  
Explanation:
Do you expect the output to be "X - Y"? or an error be thrown? both are wrong.
The output is "X - undefined"
Here's the reason: JavaScript hoisting occurs during the creation phase of the execution context that moves the variable and function declarations to the top of the script. Even though the variable 'b' is declared and initialized with a value "Y" at line # 3, the declaration gets moved to the top of the script with a value undefined.
1:  bar();  
2:  //foo();//ReferenceError: Cannot access 'foo' before initialization   
3:  let foo = () => {  
4:    console.log('hello from foo');  
5:  }  
6:  function bar() {  
7:    console.log('hello from bar');  
8:  }  
Similarly in the above code the function 'bar' is accessible even before it's defined due to hoisting which makes the code difficult to follow as a function is used before it's defined. On the other hand the function 'foo' is not accessible before it's defined as it's created using 'let'. Use only let or const to declare variables and functions

  • Closure
A closure is the combination of a function and the lexical environment within which that function was declared. This environment consists of any local variables that were in-scope at the time the closure was created.
Let's understand this with an example
1:  let simpleFactory = () => {  
2:    return () => {console.log('log from function returned by simpleFactory'); }  
3:  }  
4:  let simpleFun = simpleFactory();  
5:  simpleFun();//log from function returned by simpleFactory  
On line# 1 we define a Factory function which returns a function that simply prints a message on the console. 
Lin# 4 we got the returned function stored in a variable 'simpleFun'. 
Line# 5 we invoke the function stored in the variable 'simpleFun' which prints the log message. 
Simple to understand, right? Let's see another example

1:  let parameterizedFactory = (x) => {  
2:    return (y) => {return x*y;}  
3:  }  
4:  let parameterizedFun = parameterizedFactory(5);  
5:  console.log(parameterizedFun(2));//prints 10  
6:  console.log(parameterizedFun(3));//prints 15  
Now we are adding a little complexity to the Factory function by parameterizing it. The factory now accepts a value in parameter x and that value is used inside the function returned by the factory in line#2. 
We call parameterizedFactory by passing a value '5' which gets assigned in 'x'
The function returned in line# 2 uses the variable 'x' inside its body which is possible because when the function (at line# 2) is created an environment is created for that function which includes all the in-scoped local variables, that's 'x' in this case.
The function got stored in 'parameterizedFun' at line# 4 has access to the variable 'x' with value '5' due to Closure.

The local variable doesn't need stay constant, it can be even modified.
Let's see an example:
1:  let factoryWithLocVar = () => {  
2:    let count = 0;  
3:    return (y) => {count++; return count + y; }  
4:  }  
5:  let funWithLocVar = factoryWithLocVar();  
6:  console.log(funWithLocVar(2));//prints 3  
7:  console.log(funWithLocVar(5));//prints 7  
The function returned in line# 3 refers to a local variable count and even it modifies it during each invocation. The local variable 'count' is modified during each invocation and the modification reflects in subsequent calls to that function.

  • Pure function
A pure function in programming is a function that always produces the same output for the same set of input values and has no side effects. It means it should not change any state or data

Example:
1:  let add = (a,b) => {  
2:    return a+b;  
3:  }  
The above function would return the same result how many ever times it's called with the same input, and it doesn't modify state of any variables. In contrast an impure function may not return the same value in subsequent invocations for the same input also it could modify the state of a global variable (in the example given below it's the sum variable). 

Let's see an example:
1: let sum = 0;

2:  let add = (a,b) => {  
3:    sum += a+b;
4:    return sum;
5: }

More to follow ......

Sunday, December 20, 2020

AWS - Lambda Blue/Green deployment

AWS - Zero downtime Blue/Green deployment for Lambda

If your application is based on Lambda, the Lambda's can be updated in Production with zero downtime using Lambda Versioning and Alias. The Lambda's can be rolled back too easily if something is wrong with the latest code with zero downtime.

Let's see in detail

Understanding some keywords

  • Versions - Lambda creates a new version of your function each time that you publish the function. The versioned Lambda's code is frozen and not editable. The version is automatically named 1, 2, 3 and so on each time published and one can't name a version differently
  • Aliases - A Lambda alias is like a pointer to a specific Lambda version. Users can invoke the Lambda version using the alias. The Alias can be updated to point to different version
  • Blue version - Existing version of Lambda being currently used in Production (In Diagram 2 v2)
  • Green version - New version of Lambda being deployed to Production
Keep in mind, Lambda can be invoked in three ways
  1. Just using its name (e.g. BonusCalculatorLambda). In this case the $LATEST version of the Lambda is invoked by default
  2. Using its Version (e.g. BonusCalculatorLambda:1). In this case the version 1 of the Lambda is invoked
  3. Using its Alias (e.g. BonusCalculatorLambda:PROD). In this case the version of the Lambda currently being pointed by the Alias is invoked (As per the Diagram 2 below version 2)
Let's assume your application is built using Spring Boot or Node.js Express and uses multiple Lambdas to implement its business logic and invokes them using AWS SDK as depicted in the following picture. The gateway application is running on ECS Cluster which is exposed to Clients via an ALB. Each of the Lambda can be updated in Production with zero downtime.

Diagram 1 - Lambda based application


How do we update each Lambda with zero down time and rollback in case something is not good?

Diagram 2 - Before Production deployment state


The above diagram depicts the current state of the Application before deployment for a single Lambda. The Lambda is accessible via an Alias (called PROD) which is now pointing to Version 2 of Lambda (BLUE version). At this point we have the following versions of Lambda v1, v2 and $LATEST.

Here's how to update the Lambda with zero downtime, the same approach needs to be replicated for each Lambda. 
Follow these steps to update the Lambda with changes, refer to Diagram 3 below
  1. Update the Lambda code using a Jenkins job in Production, this would update the $LATEST version of the Lambda, this is the only version which is open for update, rest of the versions (v1, v2) are frozen and can't be modified
  2. Test the updated Lambda using a Test User, the Application would invoke $LATEST version of the Lambda based on some criteria such as logged in User (Test User) etc. For real User the Application invokes Lambda using the PROD Alias (e.g. BonusCalculatorLambda:PROD) which would still continue to invoke v2 (Blue) version of the Lambda. Here's a sample Node.js Lambda client
  3. Once the Test results of $LATEST versions is satisfactory, invoke publish Jenkins job. This would publish v3 of the Lambda. Refer to Diagram 3 below
  4. As a last step run a Jenkins job to update the Alias PROD so that it starts pointing to v3 (Green) version of Lambda
  5. In case the deployment needs to be rolled back, just invoke Alias update Jenkins job on the last Pipeline # so that it points back to previous BLUE version v2
During any of this process the Production users are not impacted and the Service is up all time. Replicate this same process for all your Lambdas

Diagram 3 - After deployment of new version in Production



Here's AWS CLI commands to do all of the things said above, the Lambda is implemented using Node.js:
  • Create index.js file with the following line of code
 exports.handler = async (event) => {  
   const response = {  
     statusCode: 200,  
     body: JSON.stringify('Hello from Lambda!'),  
   };  
   return response;  
 };  

  • Create a zip file to deploy this code as a Lambda (no .zip extension is necessary, it gets added)
 zip BonusCalculatorLambda index.js

  • Create the Lambda, make sure you are running this command from the folder where you have BonusCalculatorLambda.zip This creates the $LATEST version of the Lambda
 aws lambda create-function --function-name BonusCalculatorLambda \
--role arn:aws:iam::123903503456:role/service-role/roleLambdaExecution \ --runtime nodejs12.x \ --handler index.handler \ --zip-file "fileb://BonusCalculatorLambda.zip
"

  • Publish the Lambda to create a version, after this command there would be two version of Lambda 1 and $LATEST, only $LATEST is editable and version 1 is frozen
 aws lambda publish-version --function-name BonusCalculatorLambda --description v1  

  • Create an Alias
 aws lambda create-alias --function-name BonusCalculatorLambda \
      --name PROD \
      --function-version 1

  • Update Lambda code, this updates $LATEST version
 aws lambda update-function-code --function-name BonusCalculatorLambda \
      --zip-file "fileb://BonusCalculatorLambda.zip"

  • Upon creating new version 2, update the Alias to point to new version
 aws lambda update-alias --function-name BonusCalculatorLambda \
      --name PROD --function-version 2

Prerequisite to execute these commands
  • One should have setup Client Credentials, see here for instructions
  • The Lambda needs a role to Execute (in the example roleLambdaExecution) this should have been created already
  • The Client credential one has setup for CLI should have the following Policy action
 {  
   "Version": "2012-10-17",  
   "Statement": [  
     {  
       "Sid": "VisualEditor",  
       "Effect": "Allow",  
       "Action": "iam:PassRole",  
       "Resource": "arn:aws:iam::123903503456:role/service-role/roleLambdaExecution"  
} ] }

Note: For the Blue/Green zero downtime deployment it's not necessary your Lambda's should have been exposed via an application (Spring Boot or Node.js Express). It's a sample Architecture explained in this article. The Lambda can be exposed directly via an ALB or API Gateway and the same approach can be used to deploy changes with zero downtime with slight modifications.

Saturday, December 5, 2020

NodeJS AWS Lamda Client

In this Post let's see how to invoke an AWS Lambda from NodeJS

In case someone wants to invoke an AWS Lambda from NodeJS (could be from Express WebApp) then we can do so using the AWS SDK for JavaScript in Node.js

The following code can be used to invoke the Lambda

  • Create a function which invokes Lambda

 let AWS = require('aws-sdk');  
 const util = require('util');  
 AWS.config.update({ region: 'us-east-1' });  
 let lambda = new AWS.Lambda();  
 //Promisify the Lambda  
 let promisifiedLambda = util.promisify(lambda.invoke.bind(lambda));  
 /**  
  * This function invokes Lambda  
  * @param {*} functionName Name of the Lambda function  
  * @param {*} payload Payload to be passed onto Lambda  
  */  
 let lambdaInvoker = async (functionName, payload) => {  
   let params = {  
     FunctionName: functionName,  
     Payload: `{ "body" : ${JSON.stringify(payload)} }`  
   }  
   let res;  
   try {  
     //Use this format if Lambds is not promisified  
     res = await lambda.invoke(params).promise();  
     //Use this format if Lambda is promisified  
     //res = await promisifiedLambda(params);  
   } catch (err) {  
     console.error(`err:: ${err}`);  
   }  
   console.log(`Lambda invocation successful`);  
   console.log(res);  
   return res.Payload;  
 }  

  • Invoking the function created above passing Lambda Name and Payload
 //Let's invoke the Lambda  
 let lambdaFunctionName = 'cli-target-lambda';  
 let payload = { name: "abc", age: 20 };
 //Invoke lambdaInvoker using IIFE  
 (async () => {  
   try {  
     let response = await lambdaInvoker(lambdaFunctionName, payload);  
     console.log(response);  
   } catch (err) {  
     console.error(`err:: ${err}`);  
   }  
 })();  

The complete source code can be found here