Nupital Pomp Mesmerises

    layered-loader
    TypeScript icon, indicating that this package has built-in type declarations

    2.0.0 • Public • Published

    layered-loader

    npm version Coverage Status

    Data source agnostic data loader with support for caching and fallback data sources.

    Use-cases

    This library has three main goals:

    1. Provide transparent, highly flexible caching mechanism for data retrieval operations;
    2. Enable fallback mechanism for retrieving data when alternate sources exist;
    3. Prevent redundant data retrieval in high-load systems.

    Basic concepts

    There are three entity types used by layered-loader:

    1. LoadingOperation - defined procedure of retrieving data from one or more data sources, using a single key. LoadingOperation is composed of Loaders and Caches.
    2. Loader - data source, capable of retrieving data for a given key, synchronously or asynchronously.
    3. Cache - data source, capable of both storing and retrieving data for a given key, synchronously or asynchronously.
    • layered-loader will try loading the data from the first cache or loader defined for the LoadingOperation. In case undefined value is the result of retrieval, next data source in sequence will be used, until there is either a value, or there are no more sources available;
    • null is considered to be a value, and if the data source returns it, subsequent data source will not be queried for data;
    • If non-last data source throws an error, it is handled using configured ErrorHandler. If the last data source throws an error, and there are no remaining fallback data sources, an error will be thrown by the LoadingOperation.
    • If there are any caches preceding the data source that returned a value, all of them will be updated with that value;
    • If there is an ongoing retrieval operation for the given key, promise for that retrieval will be reused and returned as a result of loadingOperation.get, instead of starting a new retrieval.

    Basic example

    Let's define a loader, which will be the primary source of truth, and two levels of caching:

    import Redis from 'ioredis'
    import { RedisCache } from 'layered-loader/dist/lib/redis'
    import { InMemoryCache } from "layered-loader/dist/lib/memory";
    import type { Loader } from 'layered-loader'
    
    const ioRedis = new Redis({
        host: 'localhost',
        port: 6379,
        password: 'sOmE_sEcUrE_pAsS',
    })
    
    class ClassifiersLoader implements Loader<Record<string, any>> {
        private readonly db: Knex
        name = 'Classifiers DB loader'
        isCache = false
    
        constructor(db: Knex) {
            this.db = db
        }
    
        async get(key: string): Promise<Record<string, any> | undefined | null> {
            const results = await this.db('classifiers').select('*').where({
                id: parseInt(key),
            })
            return results[0]
        }
    }
    
    
    const operation = new LoadingOperation<string>([
      new InMemoryCache<string>({
        ttlInMsecs: 1000 * 60,
        maxItems: 100,
      }), // this cache will be checked first
      new RedisCache(ioRedis, {
        json: true, // this instructs loader to serialize passed objects as string and deserialize them back to objects
        ttlInMsecs: 1000 * 60 * 10,
      }), // this cache will be checked second
      new ClassifiersLoader(db), // this will be used if neither cache has the requested data
    ])
    
    // If cache is empty, but there is data in the DB, after this operation is completed, both caches will be populated
    const classifier = await operation.get('1')

    LoadingOperation API

    LoadingOperation has the following config parameters:

    • throwIfUnresolved: boolean - if true, error will be thrown if all data sources return undefined;
    • cacheUpdateErrorHandler: LoaderErrorHandler - error handler to use when cache throws an error during update;
    • loadErrorHandler: LoaderErrorHandler - error handler to use when non-last data source throws an error during data retrieval.

    LoadingOperation provides following methods:

    • invalidateCacheFor(key: string): Promise<void> - expunge all entries for given key from all caches of this LoadingOperation;
    • invalidateCache(): Promise<void> - expunge all entries from all caches of this LoadingOperation;
    • get(key: string): Promise<T> - sequentially attempt to retrieve data for specified key from all caches and loaders, in an order in which they were passed to the LoadingOperation constructor.

    Provided caches

    InMemoryCache

    In order to use InMemoryCache, please install tiny-lru:

    npm install 'tiny-lru' --save

    InMemoryCache stores data in-memory, and as such is not recommended for usage in highly-distributed systems for data that is modified frequently. It has following configuration options:

    • ttlInMsecs: number - after how many milliseconds data will be considered stale and will no longer be accessible;
    • maxItems: number - what is the maximum amount of items that can be retained in cache at the same time. Entries are being evoked based on LRU (least recently used) principle.

    RedisCache

    RedisCache uses Redis for caching data, and is recommended for highly distributed systems. It requires an active instance of ioredis, and it does not perform any connection/disconnection operations on its own. It has following configuration options:

    • ttlInMsecs: number - after how many milliseconds data will be considered stale and will no longer be accessible;
    • json: boolean - if false, all passed data will be sent to Redis and returned from it as-is. If true, it will be serialized using JSON.stringify and deserialized, using JSON.parse.

    Supported environments

    LoadingOperation and InMemoryCache support both browser and Node.js (ES6 support is required); RedisCache only works in Node.js; Tree-shaking should work correctly, but wasn't tested.

    Install

    npm i layered-loader

    DownloadsWeekly Downloads

    93

    Version

    2.0.0

    License

    MIT

    Unpacked Size

    29.5 kB

    Total Files

    24

    Last publish

    Collaborators

    • kibertoad