Cache Engine

Promise and callback support

Because of some testing restiction on ES6 constructors, all the classes are namspaces with RedisUrlCache

Redis-url-cache supports either a promise API or a node.js callback API ( with err as a first callback parameter ). To use the promise, you need to require the CacheEnginePromise class, or the CacheEngineCB for the callback


    var CacheEngine = require('redis-url-cache').RedisUrlCache.CacheEnginePromise;

    //or

    var CacheEngine = require('redis-url-cache').RedisUrlCache.CacheEngineCB;

All subsequent usage will then use promise or callbacks depending on which class you used to initiate your cacheEngine object.

Please not that Instance and Cache Creator don't use promise.

methods

Title

constructor


    constructor(defaultDomain:string, instance: Instance)

The constructor is the same whenever you use the promise or the callback CacheEngine. It must takes a default domain and an initialized Instance.

url


    url(url: string): UrlCB
    url(url: string): UrlPromise

It will return a UrlCB or a UrlPromise.

The URL can be relative or absolute. If the URL is relative, then the default domain specified in the CacheEngine constructor will be used.

clearDomain


    //With callbacks
    clearDomain(domain?: string, callback: CallBackBooleanParam): void

    //with promises
    clearDomain(domain?: string): Promise

The domain is the full hostname (ex: http://www.domain.com:8080) you want all the cached url deleted from.

If no domain is specified, then the default domain will be cleared.

clearInstance


    //With callbacks
    clearInstance(callback: CallBackBooleanParam): void

    //with promises
    clearInstance(): Promise

This will delete all cached URL associated with the instance associated with the CacheEngine.

getStoredHostnames


    //With callbacks
    getStoredHostNames(callback: CallBackStringArrayParam): void

    //with promises
    getStoredHostNames(): Promise

Retrieves an array of string representing all the domain that contains URL cached.

getStoredURLs


    //With callbacks
    getStoredURLs(domain: string, callback: CallBackStringArrayParam): void

    //with promises
    getStoredURLs(domain: string): Promise

Retrieves all urls stored within the domain specified. If no domain is provided, it will return the urls stored for the default domain

Cache Creator

Introduction

You need to store the CacheRules at least once into the REDIS store to use the instance. This class will return a callback error if a CacheRule configuration already exists for this instance

A typical workflow:

  1. Create and store a new set of CacheRules and stores them into Redis with CacheRuleCreator
  2. Initialize an instance which loads the CacheRules definition from redis and pools the Redis connections
  3. Create a new CacheEngine linked to this instance

createCache


static createCache(instanceName: string, force: boolean, redisConfig: RedisStorageConfig, rules: CacheRules, callback: Function(error: string)): void
  • instanceName Is the name of the instance. It must be unique. If an instance alredy exists, the callback will return an error.
  • force If true and an instance already exists with the same name, it will delete the previous instance and replace the config.
  • redisConfig A Redis Config object.
  • rules A Cache Rules definition object.
  • callback A function the will return a null string if successfull, or an error string escribing the issue.

Cache Rule Manager

Usage

The CacheRuleManager class alows you to add/delete the CacheRule config stored into Redis

All 3 of addMaxAgeRule(), addAlwaysRule() and addNeverRule() share the same sets of parameters [domain, path and ignoreQuery].

When parsed by CacheEngine.url(), the url format is separated in two parts.
scheme:[//[user:password@]host[:port]] is matched against the domain
[/]path[?query] is matched against the path RegExp. The query will be ignored if the ignoreQuery parameter is set to true

A typical CacheRuleManager usage would be:


    // This assumes a config has already been created with CacheRuleCreator
    const instance = new Instance('INSTANCE1', redisConfig, err => {
        if(err) throw err;
        // retrives the cacheRuleManager
        const cacheRuleManager = instance.getManager();
        //adds an always rule
        cacheRuleManager.addAlwaysRule(/localhost/, /.*/, (err) => {
            if (err) throw err;
            //notifies all existing CacheEngine running in other process for the change
            instance.publish();
        }
    });

Methods

addMaxAgeRule


  addMaxAgeRule( domain: string | RegExp, path: RegExp, ttl: number, ignoreQuery?:boolean): void

All urls matching this rule will be cached with an expiration time specified by ttl in seconds

addAlwaysRule


  addAlwaysRule( domain: string | RegExp, path: RegExp, ignoreQuery?:boolean): void

All URLs matching this rule will be cached

addNeverRule


  addNeverRule( domain: string | RegExp, path: RegExp, ignoreQuery?:boolean): void

All URLs matching this rule will never be cached

removeAll


  removeAll(): void

This will clear all the cache rules from the config, making all the URLs matching the default strategy.

removeAllMaxAgeRules


  removeAllMaxAgeRules(): void

Removes all the maxAge CacheRules- good for unit testing integration

removeAllNeverRules


  removeAllNeverRules(): void

Removes all the never CacheRules- good for unit testing integration

removeAllAlwaysRules


  removeAllAlwaysRules(): void

Removes all the always CacheRules - good for unit testing integration

removeRule


  removeRule(domain:string | RegExp, rule: RegExp)

It will traverse all the never, always and maxAge rules and if i finds a matching rule, will remove it.

setDefault


  setDefault(strategy: string): void

When the url doesn't match any rules, then this classification will be applied. Can only accept always or never.

getRules


  getRules(): CacheRules

Returns an object representing the active rules used by the instance

Config

Redis Config

This JSON object defines how you connect to the REDIS data store. INternally, redis-url-cache uses node-redis. This config bject ise exactly the same as defined here : Redis.createClient() config

A very basic redis config object example:


var redisConfig = {
    host: 127.0.0.1,
    port: 6379s,
    socket_keepalive: true
}

Cache Rules

This JSON object defines which URL will be cached, which ones will be ignored and for how long

Under the hood the typescript definition for this config is:


export interface RegexRule {
    regex: RegExp,
    ignoreQuery?: boolean
}

export interface MaxAgeRegexRule extends RegexRule{
    maxAge: number
}

export interface DomainRule {
    domain: string | RegExp,
    rules: T[]
}

export interface CacheRules{
    maxAge: DomainRule[],
    always: DomainRule[],
    never: DomainRule[],
    default: string
}

For those with no typescript knowledge , here is an example:


var cacheRules = {
    maxAge: [
        {
            domain: /code\.jquery\.com/,
            rules: [
                regex: /.*/,
                maxAge: 2592000
            ]
        }
    ],
    always: [
        {
            domain: /.*/,
            rules: [
                { regex: /\.gif$/ }
            ]
        }
    ],
    never: [],
    default: 'never'
}

In this example, every url coming from cdn.jquery.com will be cached for 30 days, all url ending with .gif will be stored indefinitvely, and every thing else will be excluded from caching.

Instance

Usage

Several cacheEngines can share the same instance, which helps improves performances. Although it isn't not really possible yet to share cache between different instance, it is on the roadmap.


  //will loads the CacheRules related to REST caching strategies
  const instance1 = new Instance('REST', redisConf, err =>{});

  //will loads the CacheRules related to HTML caching strategies
  const instance2 = new Instance('HTML', redisConf, err =>{});

  // cacheEngine 1 & 2 will share the same instance
  const cacheEngine1 = new CacheEngine('http://domain1.com', instance1);
  const cacheEngine2 = new CacheEngine('http://domain2.com', instance1);

  // While cacheEngine 3 will have its own storage space
  const cacheEngine3 = new CacheEngine('http://domain1.com', instance2);



This helps to logically cluster Redis storage as well as complex Cache Rule configs in very complex scenarios

methods

constructor


 constructor(instanceName: string, redisConfig: RedisStorageConfig, config: InstanceConfig, function(err) {})

Loads a CacheRules from Redis with the redisConfig provided.

InstanceConfig Defines some mandatory behavior


interface InstanceConfig {

    on_existing_regex?: 'error' | 'ignore' | 'replace'
    on_publish_update?: boolean // when the cacheEngine.publish( is called, will scann all existing created url objects, and re-calculate the url's category
}

on_existing_regex: when adding a regex with CacheRuleManager's methods, and an existing Regex is found, either replace it, ignore it, or throws an error

on_publish_update: when the instance.publish() is called, it will scan all existing created url objects, and re-calculate the url's category
This can have some performances issues in the scenario when your script creates plenty of url in the same runtime, because it forces the CacheEngine to keep an array listing all urls created in memory.
Example:


  const instance = new Instance('HTML', redisConfig, {on_publish_update: true, (err) => {})
  const cacheEngine = new CacheEngine('localhost', instance);
  const url = cacheEngine.url('/someUrl.html');

//Let's assume the that CacheRule Config mark this url's category as never
  const cacheRuleManager = instance.getManager();

//Let's change this url's category dynamically
  cacheRuleManager.removeAll()
  cacheRuleManager.setDefault('always');
  instance.publish();
  // at this stage, url's category is now 'always'
  // withouth on_publish_update, we would have to manually run to update it.
  url.setCacheCategory();

getInstanceName


getInstanceName(): string

Returns the name of the instance

getConfig


getConfig(): CacheRules

Returns the cache rules definition asociated with this instance. It is usefull for cache rule backup - or if you need to build a cache rule UI

getManager


getManager(): CacheRuleManager

Returns the Cache rule Manager associated with this instance

A typical usage would be to edit the cache rules definition.

publish


pubish():void

When the Cache Rule Manager associated with this instance has been modified, calling this method will save the changes in the storage and propagate the changes to all open instances

Example



    //File A.js on server 1
    var instance = new Instance('STORAGE', redisConfig, instanceConfig, function(err) {
        if(err) throw err;

        var ruleManager = instance.getManager();
        ruleManager.setDefault('always'); // We assume it was previously set to `never`

        instance.publish();

        // Will propagate config changes to all instanciated instances STORAGE.

    }

    //File B.js on server 2
    var instance = new Instance('STORAGE', redisConfig, instanceConfig, function(err) {
       if(err) throw err;

        var cacheEngine = new CacheEngineCB('domain', instance);

        var url1 = new url('http://domain.com/some-un-matched-url.html');
        console.log(url1.getCategory());
        // `never`

        setTimeout( function(){
            var url2 = new url('http://domain.com/some-un-matched-url.html');
            console.log(url2.getCategory());
            // `always`
        }, 10000);

        setTimeout( function(){
            console.log(url1.getCategory());
            // `always` or `never` depending on the instanceConfig.on_publish_update value
        });
    }

Url

Promises and callbacks support

UrlCB is the class name for callback support while UrlPromise is the one associated for promises

Internally, UrlPromise ses UrlCB's code. For this reason, we will document only UrlCB's API.

To transpose UrlCb API to UrlPromise API, it is easy to remember that we follow the node.js classic callback parameter order (err, value), with err first.

The promise will return the value on success, and the error on rejection. Example:



var urlCB = cacheEngineCB.url('http://someURL');
var urlPromise = cacheEnginePromise.url('http://someURL');


urlCB.has( (err: string | Error, has: boolean) => {
    //do something
});

urlPromise.has().then ( (has: boolean) => {
    //do something on success
}, (err: string | Error) => {
    //do something on error
});


urlCB.get( (err: string | Error, content: IGetResults) => {
//do something
});

urlPromise.get().then ( (content: IGetResults) => {
    //do something on success
}, (err: string | Error) => {
    //do something on error
});

setters and getters

delete


url.delete( callback: Function(err: string) )

Deletes a stored url. Will return an error string if the url doesnt exists or for a redis error

has


url.has( callback: Fuction(err: string, has: boolean) )

Checks if the url is cached.

get


url.get( callback: Function(err: string, content: IGetResults) )

Retrieves the cached url. If the url is not cached, will return an 'err'

The structure of the content: IGetResults is:


interface IGetResults {
    content: string, //the URL file content
    createdOn: number, // Cacheing timestamp
    extra: any //Any SONmobject stored alongside the URL content
}

set


url.set(htmlContent: string, extra: Object, force: boolean, callback: Function(err, result: boolean))

Conditionally stores a URL depending on matching rules

htmlContent
A non empty string describing the URL content. So far, only UTF-8 has been tested.

extra
A JSON object representing extra informations. Can be anything, headers, dependecies....

force
A boolean. If set to true, URL that are already cached are rewritten, URL with the never category are stored anyway, and URL with a TTL will get their ttl updated in addition of the content updated.

callback(error: string, result: boolean)
A function that returns an error string on error, and a boolean value. True when stored successfully and False in the following scenario:
  • The url category is 'never' and force = false
  • The url is already cached and force = false.

other methods

getDomain


url.getDomain(): string

returns the domain name associated with this URL

getUrl


url.getUrl(): string

Returns the url path. For example:


var url = cacheEngine.url('http://domain.com/some/path.html');

console.log(url.getUrl());
//  /some/path.html

getCategory


url.getCategory(): string

Returns the computed url category. Either always, never or maxAge

getTTL


url.getTTL(): number

Returns a positive number (seconds) if the url category is maxAge, 0 otherwise

getInstanceName


url.getInstanceName(): string

returns the instanceName associated with this urlCB | urlPromise object

setCacheCategory