|
| 1 | +# Cache Extension Demo in Go |
| 2 | + |
| 3 | +The provided code sample demo's a cache extension written in Go that acts as a companion process which the AWS Lambda function runtime can communicate. |
| 4 | + |
| 5 | +# Introduction |
| 6 | +Having a caching layer inside the Lambda function is a very common use case. It would allow the lambda function to process requests quicker and avoid the additional cost incurred by calling the AWS services over and over again. They are two types of cache: |
| 7 | +- Data cache (caching data from databases like RDS, dynamodb, etc.) |
| 8 | +- Configuration cache (caching data from a configuration system like parameter store, app config, secrets, etc.) |
| 9 | + |
| 10 | +This extension demo's the Lambda layer that enables both data cache (using dynamodb) and configuration cache (using parameter store). |
| 11 | +Here is how it works: |
| 12 | +- Uses `config.yaml` defined part of the lambda function to determine the items that needs to be cached |
| 13 | +- All the data are cached in memory before the request gets handled to the lambda function. So no cold start problems |
| 14 | +- Starts a local HTTP server at port `3000` that replies to request for reading items from the cache depending upon path variables |
| 15 | +- Uses `"CACHE_EXTENSION_TTL"` Lambda environment variable to let users define cache refresh interval (defined based on Go time format, ex: 30s, 3m, etc) |
| 16 | +- Uses `"CACHE_EXTENSION_INIT_STARTUP"` Lambda environment variable used to specify whether to load all items specified in `"cache.yml"` into cache part of extension startup (takes boolean value, ex: true and false) |
| 17 | + |
| 18 | +Here are some advantages of having the cache layer part of Lambda extension instead of having it inside the function |
| 19 | +- Reuse the code related to cache in multiple Lambda functions |
| 20 | +- Common dependencies like SDK are packaged part of the Lambda layers |
| 21 | + |
| 22 | +## Architecture |
| 23 | +Here is the high level view of all the components |
| 24 | + |
| 25 | + |
| 26 | + |
| 27 | +## Initialize extension and reading secrets from the cache |
| 28 | +Below sequence diagram explains the initialization of lambda extension and how lambda function |
| 29 | +reads cached items using HTTP server hosted inside the extension |
| 30 | + |
| 31 | + |
| 32 | +## Pre-requisites |
| 33 | +- Zip utility needs to be installed in the local system |
| 34 | +- AWS CLI needs to be installed in the local system, for more information click [here](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html) |
| 35 | + |
| 36 | +## Deploy |
| 37 | +### Parameter Store |
| 38 | +Create a new parameter using the following command |
| 39 | + |
| 40 | +```bash |
| 41 | +aws ssm put-parameter \ |
| 42 | + --name "Parameter1" \ |
| 43 | + --type "String" \ |
| 44 | + --value "Parameter_value" |
| 45 | +``` |
| 46 | + |
| 47 | +### DynamoDB |
| 48 | +- Create a new dynamodb table with a partition key compassing of hash and sort key |
| 49 | + |
| 50 | +```bash |
| 51 | +aws dynamodb create-table \ |
| 52 | + --table-name DynamoDbTable \ |
| 53 | + --attribute-definitions AttributeName=pKey,AttributeType=S AttributeName=sKey,AttributeType=S \ |
| 54 | + --key-schema AttributeName=pKey,KeyType=HASH AttributeName=sKey,KeyType=RANGE \ |
| 55 | + --billing-mode PAY_PER_REQUEST |
| 56 | +``` |
| 57 | +- Save the following JSON in a file named as `"item.json"` |
| 58 | + |
| 59 | +```json |
| 60 | +{ |
| 61 | + "pKey": {"S": "pKey1"}, |
| 62 | + "sKey": {"S": "sKey1"}, |
| 63 | + "Data": {"S": "Data goes here"} |
| 64 | +} |
| 65 | +``` |
| 66 | + |
| 67 | +- Add a new record to the `DynamoDbTable` table, by running the following command |
| 68 | +```bash |
| 69 | +aws dynamodb put-item \ |
| 70 | + --table-name DynamoDbTable \ |
| 71 | + --item file://item.json \ |
| 72 | + --return-consumed-capacity TOTAL \ |
| 73 | + --return-item-collection-metrics SIZE |
| 74 | +``` |
| 75 | + |
| 76 | +## Compile package and dependencies |
| 77 | +To run this example, you will need to ensure that your build architecture matches that of the Lambda execution environment by compiling with `GOOS=linux` and `GOARCH=amd64` if you are not running in a Linux environment. |
| 78 | + |
| 79 | +Building and saving package into a `bin/extensions` directory: |
| 80 | +```bash |
| 81 | +$ cd cache-extension-demo |
| 82 | +$ GOOS=linux GOARCH=amd64 go build -o bin/extensions/cache-extension-demo main.go |
| 83 | +$ chmod +x bin/extensions/cache-extension-demo |
| 84 | +``` |
| 85 | + |
| 86 | +## Layer Setup Process |
| 87 | +The extensions .zip file should contain a root directory called `extensions/`, where the extension executables are located. In this sample project we must include the `cache-extension-demo` binary. |
| 88 | + |
| 89 | +Creating zip package for the extension: |
| 90 | +```bash |
| 91 | +$ cd bin |
| 92 | +$ zip -r extension.zip extensions/ |
| 93 | +``` |
| 94 | + |
| 95 | +Ensure that you have aws-cli v2 for the commands below. |
| 96 | +Publish a new layer using the `extension.zip`. The output of the following command should provide you a layer arn. |
| 97 | +```bash |
| 98 | +aws lambda publish-layer-version \ |
| 99 | + --layer-name "cache-extension-demo" \ |
| 100 | + --region <use your region> \ |
| 101 | + --zip-file "fileb://extension.zip" |
| 102 | +``` |
| 103 | +Note the LayerVersionArn that is produced in the output. |
| 104 | +eg. `"LayerVersionArn": "arn:aws:lambda:<region>:123456789012:layer:<layerName>:1"` |
| 105 | + |
| 106 | +Add the newly created layer version to a Lambda function. |
| 107 | +- You can use the provided `index.js` (nodeJS extension) in the `example/` directory |
| 108 | +- Make sure to have a `config.yaml` in the root of the lambda function's directory and updated with the correct region information. You can use the provided `config.yaml` in the the `example/` directory |
| 109 | +- Make sure to increase the default timeout to 2 mins and memory to 512 MB |
| 110 | + |
| 111 | +>Note: Make sure to have`'AmazonDynamoDBReadOnlyAccess'` & `'AmazonSSMReadOnlyAccess'` IAM policies assigned to the IAM role associated with the Lambda function |
| 112 | +
|
| 113 | +Here is the AWS CLI command that can update the layers on the existing AWS Lambda function |
| 114 | + |
| 115 | +```bash |
| 116 | +aws lambda update-function-configuration \ |
| 117 | + --function-name <<function-name>> \ |
| 118 | + --layers $(aws lambda list-layer-versions --layer-name cache-extension-demo \ |
| 119 | + --max-items 1 --no-paginate --query 'LayerVersions[0].LayerVersionArn' \ |
| 120 | + --output text) |
| 121 | +``` |
| 122 | +
|
| 123 | +>Note: Make sure to replace `function-name` with the actual lambda function name |
| 124 | +
|
| 125 | +## Function Invocation and Extension Execution |
| 126 | +You can invoke the Lambda function using the following CLI command |
| 127 | +```bash |
| 128 | +aws lambda invoke \ |
| 129 | + --function-name "<<function-name>>" \ |
| 130 | + --payload '{"payload": "hello"}' /tmp/invoke-result \ |
| 131 | + --cli-binary-format raw-in-base64-out \ |
| 132 | + --log-type Tail |
| 133 | +``` |
| 134 | +>Note: Make sure to replace `function-name` with the actual lambda function name |
| 135 | +
|
| 136 | +The function should return ```"StatusCode": 200```. |
| 137 | +
|
| 138 | +When invoking the function, you should now see log messages from the example extension similar to the following: |
| 139 | +``` |
| 140 | +XXXX-XX-XXTXX:XX:XX.XXX-XX:XX START RequestId: 9ca08945-de9b-46ec-adc6-3fe9ef0d2e8d Version: $LATEST |
| 141 | +XXXX-XX-XXTXX:XX:XX.XXX-XX:XX [cache-extension-demo] Register response: { |
| 142 | + "functionName": "my-function", |
| 143 | + "functionVersion": "$LATEST", |
| 144 | + "handler": "function.handler" |
| 145 | + } |
| 146 | +XXXX-XX-XXTXX:XX:XX.XXX-XX:XX [cache-extension-demo] Cache successfully loaded |
| 147 | +XXXX-XX-XXTXX:XX:XX.XXX-XX:XX [cache-extension-demo] Waiting for event... |
| 148 | +XXXX-XX-XXTXX:XX:XX.XXX-XX:XX [cache-extension-demo] Starting Httpserver on port 3000 |
| 149 | +XXXX-XX-XXTXX:XX:XX.XXX-XX:XX EXTENSION Name: cache-extension-demo State: Ready Events: [INVOKE,SHUTDOWN] |
| 150 | +... |
| 151 | +... |
| 152 | +Function logs... |
| 153 | +... |
| 154 | +... |
| 155 | +XXXX-XX-XXTXX:XX:XX.XXX-XX:XX START RequestId: d94434eb-705d-4c22-8600-c7f53a0c2204 Version: $LATEST |
| 156 | +XXXX-XX-XXTXX:XX:XX.XXX-XX:XX [cache-extension-demo] Waiting for event... |
| 157 | +XXXX-XX-XXTXX:XX:XX.XXX-XX:XX INFO Finally got some response here: "{\"Data\":\"Data goes here\",\"pKey\":\"pKey1\",\"sKey\":\"sKey1\"}" |
| 158 | +XXXX-XX-XXTXX:XX:XX.XXX-XX:XX END RequestId: d94434eb-705d-4c22-8600-c7f53a0c2204 |
| 159 | +XXXX-XX-XXTXX:XX:XX.XXX-XX:XX REPORT RequestId: d94434eb-705d-4c22-8600-c7f53a0c2204 Duration: 17.09 ms Billed Duration: 18 ms Memory Size: 1472 MB Max Memory Used: 89 MB Init Duration: 289.40 ms |
| 160 | +``` |
0 commit comments