5.0.4 • Published 7 years ago

byu-circuitbreaker v5.0.4

Weekly downloads
4
License
Apache-2.0
Repository
github
Last release
7 years ago

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 using 0 will permanently block retries. Defaults to 15 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 using 0 will permanently block retries. Defaults to 300 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:
      function(byu_id, verifiedJWTs) {
          //Return an object that defines the options when making an API request.
          return {
              "url": "http://www.theapitocall.com/"+byu_id
          };
      }
      If using an asynchronous function, this should return a promise instead. For the format of the object, see the "options" object at https://www.npmjs.com/package/request-promise. Some common parameters include method, url, headers, and body. It's recommended to require customErrors and throw a PreparationFunctionError 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.
    • validationFunction -- A function to determine if the API gave us an expected response. Uses the format:
      function(full_api_response) {
          //Return if what API gives us is expected, or throw an error if unexpected response
      }
      Note that this should receive the response, even if statusCode != 200 (unless API threw an error). It's recommended to require customErrors and throw an APIError 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 a NotFoundError 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.

5.0.4

7 years ago

5.0.3

7 years ago

5.0.2

7 years ago

5.0.1

7 years ago

5.0.0

7 years ago

4.0.1

7 years ago

4.0.0

7 years ago

3.5.2

7 years ago

3.5.1

7 years ago

3.5.0

7 years ago

3.4.6

7 years ago

3.4.5

7 years ago

3.4.4

7 years ago

3.4.3

7 years ago

3.4.2

7 years ago

3.4.1

7 years ago

3.4.0

7 years ago

3.3.2

7 years ago

3.3.1

7 years ago

3.3.0

7 years ago

3.2.0

7 years ago

3.1.0

7 years ago

3.0.1

7 years ago

3.0.0

7 years ago

2.1.1

7 years ago

2.1.0

7 years ago

2.0.1

7 years ago

2.0.0

7 years ago

1.0.2

7 years ago

1.0.1

7 years ago

0.3.0

7 years ago

0.2.1

7 years ago

0.2.0

7 years ago

0.1.3

7 years ago

0.1.2

7 years ago

0.1.1

7 years ago

0.1.0

7 years ago