-
Notifications
You must be signed in to change notification settings - Fork 765
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add unit tests for cache, convert it to es6 class #427
Changes from all commits
506fb90
8020a1a
ed3a7da
00e5fb7
51070a4
1482946
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,144 +3,191 @@ const Tree = require('functional-red-black-tree') | |
const Account = require('ethereumjs-account') | ||
const async = require('async') | ||
|
||
var Cache = module.exports = function (trie) { | ||
this._cache = Tree() | ||
this._checkpoints = [] | ||
this._trie = trie | ||
} | ||
|
||
Cache.prototype.put = function (key, val, fromTrie) { | ||
var modified = !fromTrie | ||
this._update(key, val, modified, false) | ||
} | ||
|
||
// returns the queried account or an empty account | ||
Cache.prototype.get = function (key) { | ||
var account = this.lookup(key) | ||
if (!account) { | ||
account = new Account() | ||
module.exports = class Cache { | ||
constructor (trie) { | ||
this._cache = Tree() | ||
this._checkpoints = [] | ||
this._trie = trie | ||
} | ||
return account | ||
} | ||
|
||
// returns the queried account or undefined | ||
Cache.prototype.lookup = function (key) { | ||
key = key.toString('hex') | ||
/** | ||
* Puts account to cache under its address. | ||
* @param {Buffer} key - Address of account | ||
* @param {Account} val - Account | ||
* @param {bool} [fromTrie] | ||
*/ | ||
put (key, val, fromTrie = false) { | ||
const modified = !fromTrie | ||
this._update(key, val, modified, false) | ||
} | ||
|
||
var it = this._cache.find(key) | ||
if (it.node) { | ||
var account = new Account(it.value.val) | ||
/** | ||
* Returns the queried account or an empty account. | ||
* @param {Buffer} key - Address of account | ||
*/ | ||
get (key) { | ||
let account = this.lookup(key) | ||
if (!account) { | ||
account = new Account() | ||
} | ||
return account | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
|
||
Cache.prototype._lookupAccount = function (address, cb) { | ||
var self = this | ||
self._trie.get(address, function (err, raw) { | ||
if (err) return cb(err) | ||
var account = new Account(raw) | ||
cb(null, account) | ||
}) | ||
} | ||
/** | ||
* Returns the queried account or undefined. | ||
* @param {buffer} key - Address of account | ||
*/ | ||
lookup (key) { | ||
key = key.toString('hex') | ||
|
||
const it = this._cache.find(key) | ||
if (it.node) { | ||
const account = new Account(it.value.val) | ||
return account | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok. |
||
|
||
Cache.prototype.getOrLoad = function (key, cb) { | ||
var self = this | ||
var account = this.lookup(key) | ||
if (account) { | ||
async.nextTick(cb, null, account) | ||
} else { | ||
self._lookupAccount(key, function (err, account) { | ||
/** | ||
* Looks up address in underlying trie. | ||
* @param {Buffer} address - Address of account | ||
* @param {Function} cb - Callback with params (err, account) | ||
*/ | ||
_lookupAccount (address, cb) { | ||
this._trie.get(address, (err, raw) => { | ||
if (err) return cb(err) | ||
self._update(key, account, false, false) | ||
var account = new Account(raw) | ||
cb(null, account) | ||
}) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok. |
||
} | ||
|
||
Cache.prototype.warm = function (addresses, cb) { | ||
var self = this | ||
// shim till async supports iterators | ||
var accountArr = [] | ||
addresses.forEach(function (val) { | ||
if (val) accountArr.push(val) | ||
}) | ||
|
||
async.eachSeries(accountArr, function (addressHex, done) { | ||
var address = Buffer.from(addressHex, 'hex') | ||
self._lookupAccount(address, function (err, account) { | ||
if (err) return done(err) | ||
self._update(address, account, false, false) | ||
done() | ||
/** | ||
* Looks up address in cache, if not found, looks it up | ||
* in the underlying trie. | ||
* @param {Buffer} key - Address of account | ||
* @param {Function} cb - Callback with params (err, account) | ||
*/ | ||
getOrLoad (key, cb) { | ||
const account = this.lookup(key) | ||
if (account) { | ||
async.nextTick(cb, null, account) | ||
} else { | ||
this._lookupAccount(key, (err, account) => { | ||
if (err) return cb(err) | ||
this._update(key, account, false, false) | ||
cb(null, account) | ||
}) | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With addition of |
||
|
||
/** | ||
* Warms cache by loading their respective account from trie | ||
* and putting them in cache. | ||
* @param {Array} addresses - Array of addresses | ||
* @param {Function} cb - Callback | ||
*/ | ||
warm (addresses, cb) { | ||
// shim till async supports iterators | ||
var accountArr = [] | ||
addresses.forEach((val) => { | ||
if (val) accountArr.push(val) | ||
}) | ||
}, cb) | ||
} | ||
|
||
Cache.prototype.flush = function (cb) { | ||
var it = this._cache.begin | ||
var self = this | ||
var next = true | ||
async.whilst(function () { | ||
return next | ||
}, function (done) { | ||
if (it.value && it.value.modified) { | ||
it.value.modified = false | ||
it.value.val = it.value.val.serialize() | ||
self._trie.put(Buffer.from(it.key, 'hex'), it.value.val, function () { | ||
next = it.hasNext | ||
it.next() | ||
async.eachSeries(accountArr, (addressHex, done) => { | ||
var address = Buffer.from(addressHex, 'hex') | ||
this._lookupAccount(address, (err, account) => { | ||
if (err) return done(err) | ||
this._update(address, account, false, false) | ||
done() | ||
}) | ||
} else if (it.value && it.value.deleted) { | ||
it.value.modified = false | ||
it.value.deleted = false | ||
it.value.val = (new Account()).serialize() | ||
self._trie.del(Buffer.from(it.key, 'hex'), function () { | ||
}, cb) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok. |
||
|
||
/** | ||
* Flushes cache by updating accounts that have been modified | ||
* and removing accounts that have been deleted. | ||
* @param {function} cb - Callback | ||
*/ | ||
flush (cb) { | ||
const it = this._cache.begin | ||
let next = true | ||
async.whilst(() => next, (done) => { | ||
if (it.value && it.value.modified) { | ||
it.value.modified = false | ||
it.value.val = it.value.val.serialize() | ||
this._trie.put(Buffer.from(it.key, 'hex'), it.value.val, () => { | ||
next = it.hasNext | ||
it.next() | ||
done() | ||
}) | ||
} else if (it.value && it.value.deleted) { | ||
it.value.modified = false | ||
it.value.deleted = false | ||
it.value.val = (new Account()).serialize() | ||
this._trie.del(Buffer.from(it.key, 'hex'), () => { | ||
next = it.hasNext | ||
it.next() | ||
done() | ||
}) | ||
} else { | ||
next = it.hasNext | ||
it.next() | ||
done() | ||
}) | ||
} else { | ||
next = it.hasNext | ||
it.next() | ||
async.nextTick(done) | ||
} | ||
}, cb) | ||
} | ||
async.nextTick(done) | ||
} | ||
}, cb) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also a `nextTick' addition, ok. |
||
|
||
Cache.prototype.checkpoint = function () { | ||
this._checkpoints.push(this._cache) | ||
} | ||
/** | ||
* Marks current state of cache as checkpoint, which can | ||
* later on be reverted or commited. | ||
*/ | ||
checkpoint () { | ||
this._checkpoints.push(this._cache) | ||
} | ||
|
||
Cache.prototype.revert = function () { | ||
this._cache = this._checkpoints.pop(this._cache) | ||
} | ||
/** | ||
* Revert changes to cache last checkpoint (no effect on trie). | ||
*/ | ||
revert () { | ||
this._cache = this._checkpoints.pop() | ||
} | ||
|
||
Cache.prototype.commit = function () { | ||
this._checkpoints.pop() | ||
} | ||
/** | ||
* Commits to current state of cache (no effect on trie). | ||
*/ | ||
commit () { | ||
this._checkpoints.pop() | ||
} | ||
|
||
Cache.prototype.clear = function () { | ||
this._cache = Tree() | ||
} | ||
/** | ||
* Clears cache. | ||
*/ | ||
clear () { | ||
this._cache = Tree() | ||
} | ||
|
||
Cache.prototype.del = function (key) { | ||
this._update(key, new Account(), false, true) | ||
} | ||
/** | ||
* Marks address as deleted in cache. | ||
* @params {Buffer} key - Address | ||
*/ | ||
del (key) { | ||
this._update(key, new Account(), false, true) | ||
} | ||
|
||
Cache.prototype._update = function (key, val, modified, deleted) { | ||
key = key.toString('hex') | ||
var it = this._cache.find(key) | ||
if (it.node) { | ||
this._cache = it.update({ | ||
val: val, | ||
modified: modified, | ||
deleted: deleted | ||
}) | ||
} else { | ||
this._cache = this._cache.insert(key, { | ||
val: val, | ||
modified: modified, | ||
deleted: deleted | ||
}) | ||
_update (key, val, modified, deleted) { | ||
key = key.toString('hex') | ||
const it = this._cache.find(key) | ||
if (it.node) { | ||
this._cache = it.update({ | ||
val: val, | ||
modified: modified, | ||
deleted: deleted | ||
}) | ||
} else { | ||
this._cache = this._cache.insert(key, { | ||
val: val, | ||
modified: modified, | ||
deleted: deleted | ||
}) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Everything ok, looked for change equivalency and completeness. |
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, equivalent.