Added a utxo cache.

This commit is contained in:
Chris Kleeschulte 2017-10-25 17:52:36 -04:00
parent 95d5441031
commit bdea9e8517
No known key found for this signature in database
GPG Key ID: 33195D27EF6BDB7F
3 changed files with 110 additions and 76 deletions

View File

@ -6,6 +6,7 @@ var async = require('async');
var TxController = require('./transactions'); var TxController = require('./transactions');
var Common = require('./common'); var Common = require('./common');
var _ = require('lodash'); var _ = require('lodash');
var LRU = require('lru-cache');
function AddressController(node) { function AddressController(node) {
this.node = node; this.node = node;
@ -14,6 +15,14 @@ function AddressController(node) {
this.txController = new TxController(node); this.txController = new TxController(node);
this.common = new Common({log: this.node.log}); this.common = new Common({log: this.node.log});
this._block = this.node.services.block; this._block = this.node.services.block;
this._utxoCache = new LRU({
max: 250,
maxAge: 1000 * 10
});
// limit the size of the request to about 100,000 bytes
// we'll use 34 bytes as the size of each address.
this._maxAddresses = Math.floor(100000/34);
} }
AddressController.prototype.show = function(req, res) { AddressController.prototype.show = function(req, res) {
@ -142,73 +151,25 @@ AddressController.prototype.check = function(addresses) {
AddressController.prototype.utxo = function(req, res) { AddressController.prototype.utxo = function(req, res) {
var self = this; var self = this;
var cachedUtxos = this._utxoCache.get(req.addr);
if (cachedUtxos) {
return res.jsonp(cachedUtxos);
}
this._address.getAddressUnspentOutputs(req.addr, {}, function(err, utxos) { this._address.getAddressUnspentOutputs(req.addr, {}, function(err, utxos) {
var results;
if(err) { if(err) {
return self.common.handleErrors(err, res); return self.common.handleErrors(err, res);
} else if (!utxos.length) { } else if (!utxos.length) {
return res.jsonp([]); results = [];
} }
res.jsonp(utxos.map(self.transformUtxo.bind(self))); results = utxos.map(self.transformUtxo.bind(self));
self._utxoCache.set(req.addr, results);
res.jsonp(results);
}); });
}; };
// this call could take a while to run depending on what addresses are used
// considering memory constraints, we will streaming out the results for addresses
// not necessarily in the order we received them
AddressController.prototype.multiutxo = function(req, res) {
var self = this;
var addresses;
if (_.isArray(req.addrs)) {
addresses = _.uniq(req.addrs);
} else {
addresses = req.addrs.split(',');
}
var addressesLeft = addresses.length;
var startedWriting = false;
res.write('[');
var sep = ',';
async.eachLimit(addresses, 4, function(addr, next) {
self._address.getAddressUnspentOutputs(addr, {}, function(err, utxos) {
if (err) {
return next(err);
}
if (addressesLeft-- > 0 && utxos.length > 0 && startedWriting) {
res.write(sep);
}
for(var i = 0; i < utxos.length; i++) {
startedWriting = true;
if (utxos.length - 1 === i) {
sep = '';
}
res.write(JSON.stringify(self.transformUtxo(utxos[i])) + sep);
}
sep = ',';
next();
});
}, function(err) {
if (err) {
return self.common.handleErrors(err, res);
}
res.write(']');
res.end();
});
};
AddressController.prototype.transformUtxo = function(utxoArg) { AddressController.prototype.transformUtxo = function(utxoArg) {
var utxo = { var utxo = {
address: utxoArg.address, address: utxoArg.address,
@ -238,6 +199,81 @@ AddressController.prototype._getTransformOptions = function(req) {
}; };
}; };
// this call could take a while to run depending on what addresses are used
// considering memory constraints, we will streaming out the results for addresses
// not necessarily in the order we received them
AddressController.prototype.multiutxo = function(req, res) {
var self = this;
var addresses;
if (_.isArray(req.addrs)) {
addresses = _.uniq(req.addrs);
} else {
addresses = _.compact(req.addrs.split(','));
}
var cacheKey = addresses.join('');
if (addresses.length > this._maxAddresses) {
return self.common.handleErrors(new Error('Too many addresses.'), res);
}
var cachedUtxos = this._utxoCache.get(cacheKey);
if (cachedUtxos) {
return res.jsonp(cachedUtxos);
}
var addressesLeft = addresses.length;
var startedWriting = false;
var cache = [];
res.write('[');
var sep = ',';
async.eachLimit(addresses, 4, function(addr, next) {
self._address.getAddressUnspentOutputs(addr, {}, function(err, utxos) {
if (err) {
return next(err);
}
if (addressesLeft-- > 0 && utxos.length > 0 && startedWriting) {
res.write(sep);
}
for(var i = 0; i < utxos.length; i++) {
startedWriting = true;
if (utxos.length - 1 === i) {
sep = '';
}
cache.push(utxos[i]);
res.write(JSON.stringify(self.transformUtxo(utxos[i])) + sep);
}
sep = ',';
next();
});
}, function(err) {
if (err) {
return self.common.handleErrors(err, res);
}
self._utxoCache.set(cacheKey, cache);
res.write(']');
res.end();
});
};
AddressController.prototype.multitxs = function(req, res) { AddressController.prototype.multitxs = function(req, res) {
var self = this; var self = this;

30
package-lock.json generated
View File

@ -1063,24 +1063,12 @@
"integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=" "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4="
}, },
"lru-cache": { "lru-cache": {
"version": "4.0.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.1.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz",
"integrity": "sha1-E0OVXtry432bnn7nJB4nxLn7cr4=", "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==",
"requires": { "requires": {
"pseudomap": "1.0.2", "pseudomap": "1.0.2",
"yallist": "2.0.0" "yallist": "2.1.2"
},
"dependencies": {
"pseudomap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
},
"yallist": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.0.0.tgz",
"integrity": "sha1-MGxUODXwnuGkyyO3vOmrNByRzdQ="
}
} }
}, },
"mime-db": { "mime-db": {
@ -1542,6 +1530,11 @@
} }
} }
}, },
"pseudomap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
},
"pump": { "pump": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/pump/-/pump-1.0.2.tgz", "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.2.tgz",
@ -2417,6 +2410,11 @@
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
}, },
"yallist": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
},
"yeast": { "yeast": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",

View File

@ -36,7 +36,7 @@
"body-parser": "^1.13.3", "body-parser": "^1.13.3",
"compression": "^1.6.1", "compression": "^1.6.1",
"lodash": "^2.4.1", "lodash": "^2.4.1",
"lru-cache": "^4.0.1", "lru-cache": "^4.1.1",
"morgan": "^1.7.0", "morgan": "^1.7.0",
"request": "^2.64.0" "request": "^2.64.0"
}, },