aboutsummaryrefslogtreecommitdiffstats
path: root/cmd
diff options
context:
space:
mode:
authorPéter Szilágyi <peterke@gmail.com>2018-09-21 18:15:09 +0800
committerPéter Szilágyi <peterke@gmail.com>2018-09-21 18:31:00 +0800
commitc528e3e3cf934f10ab23e603233c9b23848913a5 (patch)
treea34cd54b77e15cd60826c6fad763b904aebbf8b0 /cmd
parentab13cd992405f84dc48b7d49b54c5de06a7cd5ab (diff)
downloaddexon-c528e3e3cf934f10ab23e603233c9b23848913a5.tar
dexon-c528e3e3cf934f10ab23e603233c9b23848913a5.tar.gz
dexon-c528e3e3cf934f10ab23e603233c9b23848913a5.tar.bz2
dexon-c528e3e3cf934f10ab23e603233c9b23848913a5.tar.lz
dexon-c528e3e3cf934f10ab23e603233c9b23848913a5.tar.xz
dexon-c528e3e3cf934f10ab23e603233c9b23848913a5.tar.zst
dexon-c528e3e3cf934f10ab23e603233c9b23848913a5.zip
cmd/faucet: cache internal state, avoid sync-trashing les
Diffstat (limited to 'cmd')
-rw-r--r--cmd/faucet/faucet.go112
1 files changed, 67 insertions, 45 deletions
diff --git a/cmd/faucet/faucet.go b/cmd/faucet/faucet.go
index cfe4e45f1..e17ac36f5 100644
--- a/cmd/faucet/faucet.go
+++ b/cmd/faucet/faucet.go
@@ -199,6 +199,8 @@ type faucet struct {
keystore *keystore.KeyStore // Keystore containing the single signer
account accounts.Account // Account funding user faucet requests
+ head *types.Header // Current head header of the faucet
+ balance *big.Int // Current balance of the faucet
nonce uint64 // Current pending nonce of the faucet
price *big.Int // Current gas price to issue funds with
@@ -324,33 +326,30 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
nonce uint64
err error
)
- for {
- // Attempt to retrieve the stats, may error on no faucet connectivity
- ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
- head, err = f.client.HeaderByNumber(ctx, nil)
- if err == nil {
- balance, err = f.client.BalanceAt(ctx, f.account.Address, head.Number)
- if err == nil {
- nonce, err = f.client.NonceAt(ctx, f.account.Address, nil)
- }
+ for head == nil || balance == nil {
+ // Retrieve the current stats cached by the faucet
+ f.lock.RLock()
+ if f.head != nil {
+ head = types.CopyHeader(f.head)
}
- cancel()
+ if f.balance != nil {
+ balance = new(big.Int).Set(f.balance)
+ }
+ nonce = f.nonce
+ f.lock.RUnlock()
- // If stats retrieval failed, wait a bit and retry
- if err != nil {
- if err = sendError(conn, errors.New("Faucet offline: "+err.Error())); err != nil {
+ if head == nil || balance == nil {
+ // Report the faucet offline until initial stats are ready
+ if err = sendError(conn, errors.New("Faucet offline")); err != nil {
log.Warn("Failed to send faucet error to client", "err", err)
return
}
time.Sleep(3 * time.Second)
- continue
}
- // Initial stats reported successfully, proceed with user interaction
- break
}
// Send over the initial stats and the latest header
if err = send(conn, map[string]interface{}{
- "funds": balance.Div(balance, ether),
+ "funds": new(big.Int).Div(balance, ether),
"funded": nonce,
"peers": f.stack.Server().PeerCount(),
"requests": f.reqs,
@@ -520,6 +519,47 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
}
}
+// refresh attempts to retrieve the latest header from the chain and extract the
+// associated faucet balance and nonce for connectivity caching.
+func (f *faucet) refresh(head *types.Header) error {
+ // Ensure a state update does not run for too long
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+
+ // If no header was specified, use the current chain head
+ var err error
+ if head == nil {
+ if head, err = f.client.HeaderByNumber(ctx, nil); err != nil {
+ return err
+ }
+ }
+ // Retrieve the balance, nonce and gas price from the current head
+ var (
+ balance *big.Int
+ nonce uint64
+ price *big.Int
+ )
+ if balance, err = f.client.BalanceAt(ctx, f.account.Address, head.Number); err != nil {
+ return err
+ }
+ if nonce, err = f.client.NonceAt(ctx, f.account.Address, head.Number); err != nil {
+ return err
+ }
+ if price, err = f.client.SuggestGasPrice(ctx); err != nil {
+ return err
+ }
+ // Everything succeeded, update the cached stats and eject old requests
+ f.lock.Lock()
+ f.head, f.balance = head, balance
+ f.price, f.nonce = price, nonce
+ for len(f.reqs) > 0 && f.reqs[0].Tx.Nonce() < f.nonce {
+ f.reqs = f.reqs[1:]
+ }
+ f.lock.Unlock()
+
+ return nil
+}
+
// loop keeps waiting for interesting events and pushes them out to connected
// websockets.
func (f *faucet) loop() {
@@ -537,45 +577,27 @@ func (f *faucet) loop() {
go func() {
for head := range update {
// New chain head arrived, query the current stats and stream to clients
- var (
- balance *big.Int
- nonce uint64
- price *big.Int
- err error
- )
- ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
- balance, err = f.client.BalanceAt(ctx, f.account.Address, head.Number)
- if err == nil {
- nonce, err = f.client.NonceAt(ctx, f.account.Address, nil)
- if err == nil {
- price, err = f.client.SuggestGasPrice(ctx)
- }
+ timestamp := time.Unix(head.Time.Int64(), 0)
+ if time.Since(timestamp) > time.Hour {
+ log.Warn("Skipping faucet refresh, head too old", "number", head.Number, "hash", head.Hash(), "age", common.PrettyAge(timestamp))
+ continue
}
- cancel()
-
- // If querying the data failed, try for the next block
- if err != nil {
+ if err := f.refresh(head); err != nil {
log.Warn("Failed to update faucet state", "block", head.Number, "hash", head.Hash(), "err", err)
continue
- } else {
- log.Info("Updated faucet state", "block", head.Number, "hash", head.Hash(), "balance", balance, "nonce", nonce, "price", price)
}
// Faucet state retrieved, update locally and send to clients
- balance = new(big.Int).Div(balance, ether)
+ f.lock.RLock()
+ log.Info("Updated faucet state", "number", head.Number, "hash", head.Hash(), "age", common.PrettyAge(timestamp), "balance", f.balance, "nonce", f.nonce, "price", f.price)
- f.lock.Lock()
- f.price, f.nonce = price, nonce
- for len(f.reqs) > 0 && f.reqs[0].Tx.Nonce() < f.nonce {
- f.reqs = f.reqs[1:]
- }
- f.lock.Unlock()
+ balance := new(big.Int).Div(f.balance, ether)
+ peers := f.stack.Server().PeerCount()
- f.lock.RLock()
for _, conn := range f.conns {
if err := send(conn, map[string]interface{}{
"funds": balance,
"funded": f.nonce,
- "peers": f.stack.Server().PeerCount(),
+ "peers": peers,
"requests": f.reqs,
}, time.Second); err != nil {
log.Warn("Failed to send stats to client", "err", err)