node-machine
Advanced tools
Comparing version 0.0.3 to 0.0.4
@@ -9,2 +9,3 @@ /** | ||
var _searchChildModules = require('./search-child-modules'); | ||
var _hashFn = require('./hash-function'); | ||
@@ -203,4 +204,234 @@ /** | ||
this.fn(this._configuredInputs, switchback(this._configuredExits), this._dependencies); | ||
// | ||
// TODO: | ||
// Caching should only be allowed on machines which identify | ||
// as "referentially transparent" | ||
// TODO: | ||
// Eventually, dont' allow `_cache` to be hard-coded as an input- | ||
// instead reserve it as a private thing because it's weird otherwise. | ||
// When this is ready to be open-sourced properly, would be a pretty | ||
// horrible/confusing bug if someone not in the know tried to use the | ||
// `_cache` input in their own machine. | ||
this._cache = this._cache || this._configuredInputs._cache; | ||
delete this._configuredInputs._cache; | ||
var _cache = this._cache; | ||
// If `_cache` is not valid, null it out. | ||
if ( | ||
! ( | ||
_.isObject(_cache) && | ||
_.isObject(_cache.model) && | ||
_.isFunction(_cache.model.find) && | ||
_.isFunction(_cache.model.create) | ||
) | ||
) { | ||
_cache = false; | ||
} | ||
// Otherwise if _cache IS valid, normalize it and apply defaults | ||
else { | ||
_.defaults(_cache, { | ||
// Default TTL (i.e. "max age") is 3 hours | ||
ttl: 3 * 60 * 60 * 1000, | ||
// The maximum # of old cache entries to keep for each | ||
// unique combination of input values for a particular | ||
// machine type. | ||
// When this # is exceeded, a query will be performed to | ||
// wipe them out. Increasing this value increases memory | ||
// usage but reduces the # of extra gc queries. Reducing | ||
// this value minimizes memory usage but increases the # of | ||
// gc queries. | ||
// | ||
// When set to 0, performs an extra destroy() query every time | ||
// a cache entry expires (and this is actually fine in most cases, | ||
// since that might happen only a few times per day) | ||
maxOldEntriesBuffer: 0, | ||
// By default, the "success" exit is cached | ||
exit: 'success' | ||
}); | ||
// Pre-calculate the expiration date so we only do it once | ||
// (and also so it uses a consistent timestamp since the code | ||
// below is asynchronous) | ||
_cache.expirationDate = new Date( (new Date()) - _cache.ttl); | ||
} | ||
// Cache lookup | ||
// | ||
// The cache uses a hash function to create a unique id for every distinct | ||
// input configuration (these hash sums are only unique per-machine-type.) | ||
var hash; | ||
// | ||
// Note that this means the machine cache is global not to any particular | ||
// machine instance, but to the machine type itself-- that is, within the | ||
// scope of the cache model. | ||
// | ||
// e.g. | ||
// The cached result of a given set of inputs for a particular type of machine | ||
// will be the same for all instances of that machine using the same cache model | ||
// (which could be shared across devices/routes/processes/apps/servers/clouds/continents) | ||
// | ||
// Old cache entries are garbage collected every time a cache miss occurs | ||
// (also see `maxOldEntriesBuffer` option above for details) | ||
var oldCacheEntries; | ||
// Now attempt a cache lookup, if configured to do so: | ||
var self = this; | ||
(function _tryCacheLookup (giveUpAndRunMachineCb) { | ||
if (!_cache) return giveUpAndRunMachineCb(); | ||
// Run hash function to calculate appropriate `hash` criterion | ||
_hashFn(self._configuredInputs, function (err, _calculatedHash){ | ||
// Cache lookup encountered fatal error | ||
// (could not calculate unique hash for configured input values) | ||
if (err) return giveUpAndRunMachineCb(err); | ||
// Hashsum was calculated successfully | ||
hash = _calculatedHash; | ||
// Now hit the provided cache model | ||
// (remember- we know it's valid because we validated/normalized | ||
// our `_cache` variable ahead of time) | ||
_cache.model.find({ | ||
where: { | ||
createdAt: { | ||
'>': _cache.expirationDate | ||
}, | ||
hash: hash | ||
}, | ||
sort: 'createdAt DESC', | ||
limit: 1 | ||
}) | ||
.exec(function (err, cached) { | ||
// Cache lookup encountered fatal error | ||
if (err) return giveUpAndRunMachineCb(err); | ||
// Cache hit | ||
else if (cached.length && cached[0].data) { | ||
var newestCacheEntry = cached[0]; | ||
return switchback(self._configuredExits)(null, newestCacheEntry.data); | ||
} | ||
// Cache miss | ||
else return giveUpAndRunMachineCb(); | ||
}); | ||
}); | ||
})(function _cacheNotAnOption_justRunMachine (err){ | ||
if (err) { | ||
// If cache lookup encounters a fatal error, emit a warning | ||
// but continue (i.e. we fall back to running the machine) | ||
self.warn(err); | ||
} | ||
//////////////////////////////////////////////////////////////////// | ||
// || | ||
// \/ Notice that this code does not run the machine | ||
// (we don't need to wait for garbage collection to do that) | ||
// | ||
//////////////////////////////////////////////////////////////////// | ||
// | ||
// If `> maxOldEntriesBuffer` matching cache records exist, then | ||
// it's time to clean up. Go ahead and delete all the old unused | ||
// cache entries except the newest one | ||
// | ||
// (TODO: pull all this craziness out into a separate module/file) | ||
if (_cache) { | ||
_cache.model.count({ | ||
where: { | ||
createdAt: { | ||
'<=': _cache.expirationDate | ||
}, | ||
hash: hash | ||
} | ||
}).exec(function (err, numOldCacheEntries){ | ||
if (err) { | ||
// If this garbage collection diagnostic query encounters a fatal error, | ||
// emit a warning and then don't do anything else for now. | ||
self.warn(err); | ||
} | ||
if (numOldCacheEntries > _cache.maxOldEntriesBuffer) { | ||
// console.log('gc();'); | ||
_cache.model.destroy({ | ||
where: { | ||
createdAt: { | ||
'<=': _cache.expirationDate | ||
}, | ||
hash: hash | ||
}, | ||
sort: 'createdAt DESC', | ||
skip: _cache.maxOldEntriesBuffer | ||
}).exec(function (err, oldCacheEntries) { | ||
if (err) { | ||
// If garbage collection encounters a fatal error, emit a warning | ||
// and then don't do anything else for now. | ||
self.warn(err); | ||
} | ||
// Garbage collection was successful. | ||
// console.log('-gc success-'); | ||
}); | ||
} | ||
}); | ||
} | ||
//////////////////////////////////////////////////////////////////// | ||
// Intercept the exits | ||
var interceptedExits = _.reduce(self._configuredExits, function (m,fn,exitName){ | ||
// Don't mess with this exit if: | ||
// • the cache is not enabled for this machine at all | ||
// • this exit is not the cacheable exit | ||
// • if the hash value could not be calculated before (in which case | ||
// we can't safely cache this thing because we don't have a unique | ||
// identifier) | ||
if (!_cache || !hash || exitName !== _cache.exit) { | ||
m[exitName] = fn; | ||
return m; | ||
} | ||
// If cacheable exit is traversed, cache the output | ||
m[exitName] = function (data) { | ||
_cache.model.create({ | ||
hash: hash, | ||
data: data | ||
}) | ||
.exec(function(err) { | ||
if (err) { | ||
// If cache write encounters an error, emit a warning but | ||
// continue with sending back the output | ||
self.warn(err); | ||
} | ||
fn(data); | ||
}); | ||
}; | ||
return m; | ||
}, {}); | ||
// Run the machine | ||
self.fn(self._configuredInputs, switchback(interceptedExits), self._dependencies); | ||
}); | ||
return this; | ||
@@ -227,2 +458,4 @@ }; | ||
}).apply(this, Array.prototype.slice.call(arguments)); | ||
return this; | ||
}; | ||
@@ -248,2 +481,4 @@ | ||
}).apply(this, Array.prototype.slice.call(arguments)); | ||
return this; | ||
}; | ||
@@ -250,0 +485,0 @@ |
{ | ||
"name": "node-machine", | ||
"version": "0.0.3", | ||
"version": "0.0.4", | ||
"description": "Configure and execute a machine using inputs and exits", | ||
@@ -14,4 +14,5 @@ "main": "index.js", | ||
"lodash": "~2.4.1", | ||
"partial-apply": "^0.1.0" | ||
"partial-apply": "^0.1.0", | ||
"object-hash": "^0.3.0" | ||
} | ||
} |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
25634
13
679
0
4
+ Addedobject-hash@^0.3.0
+ Addedobject-hash@0.3.0(transitive)