mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-16 17:03:46 +00:00
accounts/keystore: don't scan all files, only file affected by event
This commit is contained in:
parent
be12392fba
commit
abd14b6ddc
3 changed files with 124 additions and 50 deletions
|
|
@ -228,8 +228,77 @@ func (ac *accountCache) close() {
|
|||
ac.mu.Unlock()
|
||||
}
|
||||
|
||||
func readAccount(path string, buf *bufio.Reader) *accounts.Account {
|
||||
|
||||
var key struct {
|
||||
Address string `json:"address"`
|
||||
}
|
||||
fd, err := os.Open(path)
|
||||
if err != nil {
|
||||
log.Trace("Failed to open keystore file", "path", path, "err", err)
|
||||
return nil
|
||||
}
|
||||
defer fd.Close()
|
||||
buf.Reset(fd)
|
||||
// Parse the 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)
|
||||
case (addr == common.Address{}):
|
||||
log.Debug("Failed to decode keystore key", "path", path, "err", "missing or zero address")
|
||||
default:
|
||||
return &accounts.Account{Address: addr, URL: accounts.URL{Scheme: KeyStoreScheme, Path: path}}
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// checkFile can be used when a file notification triggered some kind of change on a file
|
||||
// in the keystore directory. The method checks what happened (change/delete/remove/nothing) and
|
||||
// updates the keystore accordingly
|
||||
func (ac *accountCache) checkFile(path string) error {
|
||||
|
||||
start := time.Now()
|
||||
created, deleted, updated, err := ac.fileC.checkFile(path)
|
||||
|
||||
if err != nil {
|
||||
log.Debug("Failed to reload keystore contents", "err", err)
|
||||
return err
|
||||
}
|
||||
if !created && !deleted && !updated {
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
buf = new(bufio.Reader)
|
||||
)
|
||||
switch {
|
||||
case created:
|
||||
if a := readAccount(path, buf); a != nil {
|
||||
ac.add(*a)
|
||||
}
|
||||
case deleted:
|
||||
ac.deleteByFile(path)
|
||||
case updated:
|
||||
ac.deleteByFile(path)
|
||||
if a := readAccount(path, buf); a != nil {
|
||||
ac.add(*a)
|
||||
}
|
||||
}
|
||||
select {
|
||||
case ac.notify <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
end := time.Now()
|
||||
log.Trace("Handled keystore changes", "time", end.Sub(start))
|
||||
return nil
|
||||
}
|
||||
|
||||
// scanAccounts checks if any changes have occurred on the filesystem, and
|
||||
// updates the account cache accordingly
|
||||
|
||||
func (ac *accountCache) scanAccounts() error {
|
||||
// Scan the entire folder metadata for file changes
|
||||
creates, deletes, updates, err := ac.fileC.scan(ac.keydir)
|
||||
|
|
@ -240,40 +309,14 @@ func (ac *accountCache) scanAccounts() error {
|
|||
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)
|
||||
key struct {
|
||||
Address string `json:"address"`
|
||||
}
|
||||
)
|
||||
readAccount := func(path string) *accounts.Account {
|
||||
fd, err := os.Open(path)
|
||||
if err != nil {
|
||||
log.Trace("Failed to open keystore file", "path", path, "err", err)
|
||||
return nil
|
||||
}
|
||||
defer fd.Close()
|
||||
buf.Reset(fd)
|
||||
// Parse the 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)
|
||||
case (addr == common.Address{}):
|
||||
log.Debug("Failed to decode keystore key", "path", path, "err", "missing or zero address")
|
||||
default:
|
||||
return &accounts.Account{Address: addr, URL: accounts.URL{Scheme: KeyStoreScheme, Path: path}}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// Process all the file diffs
|
||||
start := time.Now()
|
||||
var (
|
||||
buf = new(bufio.Reader)
|
||||
)
|
||||
|
||||
for _, p := range creates.List() {
|
||||
if a := readAccount(p.(string)); a != nil {
|
||||
if a := readAccount(p.(string),buf); a != nil {
|
||||
ac.add(*a)
|
||||
}
|
||||
}
|
||||
|
|
@ -283,7 +326,7 @@ func (ac *accountCache) scanAccounts() error {
|
|||
for _, p := range updates.List() {
|
||||
path := p.(string)
|
||||
ac.deleteByFile(path)
|
||||
if a := readAccount(path); a != nil {
|
||||
if a := readAccount(path, buf); a != nil {
|
||||
ac.add(*a)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,35 @@ type fileCache struct {
|
|||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func (fc *fileCache) checkFile(path string) (created, deleted, updated bool, err error) {
|
||||
|
||||
fc.mu.Lock()
|
||||
defer fc.mu.Unlock()
|
||||
|
||||
created, deleted, updated, err = false, false, false, nil
|
||||
|
||||
previouslyKnown := fc.all.Has(path)
|
||||
|
||||
fi, err := os.Lstat(path)
|
||||
|
||||
if err != nil {
|
||||
//A file has been deleted, but it can be a file which we
|
||||
// were not previously watching.
|
||||
deleted = previouslyKnown && os.IsNotExist(err)
|
||||
return created, deleted, updated, err
|
||||
}
|
||||
|
||||
if skipKeyFile(fi) {
|
||||
log.Trace("Ignoring file on account scan", "path", path)
|
||||
return created, deleted, updated, err
|
||||
}
|
||||
|
||||
created = !previouslyKnown
|
||||
updated = previouslyKnown
|
||||
|
||||
return created, deleted, updated, nil
|
||||
}
|
||||
|
||||
// scan performs a new scan on the given directory, compares against the already
|
||||
// cached filenames, and returns file sets: creates, deletes, updates.
|
||||
func (fc *fileCache) scan(keyDir string) (set.Interface, set.Interface, set.Interface, error) {
|
||||
|
|
|
|||
|
|
@ -19,8 +19,6 @@
|
|||
package keystore
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/rjeczalik/notify"
|
||||
)
|
||||
|
|
@ -80,29 +78,33 @@ func (w *watcher) loop() {
|
|||
// Wait for file system events and reload.
|
||||
// When an event occurs, the reload call is delayed a bit so that
|
||||
// multiple events arriving quickly only cause a single reload.
|
||||
var (
|
||||
debounceDuration = 500 * time.Millisecond
|
||||
rescanTriggered = false
|
||||
debounce = time.NewTimer(0)
|
||||
)
|
||||
//var (
|
||||
//debounceDuration = 500 * time.Millisecond
|
||||
//rescanTriggered = false
|
||||
//debounce = time.NewTimer(0)
|
||||
//)
|
||||
// Ignore initial trigger
|
||||
if !debounce.Stop() {
|
||||
<-debounce.C
|
||||
}
|
||||
defer debounce.Stop()
|
||||
//if !debounce.Stop() {
|
||||
// <-debounce.C
|
||||
//}
|
||||
//defer debounce.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-w.quit:
|
||||
return
|
||||
case <-w.ev:
|
||||
case ei := <-w.ev:
|
||||
// fmt.Printf("Event: %v\n", ei)
|
||||
ei.Path()
|
||||
w.ac.checkFile(ei.Path())
|
||||
// w.ac.scanAccounts()
|
||||
// Trigger the scan (with delay), if not already triggered
|
||||
if !rescanTriggered {
|
||||
debounce.Reset(debounceDuration)
|
||||
rescanTriggered = true
|
||||
}
|
||||
case <-debounce.C:
|
||||
w.ac.scanAccounts()
|
||||
rescanTriggered = false
|
||||
// if !rescanTriggered {
|
||||
// debounce.Reset(debounceDuration)
|
||||
// rescanTriggered = true
|
||||
// }
|
||||
// case <-debounce.C:
|
||||
// w.ac.scanAccounts()
|
||||
// rescanTriggered = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue