mempool: evict by lowest fee rate.
This commit is contained in:
parent
ef92070a28
commit
210ce4c767
@ -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
|
||||
*/
|
||||
|
||||
@ -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,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user