accounts/keystore: lock file cache during scan, minor polish

This commit is contained in:
Péter Szilágyi
2017-11-20 12:21:52 +02:00
parent 8a79836044
commit 6810674de9
2 changed files with 124 additions and 91 deletions

View File

@ -20,7 +20,6 @@ import (
"bufio"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sort"
@ -75,13 +74,6 @@ type accountCache struct {
fileC fileCache
}
// fileCache is a cache of files seen during scan of keystore
type fileCache struct {
all *set.SetNonTS // list of all files
mtime time.Time // latest mtime seen
mu sync.RWMutex
}
func newAccountCache(keydir string) (*accountCache, chan struct{}) {
ac := &accountCache{
keydir: keydir,
@ -236,66 +228,22 @@ func (ac *accountCache) close() {
ac.mu.Unlock()
}
// scanFiles performs a new scan on the given directory, compares against the already
// cached filenames, and returns file sets: new, missing , modified
func (fc *fileCache) scanFiles(keyDir string) (set.Interface, set.Interface, set.Interface, error) {
t0 := time.Now()
files, err := ioutil.ReadDir(keyDir)
t1 := time.Now()
if err != nil {
return nil, nil, nil, err
}
fc.mu.RLock()
prevMtime := fc.mtime
fc.mu.RUnlock()
filesNow := set.NewNonTS()
moddedFiles := set.NewNonTS()
var newMtime time.Time
for _, fi := range files {
modTime := fi.ModTime()
path := filepath.Join(keyDir, fi.Name())
if skipKeyFile(fi) {
log.Trace("Ignoring file on account scan", "path", path)
continue
}
filesNow.Add(path)
if modTime.After(prevMtime) {
moddedFiles.Add(path)
}
if modTime.After(newMtime) {
newMtime = modTime
}
}
t2 := time.Now()
fc.mu.Lock()
// Missing = previous - current
missing := set.Difference(fc.all, filesNow)
// New = current - previous
newFiles := set.Difference(filesNow, fc.all)
// Modified = modified - new
modified := set.Difference(moddedFiles, newFiles)
fc.all = filesNow
fc.mtime = newMtime
fc.mu.Unlock()
t3 := time.Now()
log.Debug("FS scan times", "list", t1.Sub(t0), "set", t2.Sub(t1), "diff", t3.Sub(t2))
return newFiles, missing, modified, nil
}
// scanAccounts checks if any changes have occurred on the filesystem, and
// updates the account cache accordingly
func (ac *accountCache) scanAccounts() error {
newFiles, missingFiles, modified, err := ac.fileC.scanFiles(ac.keydir)
t1 := time.Now()
// Scan the entire folder metadata for file changes
creates, deletes, updates, err := ac.fileC.scan(ac.keydir)
if err != nil {
log.Debug("Failed to reload keystore contents", "err", err)
return err
}
if creates.Size() == 0 && deletes.Size() == 0 && updates.Size() == 0 {
return nil
}
// Create a helper method to scan the contents of the key files
var (
buf = new(bufio.Reader)
keyJSON struct {
buf = new(bufio.Reader)
key struct {
Address string `json:"address"`
}
)
@ -308,9 +256,9 @@ func (ac *accountCache) scanAccounts() error {
defer fd.Close()
buf.Reset(fd)
// Parse the address.
keyJSON.Address = ""
err = json.NewDecoder(buf).Decode(&keyJSON)
addr := common.HexToAddress(keyJSON.Address)
key.Address = ""
err = json.NewDecoder(buf).Decode(&key)
addr := common.HexToAddress(key.Address)
switch {
case err != nil:
log.Debug("Failed to decode keystore key", "path", path, "err", err)
@ -321,47 +269,30 @@ func (ac *accountCache) scanAccounts() error {
}
return nil
}
// Process all the file diffs
start := time.Now()
for _, p := range newFiles.List() {
path, _ := p.(string)
a := readAccount(path)
if a != nil {
for _, p := range creates.List() {
if a := readAccount(p.(string)); a != nil {
ac.add(*a)
}
}
for _, p := range missingFiles.List() {
path, _ := p.(string)
ac.deleteByFile(path)
for _, p := range deletes.List() {
ac.deleteByFile(p.(string))
}
for _, p := range modified.List() {
path, _ := p.(string)
a := readAccount(path)
for _, p := range updates.List() {
path := p.(string)
ac.deleteByFile(path)
if a != nil {
if a := readAccount(path); a != nil {
ac.add(*a)
}
}
t2 := time.Now()
end := time.Now()
select {
case ac.notify <- struct{}{}:
default:
}
log.Trace("Handled keystore changes", "time", t2.Sub(t1))
log.Trace("Handled keystore changes", "time", end.Sub(start))
return nil
}
func skipKeyFile(fi os.FileInfo) bool {
// Skip editor backups and UNIX-style hidden files.
if strings.HasSuffix(fi.Name(), "~") || strings.HasPrefix(fi.Name(), ".") {
return true
}
// Skip misc special files, directories (yes, symlinks too).
if fi.IsDir() || fi.Mode()&os.ModeType != 0 {
return true
}
return false
}