UNPKG

flipr-etcd

Version:

Flipr source to retrieve config from etcd: a distributed, consistent, partition-tolerant key/value store

83 lines (68 loc) 2.44 kB
'use strict'; var EventEmitter = require('events').EventEmitter; var util = require('util'); var _ = require('lodash'); var Etcd = require('node-etcd'); var memoize = require('memoizee'); var parseClientResult = require('./parse-client-result'); module.exports = FliprEtcd; function FliprEtcd(options) { //Force instantiation if(!(this instanceof FliprEtcd)) return new FliprEtcd(options); EventEmitter.call(this); options = _.defaults({}, options, { hosts: [], host: '127.0.0.1', port: '4001', directory: 'default', key: 'config' }); var _changeActionsToIgnore = ['delete', 'compareAndDelete', 'expire']; var _fullKey = util.format('flipr/%s/%s', options.directory, options.key); var _lastValidConfig; this.events = { beforeChange: 'before-change', flush: 'flush', afterChange: 'after-change', error: 'error' }; if(options.hosts.length > 0) this.client = new Etcd(options.hosts, options.ssl); else this.client = new Etcd(options.host, options.port, options.ssl); this.getConfig = memoize(_.bind(function(cb){ this.client.get(_fullKey, _.bind(function(err, result){ result = parseClientResult(err, _lastValidConfig, result); if(!result.config) { _.partial(this.emit, this.events.error).apply(this, result.errors || []); return cb(err || _.last(result.errors) || new Error('Unexpected error while getting config from etcd.')); } return cb(null, result.config); }, this)); }, this), {async: true}); //Calling getConfig will memoize the cache. this.preload = this.getConfig; this.flush = _.bind(function(){ this.getConfig.clear(); this.emit(this.events.flush); }, this); this.watcher = this.client.watcher(_fullKey); this.watcher.on('change', _.bind(function(result){ if(!result || !result.action) { this.emit(this.events.error, new Error('Unrecognized result from etcd watcher.'), result); return; } if(_changeActionsToIgnore.indexOf(result.action) > -1) return; this.emit(this.events.beforeChange); //TODO: change should be able to update the cached config without //making the second call to preload. this.flush(); this.preload(_.bind(function(){ this.emit(this.events.afterChange); }, this)); }, this)); this.watcher.on('error', _.partial(this.emit, this.events.error)); } util.inherits(FliprEtcd, EventEmitter);