Interactive Reference Architecture
Click on the components or numbered steps below to explore how this architecture works.
The Circuit Breaker pattern keeps track of the number of failed (or slow) API calls by using a cache to share the status across multiple Lambda functions. In this example, we're using a DynamoDB table so that we can avoid using a VPC. If you were in a VPC already, ElastiCache would be a good alternative.
Here's how it works. When the number of failures reaches a certain threshold, we "open" the circuit and send errors back to the calling client immediately without even trying to call the API. After a short period of time, we "half open" the circuit, sending just a few requests through to see if the API is finally responding correctly. All other requests receive an error. If the sample requests are successful, we "close" the circuit and start letting all traffic through. However, if some or all of those requests fail, the circuit stays "open", and the process repeats with some algorithm for increasing the timeout between "half open" retry attempts.
This is an incredibly powerful (and cost saving) pattern for any type of synchronous request to an API or downstream system. You are accumulating charges whenever a Lambda function is running and waiting for another task to complete. Allowing your systems to self-identify issues like this, provide incremental backoff, and then self-heal when the service comes back online, adds a tremendous amount of resiliency to your applications.
Deploy this Pattern
Below are the basic configurations for deploying this pattern using different frameworks and platforms. Additional configuration for your environment will be necessary. The source files and additional examples are available in the GitHub repo.
-
-
SAM
-
Stackery
-
Serverless Framework
1service: simple-web-service2provider:3 name: aws4 runtime: nodejs12.x5 stage: ${opt:stage,'dev'}6 region: us-east-17 stackName: ${self:service}-${self:provider.stage}8 stackTags:9 SERVICE: ${self:service}10 httpApi:11 payload: '2.0'12 cors: true1314functions:15 GetUser:16 handler: index.handler17 memorySize: 102418 timeout: 619 tracing: Active20 environment:21 TABLE_NAME: !Ref Users22 TABLE_ARN: !GetAtt Users.Arn23 iamRoleStatementsName: ${self:service}-${self:provider.stage}-getuser-role24 iamRoleStatements:25 - Effect: Allow26 Action:27 - dynamodb:getItem28 - dynamodb:putItem29 - dynamodb:updateItem30 - dynamodb:deleteItem31 Resource:32 - !Join [ '/', [ !GetAtt Users.Arn, '*' ] ]33 - !GetAtt Users.Arn34 - Effect: Allow35 Action:36 - xray:PutTraceSegments37 - xray:PutTelemetryRecords38 Resource: "*"39 events:40 - httpApi:41 path: /user/{id}42 method: get4344plugins:45 - serverless-iam-roles-per-function4647resources:48 Resources:49 Users:50 Type: AWS::DynamoDB::Table51 Properties:52 AttributeDefinitions:53 - AttributeName: id54 AttributeType: S55 BillingMode: PAY_PER_REQUEST56 KeySchema:57 - AttributeName: id58 KeyType: HASH59 StreamSpecification:60 StreamViewType: NEW_AND_OLD_IMAGES
-