byu-circuitbreaker v5.0.4
byu-circuitbreaker
An NPM package that facilitates implementation of a circuit breaker, intended as a responder in a Scatter-Gather pattern. This module provides a framework for implementing:
- A circuit breaker if an API goes down
- A throttle for identical requests
- A backoff function for calling an API
It's built to be able to take advantage of AWS ElastiCache as a way to share a state between load-balanced server instances.
Installation
npm install byu-circuitbreaker
Usage
This module is quite flexible.
Be sure to require it like:
var CircuitBreaker = require('byu-circuitbreaker');
Then, you pass in configuration similar to this when you make a new circuit breaker:
var missionary_config = {
"sns_alert_arn": "arn:aws:sns:us-west-2:026968893061:missionary_alerts",
"use_elasticache": true,
"elasticache_endpoint": "missionarycbcluster.u5wkfp.0001.usw2.cache.amazonaws.com",
"elasticache_port": 6379,
"seconds_before_allowing_retry": 15,
"seconds_to_block_api": 300,
"populateLinks": require("./populateLinks"),
"prepareRequestOptions": require('./preparationFunction'),
"backoff_options": {
initial_delay: 1000,
max_delay: 5000,
num_retries_allowed: 5
},
"validationFunction": require('./validateMissions'),
"formatValues": require('./formatValues')
};
var missionary_cb = new CircuitBreaker(missionary_config);
Or as some examples of simpler configurations:
var simple_config_w_values_array = {
"prepareRequestOptions": function(byu_id, verifiedJWTs) {
return {
"url": "http://www.theapitocall.com"+byu_id
};
},
"populateLinks": function(byu_id) {
return {
"missions__info": {
"rel": "self",
"href": "https://api.byu.edu/cesapi/applicants/"+byu_id+"/missions",
"method": "GET",
"title": "missions.getMissions"
}
};
},
//Note: The metadata field will be determined automatically based on the size of this array:
"formatValues": function(full_api_response, byu_id, verifiedJWTs) {
var values = [];
var value = {
"applicant_id": {
"value": byu_id,
"api_type": "system",
"key": true,
"display_label": "BYU ID"
},
"mission_service": {
"value": "Y",
"api_type": "read-only",
"display_label": "Have you served or are you currently serving a mission?"
},
"mission_name": {
"value": full_api_response.body.mission_name,
"domain": "https://api.byu.edu/byuapi/meta/mission_names",
"api_type": "read-only",
"display_label": "Mission Name"
}
};
values.push(value);
return values;
}
};
var simple_cb = new CircuitBreaker(simple_config_w_values_array);
var simple_config_without_array = {
"wellknown_url": "https://api.byu.edu/.well-known/openid-configuration", //Used for checking JWTs
"prepareRequestOptions": function(byu_id, verifiedJWTs) {
return {
"url": "http://www.theapitocall.com"+byu_id
};
},
"populateLinks": function(byu_id) {
return {
"missions__info": {
"rel": "self",
"href": "https://api.byu.edu/cesapi/applicants/"+byu_id+"/missions",
"method": "GET",
"title": "missions.getMissions"
}
};
},
"formatValues": function(full_api_response, byu_id, verifiedJWTs) {
return {
"applicant_id": {
"value": byu_id,
"api_type": "system",
"key": true,
"display_label": "Applicant ID"
},
"mission_service": {
"value": "Y",
"api_type": "read-only",
"display_label": "Have you served or are you currently serving a mission?"
},
"mission_name": {
"value": full_api_response.body.mission_name,
"domain": "https://api.byu.edu/byuapi/meta/mission_names",
"api_type": "read-only",
"display_label": "Mission Name"
}
};
}
};
var simple_cb = new CircuitBreaker(simple_config_without_array);
and finally, you use it like this:
missionary_cb.processRequest(byu_id, verifiedJWTs)
.then(function(output) {
//Do something with output
});
Explanation of Configuration Options
- config - REQUIRED An object containing:
- sns_alert_arn -- Recommended. The SNS topic ARN to post urgent alerts to (like the API being down).
- use_elasticache -- A boolean defining if you intend to use AWS Elasticache.
Defaults to
false
. - elasticache_endpoint -- (Required if
use_elasticache = true
) The endpoint of an AWS ElastiCache cluster using Redis. - elasticache_port -- (Required if
use_elasticache = true
) The port for the AWS ElastiCache cluster using Redis. - seconds_before_allowing_retry -- Prevent identical requests for the specified period, due to the high likelihood of the information being cached.
Using
-1
will disable this feature, and using0
will permanently block retries. Defaults to15
seconds. - seconds_to_block_api -- If API is down (as determined by requests repeatedly timing out, unexpected responses, etc.), prevent future calls to the API for the specified period.
Using
-1
will disable this feature, and using0
will permanently block retries. Defaults to300
seconds. - populateLinks -- A function to prepare the links in our successful response.
Uses the format:
function(byu_id) { //Return an object that matches the "links" field of our API specifications return {}; }
- prepareRequestOptions -- REQUIRED A function to define how to call the API.
Uses the format:
If using an asynchronous function, this should return a promise instead. For the format of the object, see thefunction(byu_id, verifiedJWTs) { //Return an object that defines the options when making an API request. return { "url": "http://www.theapitocall.com/"+byu_id }; }
"options"
object at https://www.npmjs.com/package/request-promise. Some common parameters includemethod
,url
,headers
, andbody
. It's recommended to requirecustomErrors
and throw aPreparationFunctionError
if something goes wrong. To properly process responses, we will always use the parameters:{ simple: false, resolveWithFullResponse: true }
With those parameters, request-promise behaves almost identically to the regular request package. - backoff_options -- An object containing at least one of the following parameters to define an exponential backoff (10ms, 20ms, 40ms, etc.) when calling the API:
- initial_delay -- Delay in milliseconds before first retry. Defaults to
2000
ms. - max_delay -- A cap to the time between retries. Defaults to
10000
ms. - num_retries_allowed -- Number of retries without responses before we determine that the API is down.
Defaults to
5
.
- initial_delay -- Delay in milliseconds before first retry. Defaults to
- validationFunction -- A function to determine if the API gave us an expected response.
Uses the format:
Note that this should receive the response, even iffunction(full_api_response) { //Return if what API gives us is expected, or throw an error if unexpected response }
statusCode != 200
(unless API threw an error). It's recommended to requirecustomErrors
and throw anAPIError
so that circuit breaker can block the API if it's giving invalid info. Defaults to say every response is valid. formatValues -- REQUIRED The function to prepare our successful output. Uses the format:
function(full_api_response, byu_id, verifiedJWTs) { //Should return either an object or an array //The object will be merged with our response, the array will be placed in a "values" field in our response return []; }
It's recommended to require
customErrors
and throw aNotFoundError
if API gave us no useful info, which will correspond to a 404.
###Some Notes about ElastiCache
Elasticache is only accessible from an EC2 instance (whether you start that instance directly, or through Elastic Beanstalk).
So, if you're developing locally, you will need to make sure you haven't set use_elasticache = true
.
We use Redis-based ElastiCache Clusters. When setting them up, be sure they are in the same VPC as your EC2 instance(s) and that the security group has the appropriate inbound rule to allow access to ElastiCache. For further reference, see Amazon's documentation.
###Example
For an example of this in use, see /example/missionary_cb.js
.
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago