mempool: evict by lowest fee rate.

This commit is contained in:
Christopher Jeffrey 2017-03-05 02:03:14 -08:00
parent ef92070a28
commit 210ce4c767
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
2 changed files with 92 additions and 27 deletions

View File

@ -31,6 +31,7 @@ var Fees = require('./fees');
var Map = require('../utils/map');
var CoinView = require('../coins/coinview');
var Coins = require('../coins/coins');
var Heap = require('../utils/heap');
var VerifyError = errors.VerifyError;
var VerifyResult = errors.VerifyResult;
@ -357,58 +358,74 @@ Mempool.prototype._reset = co(function* reset() {
});
/**
* Ensure the size of the mempool stays below 300mb.
* @param {Hash} entryHash - TX that initiated the trim.
* Ensure the size of the mempool stays below `maxSize`.
* Evicts entries by timestamp and cumulative fee rate.
* @param {MempoolEntry} added
* @returns {Promise}
*/
Mempool.prototype.limitSize = function limitSize(entryHash) {
Mempool.prototype.limitSize = function limitSize(added) {
var maxSize = this.options.maxSize;
var expiryTime = this.options.expiryTime;
var now = util.now();
var trimmed = false;
var i, hashes, hash, end, entry;
var i, queue, entry, keys, hash, start;
if (this.getSize() <= this.options.maxSize)
if (this.size <= maxSize)
return trimmed;
hashes = this.getSnapshot();
end = util.now() - this.options.expiryTime;
queue = new Heap(cmpRate);
keys = this.getSnapshot();
for (i = 0; i < hashes.length; i++) {
hash = hashes[i];
start = util.hrtime();
for (i = 0; i < keys.length; i++) {
hash = keys[i];
entry = this.getEntry(hash);
if (!entry)
continue;
if (entry.ts >= end)
if (now < entry.ts + expiryTime) {
queue.insert(entry);
continue;
}
if (!trimmed && hash === entryHash)
if (entry === added)
trimmed = true;
this.evictEntry(entry);
if (this.getSize() <= this.options.maxSize)
return trimmed;
}
hashes = this.getSnapshot();
if (this.size <= maxSize)
return trimmed;
for (i = 0; i < hashes.length; i++) {
hash = hashes[i];
entry = this.getEntry(hash);
this.logger.debug(
'(bench) Heap mempool traversal: %d.',
util.hrtime(start));
if (!entry)
start = util.hrtime();
while (queue.size() > 0) {
entry = queue.shift();
hash = entry.hash('hex');
if (!this.hasTX(hash))
continue;
if (!trimmed && hash === entryHash)
trimmed = true;
this.evictEntry(entry);
if (this.getSize() <= this.options.maxSize)
return trimmed;
if (entry === added)
trimmed = true;
if (this.size <= maxSize - (maxSize / 10))
break;
}
this.logger.debug(
'(bench) Heap mempool map removal: %d.',
util.hrtime(start));
return trimmed;
};
@ -860,7 +877,7 @@ Mempool.prototype.insertTX = co(function* insertTX(tx) {
yield this.addEntry(entry, view);
// Trim size if we're too big.
if (this.limitSize(hash)) {
if (this.limitSize(entry)) {
throw new VerifyError(tx,
'insufficientfee',
'mempool full',
@ -1084,7 +1101,9 @@ Mempool.prototype.addEntry = co(function* addEntry(entry, view) {
if (this.fees)
this.fees.processTX(entry, this.chain.synced);
this.logger.debug('Added tx %s to mempool.', tx.txid());
this.logger.debug(
'Added %s to mempool (txs=%d).',
tx.txid(), this.totalTX);
this.cache.save(entry);
@ -1517,7 +1536,9 @@ Mempool.prototype.handleOrphans = co(function* handleOrphans(tx) {
throw err;
}
this.logger.debug('Resolved orphan %s in mempool.', orphan.txid());
this.logger.debug(
'Resolved orphan %s in mempool (txs=%d).',
orphan.txid(), this.totalTX);
}
return resolved;
@ -2517,6 +2538,40 @@ function removeFee(parent, child) {
parent.descSize -= child.descSize;
}
function cmpRate(a, b) {
var xf = a.fee;
var xs = a.size;
var yf = b.fee;
var ys = b.size;
var x, y;
if (useDesc(a)) {
xf = a.descFee;
xs = a.descSize;
}
if (useDesc(b)) {
yf = b.descFee;
ys = b.descSize;
}
x = xf * ys;
y = xs * yf;
if (x === y) {
x = a.ts;
y = b.ts;
}
return x - y;
}
function useDesc(a) {
var x = a.fee * a.descSize;
var y = a.descFee * a.size;
return y > x;
}
/*
* Expose
*/

View File

@ -132,6 +132,16 @@ MempoolEntry.fromTX = function fromTX(tx, view, height) {
return new MempoolEntry().fromTX(tx, view, height);
};
/**
* Calculate transaction hash.
* @param {String?} enc
* @returns {Hash}
*/
MempoolEntry.prototype.hash = function hash(enc) {
return this.tx.hash(enc);
};
/**
* Calculate priority, taking into account
* the entry height delta, modified size,