Skip to content

Commit c528e3e

Browse files
committed
cmd/faucet: cache internal state, avoid sync-trashing les
1 parent ab13cd9 commit c528e3e

File tree

1 file changed

+67
-45
lines changed

1 file changed

+67
-45
lines changed

cmd/faucet/faucet.go

Lines changed: 67 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,8 @@ type faucet struct {
199199

200200
keystore *keystore.KeyStore // Keystore containing the single signer
201201
account accounts.Account // Account funding user faucet requests
202+
head *types.Header // Current head header of the faucet
203+
balance *big.Int // Current balance of the faucet
202204
nonce uint64 // Current pending nonce of the faucet
203205
price *big.Int // Current gas price to issue funds with
204206

@@ -324,33 +326,30 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
324326
nonce uint64
325327
err error
326328
)
327-
for {
328-
// Attempt to retrieve the stats, may error on no faucet connectivity
329-
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
330-
head, err = f.client.HeaderByNumber(ctx, nil)
331-
if err == nil {
332-
balance, err = f.client.BalanceAt(ctx, f.account.Address, head.Number)
333-
if err == nil {
334-
nonce, err = f.client.NonceAt(ctx, f.account.Address, nil)
335-
}
329+
for head == nil || balance == nil {
330+
// Retrieve the current stats cached by the faucet
331+
f.lock.RLock()
332+
if f.head != nil {
333+
head = types.CopyHeader(f.head)
336334
}
337-
cancel()
335+
if f.balance != nil {
336+
balance = new(big.Int).Set(f.balance)
337+
}
338+
nonce = f.nonce
339+
f.lock.RUnlock()
338340

339-
// If stats retrieval failed, wait a bit and retry
340-
if err != nil {
341-
if err = sendError(conn, errors.New("Faucet offline: "+err.Error())); err != nil {
341+
if head == nil || balance == nil {
342+
// Report the faucet offline until initial stats are ready
343+
if err = sendError(conn, errors.New("Faucet offline")); err != nil {
342344
log.Warn("Failed to send faucet error to client", "err", err)
343345
return
344346
}
345347
time.Sleep(3 * time.Second)
346-
continue
347348
}
348-
// Initial stats reported successfully, proceed with user interaction
349-
break
350349
}
351350
// Send over the initial stats and the latest header
352351
if err = send(conn, map[string]interface{}{
353-
"funds": balance.Div(balance, ether),
352+
"funds": new(big.Int).Div(balance, ether),
354353
"funded": nonce,
355354
"peers": f.stack.Server().PeerCount(),
356355
"requests": f.reqs,
@@ -520,6 +519,47 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
520519
}
521520
}
522521

522+
// refresh attempts to retrieve the latest header from the chain and extract the
523+
// associated faucet balance and nonce for connectivity caching.
524+
func (f *faucet) refresh(head *types.Header) error {
525+
// Ensure a state update does not run for too long
526+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
527+
defer cancel()
528+
529+
// If no header was specified, use the current chain head
530+
var err error
531+
if head == nil {
532+
if head, err = f.client.HeaderByNumber(ctx, nil); err != nil {
533+
return err
534+
}
535+
}
536+
// Retrieve the balance, nonce and gas price from the current head
537+
var (
538+
balance *big.Int
539+
nonce uint64
540+
price *big.Int
541+
)
542+
if balance, err = f.client.BalanceAt(ctx, f.account.Address, head.Number); err != nil {
543+
return err
544+
}
545+
if nonce, err = f.client.NonceAt(ctx, f.account.Address, head.Number); err != nil {
546+
return err
547+
}
548+
if price, err = f.client.SuggestGasPrice(ctx); err != nil {
549+
return err
550+
}
551+
// Everything succeeded, update the cached stats and eject old requests
552+
f.lock.Lock()
553+
f.head, f.balance = head, balance
554+
f.price, f.nonce = price, nonce
555+
for len(f.reqs) > 0 && f.reqs[0].Tx.Nonce() < f.nonce {
556+
f.reqs = f.reqs[1:]
557+
}
558+
f.lock.Unlock()
559+
560+
return nil
561+
}
562+
523563
// loop keeps waiting for interesting events and pushes them out to connected
524564
// websockets.
525565
func (f *faucet) loop() {
@@ -537,45 +577,27 @@ func (f *faucet) loop() {
537577
go func() {
538578
for head := range update {
539579
// New chain head arrived, query the current stats and stream to clients
540-
var (
541-
balance *big.Int
542-
nonce uint64
543-
price *big.Int
544-
err error
545-
)
546-
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
547-
balance, err = f.client.BalanceAt(ctx, f.account.Address, head.Number)
548-
if err == nil {
549-
nonce, err = f.client.NonceAt(ctx, f.account.Address, nil)
550-
if err == nil {
551-
price, err = f.client.SuggestGasPrice(ctx)
552-
}
580+
timestamp := time.Unix(head.Time.Int64(), 0)
581+
if time.Since(timestamp) > time.Hour {
582+
log.Warn("Skipping faucet refresh, head too old", "number", head.Number, "hash", head.Hash(), "age", common.PrettyAge(timestamp))
583+
continue
553584
}
554-
cancel()
555-
556-
// If querying the data failed, try for the next block
557-
if err != nil {
585+
if err := f.refresh(head); err != nil {
558586
log.Warn("Failed to update faucet state", "block", head.Number, "hash", head.Hash(), "err", err)
559587
continue
560-
} else {
561-
log.Info("Updated faucet state", "block", head.Number, "hash", head.Hash(), "balance", balance, "nonce", nonce, "price", price)
562588
}
563589
// Faucet state retrieved, update locally and send to clients
564-
balance = new(big.Int).Div(balance, ether)
590+
f.lock.RLock()
591+
log.Info("Updated faucet state", "number", head.Number, "hash", head.Hash(), "age", common.PrettyAge(timestamp), "balance", f.balance, "nonce", f.nonce, "price", f.price)
565592

566-
f.lock.Lock()
567-
f.price, f.nonce = price, nonce
568-
for len(f.reqs) > 0 && f.reqs[0].Tx.Nonce() < f.nonce {
569-
f.reqs = f.reqs[1:]
570-
}
571-
f.lock.Unlock()
593+
balance := new(big.Int).Div(f.balance, ether)
594+
peers := f.stack.Server().PeerCount()
572595

573-
f.lock.RLock()
574596
for _, conn := range f.conns {
575597
if err := send(conn, map[string]interface{}{
576598
"funds": balance,
577599
"funded": f.nonce,
578-
"peers": f.stack.Server().PeerCount(),
600+
"peers": peers,
579601
"requests": f.reqs,
580602
}, time.Second); err != nil {
581603
log.Warn("Failed to send stats to client", "err", err)

0 commit comments

Comments
 (0)