Sync wallet files to disk as needed, instead of waiting for a timer.

While fixing this code, the dirty flag was also cleared so that
unneeded syncs wouldn't be needed later.  The dirty flag set and sync
was also added for the 'getnewaddress' handler, as it was previously
missing.
This commit is contained in:
Josh Rickmar 2013-10-15 16:55:28 -04:00
parent a5c7079fdf
commit 2782b7815e
3 changed files with 39 additions and 54 deletions

23
cmd.go
View file

@ -107,17 +107,15 @@ func (s *BtcWalletStore) Rollback(height int64, hash *btcwire.ShaHash) {
func (w *BtcWallet) Rollback(height int64, hash *btcwire.ShaHash) {
w.UtxoStore.Lock()
w.UtxoStore.dirty = w.UtxoStore.dirty || w.UtxoStore.s.Rollback(height, hash)
if w.UtxoStore.dirty {
AddDirtyAccount(w)
}
w.UtxoStore.Unlock()
w.TxStore.Lock()
w.TxStore.dirty = w.TxStore.dirty || w.TxStore.s.Rollback(height, hash)
if w.TxStore.dirty {
AddDirtyAccount(w)
}
w.TxStore.Unlock()
if err := w.writeDirtyToDisk(); err != nil {
log.Errorf("cannot sync dirty wallet: %v", err)
}
}
// walletdir returns the directory path which holds the wallet, utxo,
@ -455,8 +453,11 @@ func (w *BtcWallet) newBlockTxHandler(result interface{}, e *btcjson.Error) bool
txs := w.TxStore.s
w.TxStore.s = append(txs, t)
w.TxStore.dirty = true
AddDirtyAccount(w)
w.TxStore.Unlock()
if err = w.writeDirtyToDisk(); err != nil {
log.Errorf("cannot sync dirty wallet: %v", err)
}
}()
// Do not add output to utxo store if spent.
@ -476,8 +477,11 @@ func (w *BtcWallet) newBlockTxHandler(result interface{}, e *btcjson.Error) bool
w.UtxoStore.Lock()
w.UtxoStore.s = append(w.UtxoStore.s, u)
w.UtxoStore.dirty = true
AddDirtyAccount(w)
w.UtxoStore.Unlock()
if err = w.writeDirtyToDisk(); err != nil {
log.Errorf("cannot sync dirty wallet: %v", err)
}
confirmed := w.CalculateBalance(6)
unconfirmed := w.CalculateBalance(0) - confirmed
NotifyWalletBalance(frontendNotificationMaster, w.name, confirmed)
@ -538,9 +542,6 @@ func main() {
// Begin generating new IDs for JSON calls.
go JSONIDGenerator(NewJSONID)
// Begin wallet to disk syncer.
go DirtyAccountUpdater()
for {
replies := make(chan error)
done := make(chan int)

View file

@ -334,6 +334,10 @@ func GetNewAddress(reply chan []byte, msg *btcjson.Message) {
ReplyError(reply, msg.Id, &e)
return
}
w.dirty = true
if err = w.writeDirtyToDisk(); err != nil {
log.Errorf("cannot sync dirty wallet: %v", err)
}
w.ReqNewTxsForAddress(addr)
ReplySuccess(reply, msg.Id, addr)
} else {
@ -493,8 +497,10 @@ func SendFrom(reply chan []byte, msg *btcjson.Message) {
modified := w.UtxoStore.s.Remove(inputs)
if modified {
w.UtxoStore.dirty = true
AddDirtyAccount(w)
w.UtxoStore.Unlock()
if err := w.writeDirtyToDisk(); err != nil {
log.Errorf("cannot sync dirty wallet: %v", err)
}
// Notify all frontends of new account balances.
confirmed := w.CalculateBalance(6)
@ -638,8 +644,10 @@ func SendMany(reply chan []byte, msg *btcjson.Message) {
modified := w.UtxoStore.s.Remove(inputs)
if modified {
w.UtxoStore.dirty = true
AddDirtyAccount(w)
w.UtxoStore.Unlock()
if err := w.writeDirtyToDisk(); err != nil {
log.Errorf("cannot sync dirty wallet: %v", err)
}
// Notify all frontends of new account balances.
confirmed := w.CalculateBalance(6)
@ -778,7 +786,9 @@ func CreateEncryptedWallet(reply chan []byte, msg *btcjson.Message) {
bw.Track()
wallets.m[wname] = bw
AddDirtyAccount(bw)
if err := bw.writeDirtyToDisk(); err != nil {
log.Errorf("cannot sync dirty wallet: %v", err)
}
ReplySuccess(reply, msg.Id, nil)
}

View file

@ -23,43 +23,11 @@ import (
"time"
)
var dirtyAccountSet = make(map[*BtcWallet]bool)
var addDirtyAccount = make(chan *BtcWallet)
// DirtyAccountUpdater is responsible for listening for listens for new
// dirty wallets (changed in memory with updaets not yet saved to disk)
// to add to dirtyAccountSet. This is designed to run as a single goroutine.
func DirtyAccountUpdater() {
timer := time.Tick(time.Minute)
for {
select {
case w := <-addDirtyAccount:
dirtyAccountSet[w] = true
case <-timer:
for w := range dirtyAccountSet {
if err := w.writeDirtyToDisk(); err != nil {
log.Errorf("cannot sync dirty wallet '%v': %v", w.name, err)
} else {
delete(dirtyAccountSet, w)
log.Infof("removed dirty wallet '%v'", w.name)
}
}
}
}
}
// AddDirtyAccount adds w to a set of items to be synced to disk. The
// dirty flag must still be set on the various dirty elements of the
// account (wallet, transactions, and/or utxos) or nothing will be
// written to disk during the next scheduled sync.
func AddDirtyAccount(w *BtcWallet) {
addDirtyAccount <- w
}
// writeDirtyToDisk checks for the dirty flag on an account's wallet,
// txstore, and utxostore, writing them to disk if any are dirty.
func (w *BtcWallet) writeDirtyToDisk() error {
fmt.Println("entered")
// Temporary files append the current time to the normal file name.
// In caes of failure, the most recent temporary file can be inspected
// for validity, and moved to replace the main file.
@ -72,8 +40,8 @@ func (w *BtcWallet) writeDirtyToDisk() error {
// Wallet
if w.dirty {
w.mtx.RLock()
defer w.mtx.RUnlock()
w.mtx.Lock()
defer w.mtx.Unlock()
tmpfilepath := wfilepath + "-" + timeStr
tmpfile, err := os.Create(tmpfilepath)
if err != nil {
@ -88,12 +56,14 @@ func (w *BtcWallet) writeDirtyToDisk() error {
if err = os.Rename(tmpfilepath, wfilepath); err != nil {
return err
}
w.dirty = false
}
// Transactions
if w.TxStore.dirty {
w.TxStore.RLock()
defer w.TxStore.RUnlock()
w.TxStore.Lock()
defer w.TxStore.Unlock()
tmpfilepath := txfilepath + "-" + timeStr
tmpfile, err := os.Create(tmpfilepath)
if err != nil {
@ -108,12 +78,14 @@ func (w *BtcWallet) writeDirtyToDisk() error {
if err = os.Rename(tmpfilepath, txfilepath); err != nil {
return err
}
w.TxStore.dirty = false
}
// UTXOs
if w.UtxoStore.dirty {
w.UtxoStore.RLock()
defer w.UtxoStore.RUnlock()
w.UtxoStore.Lock()
defer w.UtxoStore.Unlock()
tmpfilepath := utxofilepath + "-" + timeStr
tmpfile, err := os.Create(tmpfilepath)
if err != nil {
@ -128,6 +100,8 @@ func (w *BtcWallet) writeDirtyToDisk() error {
if err = os.Rename(tmpfilepath, utxofilepath); err != nil {
return err
}
w.UtxoStore.dirty = false
}
return nil