Skip to content
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

Merged
merged 6 commits into from
Jan 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
277 changes: 162 additions & 115 deletions lib/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, equivalent.

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
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

put and get equivalent, ok, like this explicit addition of the default false value for fromTrie.

}

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
}
}
Copy link
Member

Choose a reason for hiding this comment

The 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)
})
}
Copy link
Member

Choose a reason for hiding this comment

The 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)
})
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With addition of nextTick, ok.


/**
* 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)
}
Copy link
Member

Choose a reason for hiding this comment

The 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)
}
Copy link
Member

Choose a reason for hiding this comment

The 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
})
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything ok, looked for change equivalency and completeness.

}
}
Loading