distributed-timers

1.0.6 • Public • Published

distributed-timers

npm package to implement timers in distributed, fault-tolerant, stateless and scalable manner, using various databases for persistence.

npm version GitHub

Why distributed-timers ?

  • The setTimeout functionality offered by Node.js is stateful and not fault tolerant, it will loose all the timers' info once the server goes down.
  • The setTimeout functionality offered by Node.js is not scalable.
  • We may use some persistence layer to store timer-info but without proper logic, it might become inconsistent during horizontal scaling.
  • distributed-timers provides the easiest way of handling timers using efficient databases & schemas and makes sure that the server remains stateless, fault tolerant, consistenct and scalable.
Note : for now only AWS dynamoDb is supported. Next step is to support other databases like Cassandra.

How it works ?

  • It connects to the database(specified by the user) creates a separate table for storing timers.
  • It scans the table periodically and picks the timers which are about to expire, loads them into memory.
  • As the timers expire, it sends the callback to the specified application.
  • Scalabilty : It uses a single database to store timers' info , Many server-instances can read from it.
  • Fault-tolerance : It is as fault tolerant as the underlying database. Timers are not stored in memory.
  • Statelessness : Since it does not store timer info in memory, it is stateless. Just before the expire-time it loads timer info into memory, even if a failure happens at that time, another instance can safely give callback.
  • Consistency : Coding logic is written to ensure all sort of consistency.

How to use ?

  • If running on localhost, download and run dynamodb-local
  • Install the package : npm install distributed-timers --save
  • Initializing :
var timerLib = require('distributed-timers');

const databaseParams = {
  database: 'dynamoDB',                 // type of database (only AWS dynamodb is supported for now). 
  region: 'local',                 // region of database.
  isLocal: true,                        // whether database is running on local machine or cloud.
  tableName: 'timers',                  // tablename for new table that is to be created for timers.
  readCapacityUnitsForGSI: 5,           // read capacity for Global Secondry Index.  (dynamoDb specific)
  writeCapacityUnitsForGSI: 5,          // write capacity for Global Secondry Index. (dynamoDb specific)
  readCapacityUnits: 5,                 // read capacity for main table.             (dynamoDb specific)
  writeCapacityUnits: 5,                // wite capacity for main table.             (dynamoDb specific)
  scanPeriod: 60000,                    // periodic scan period in miliseconds        
  endpoint: 'http://localhost:8000',    // endpoint 
  verboseLogging: true                  // option to select whether to enable detailed logs.
};

timerLib.init(databaseParams);

  • Creating new timer :
var timerParams = {
  timeout: 1000,                             //timeout in seconds
  callback: {
    url: 'https:localhost:8000/dummy-url',   // url to which the callback has to be sent
    message: {                               // JSON object to be received when the timer expires.
      key: 'value'                           
    }
  }
let timerId;                                // id to keep track of timer stored in db.
timerLib.create(timerParams)
        .then(function(timerInfo){
            timerId = timerIdInfo.timerId;
        })
        .catch((err)=>{
            console.log(err)
        })
  
  • Updating an existing timer :
var updateTimerParams = {
  timerId: <timerId>,                       //timerId of timer to be updated
  timeout: 1000,
  callback: {
    url: 'https:localhost:8000/dummy-url',
    message: {
      key: 'value'
    }
  }
};

timerLib.update(updateTimerParams)
         .then(function(timerInfo){
             timerId = timerInfo.timerId;       // upon a successful update timerId will be equal to updateTimerParams.timerId
         })
         .catch((err)=>{
             console.log(err)
         })

  • Deleting an existing timer :
var timerIdTobeDeleted = <timerId>            // timerId of timer to be deleted
timerLib.delete(timerIdTobeDeleted)
       .catch((err)=>{
           console.log(err)
       })

  • Adding timers in batch :
const batchTimerRequest = [
  { timerReference : "first-timer-of-batch",         // new-field , when timerIDs are returned it helps keep track of timer to which it points to.
    timerInfo :{
      timeout: 10000,                                //timeout in seconds
      callback: {
        url: 'https:localhost:8000/dummy-url',       // url to which the callback has to be sent
        message: {                                   // JSON object to be received when the timer expires.
          key: 'batch1'
        }
      }
    }
  },
  { timerReference : "second-timer-of-batch",
    timerInfo :{
      timeout: 10000,
      callback: {
        url: 'https:localhost:8000/dummy-url',
        message: {
          key: 'batch2'
        }
      }
    }
  }
];


timerLib.batchCreate(batchTimerRequest)
        .then(function(response){
            batchResponse = response;                // it will be of the form [{timerReference1 : timerId1 },{....}] 
        })
        .catch((err)=>{
            console.log(err)
        })
  • Deleting timers in batch :
var timerIdsTobeDeleted = [<timerId1>,<...>]            // list of timerIds of timers to be deleted
timerLib.batchDelete(timerIdsTobeDeleted)
       .catch((err)=>{
           console.log(err)
       })

Package Sidebar

Install

npm i distributed-timers

Weekly Downloads

2

Version

1.0.6

License

none

Unpacked Size

33.3 kB

Total Files

11

Last publish

Collaborators

  • dubeyabhi07