mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-02-12 20:16:27 +00:00
* Register external beacon stream header why: This will be used to sync the peers against. * Update total coverage book-keeping for 100% roll-over details: Provide commonly available/used function * Replace best pivot by beacon stream tracker details: Beacon stream header cache will be updated by external chain monitor via RPC. This cached header will then be used to sync the pivot.
231 lines
6.9 KiB
Nim
231 lines
6.9 KiB
Nim
# Nimbus - Fetch account and storage states from peers efficiently
|
|
#
|
|
# Copyright (c) 2021 Status Research & Development GmbH
|
|
# Licensed under either of
|
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
|
# http://www.apache.org/licenses/LICENSE-2.0)
|
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
|
# http://opensource.org/licenses/MIT)
|
|
# at your option. This file may not be copied, modified, or distributed
|
|
# except according to those terms.
|
|
|
|
import
|
|
std/[strformat, strutils],
|
|
chronos,
|
|
chronicles,
|
|
eth/[common, p2p],
|
|
stint,
|
|
../../../utils/prettify,
|
|
../../misc/timer_helper
|
|
|
|
{.push raises: [Defect].}
|
|
|
|
logScope:
|
|
topics = "snap-tick"
|
|
|
|
type
|
|
TickerStats* = object
|
|
beaconBlock*: Option[BlockNumber]
|
|
pivotBlock*: Option[BlockNumber]
|
|
nAccounts*: (float,float) ## Mean and standard deviation
|
|
accountsFill*: (float,float,float) ## Mean, standard deviation, merged total
|
|
nAccountStats*: int ## #chunks
|
|
nSlotLists*: (float,float) ## Mean and standard deviation
|
|
nStorageQueue*: Option[int]
|
|
nQueues*: int
|
|
|
|
TickerStatsUpdater* =
|
|
proc: TickerStats {.gcsafe, raises: [Defect].}
|
|
|
|
TickerRef* = ref object
|
|
## Account fetching state that is shared among all peers.
|
|
nBuddies: int
|
|
recovery: bool
|
|
lastRecov: bool
|
|
lastStats: TickerStats
|
|
statsCb: TickerStatsUpdater
|
|
logTicker: TimerCallback
|
|
started: Moment
|
|
visited: Moment
|
|
|
|
const
|
|
tickerStartDelay = chronos.milliseconds(100)
|
|
tickerLogInterval = chronos.seconds(1)
|
|
tickerLogSuppressMax = chronos.seconds(100)
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Private functions: pretty printing
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# proc ppMs*(elapsed: times.Duration): string
|
|
# {.gcsafe, raises: [Defect, ValueError]} =
|
|
# result = $elapsed.inMilliseconds
|
|
# let ns = elapsed.inNanoseconds mod 1_000_000 # fraction of a milli second
|
|
# if ns != 0:
|
|
# # to rounded deca milli seconds
|
|
# let dm = (ns + 5_000i64) div 10_000i64
|
|
# result &= &".{dm:02}"
|
|
# result &= "ms"
|
|
#
|
|
# proc ppSecs*(elapsed: times.Duration): string
|
|
# {.gcsafe, raises: [Defect, ValueError]} =
|
|
# result = $elapsed.inSeconds
|
|
# let ns = elapsed.inNanoseconds mod 1_000_000_000 # fraction of a second
|
|
# if ns != 0:
|
|
# # round up
|
|
# let ds = (ns + 5_000_000i64) div 10_000_000i64
|
|
# result &= &".{ds:02}"
|
|
# result &= "s"
|
|
#
|
|
# proc ppMins*(elapsed: times.Duration): string
|
|
# {.gcsafe, raises: [Defect, ValueError]} =
|
|
# result = $elapsed.inMinutes
|
|
# let ns = elapsed.inNanoseconds mod 60_000_000_000 # fraction of a minute
|
|
# if ns != 0:
|
|
# # round up
|
|
# let dm = (ns + 500_000_000i64) div 1_000_000_000i64
|
|
# result &= &":{dm:02}"
|
|
# result &= "m"
|
|
#
|
|
# proc pp(d: times.Duration): string
|
|
# {.gcsafe, raises: [Defect, ValueError]} =
|
|
# if 40 < d.inSeconds:
|
|
# d.ppMins
|
|
# elif 200 < d.inMilliseconds:
|
|
# d.ppSecs
|
|
# else:
|
|
# d.ppMs
|
|
|
|
proc pc99(val: float): string =
|
|
if 0.99 <= val and val < 1.0: "99%"
|
|
elif 0.0 < val and val <= 0.01: "1%"
|
|
else: val.toPC(0)
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Private functions: ticking log messages
|
|
# ------------------------------------------------------------------------------
|
|
|
|
template noFmtError(info: static[string]; code: untyped) =
|
|
try:
|
|
code
|
|
except ValueError as e:
|
|
raiseAssert "Inconveivable (" & info & "): " & e.msg
|
|
|
|
proc setLogTicker(t: TickerRef; at: Moment) {.gcsafe.}
|
|
|
|
proc runLogTicker(t: TickerRef) {.gcsafe.} =
|
|
let
|
|
data = t.statsCb()
|
|
now = Moment.now()
|
|
|
|
if data != t.lastStats or
|
|
t.recovery != t.lastRecov or
|
|
tickerLogSuppressMax < (now - t.visited):
|
|
var
|
|
nAcc, nSto, bulk: string
|
|
pivot = "n/a"
|
|
beacon = "n/a"
|
|
nStoQue = "n/a"
|
|
let
|
|
recoveryDone = t.lastRecov
|
|
accCov = data.accountsFill[0].pc99 &
|
|
"(" & data.accountsFill[1].pc99 & ")" &
|
|
"/" & data.accountsFill[2].pc99 &
|
|
"~" & data.nAccountStats.uint.toSI
|
|
buddies = t.nBuddies
|
|
|
|
# With `int64`, there are more than 29*10^10 years range for seconds
|
|
up = (now - t.started).seconds.uint64.toSI
|
|
mem = getTotalMem().uint.toSI
|
|
|
|
t.lastStats = data
|
|
t.visited = now
|
|
t.lastRecov = t.recovery
|
|
|
|
noFmtError("runLogTicker"):
|
|
if data.pivotBlock.isSome:
|
|
pivot = &"#{data.pivotBlock.get}/{data.nQueues}"
|
|
if data.beaconBlock.isSome:
|
|
beacon = &"#{data.beaconBlock.get}"
|
|
nAcc = (&"{(data.nAccounts[0]+0.5).int64}" &
|
|
&"({(data.nAccounts[1]+0.5).int64})")
|
|
nSto = (&"{(data.nSlotLists[0]+0.5).int64}" &
|
|
&"({(data.nSlotLists[1]+0.5).int64})")
|
|
|
|
if data.nStorageQueue.isSome:
|
|
nStoQue = $data.nStorageQueue.unsafeGet
|
|
|
|
if t.recovery:
|
|
info "Snap sync statistics (recovery)",
|
|
up, buddies, beacon, pivot, nAcc, accCov, nSto, nStoQue, mem
|
|
elif recoveryDone:
|
|
info "Snap sync statistics (recovery done)",
|
|
up, buddies, beacon, pivot, nAcc, accCov, nSto, nStoQue, mem
|
|
else:
|
|
info "Snap sync statistics",
|
|
up, buddies, beacon, pivot, nAcc, accCov, nSto, nStoQue, mem
|
|
|
|
t.setLogTicker(Moment.fromNow(tickerLogInterval))
|
|
|
|
|
|
proc setLogTicker(t: TickerRef; at: Moment) =
|
|
if not t.logTicker.isNil:
|
|
t.logTicker = safeSetTimer(at, runLogTicker, t)
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Public constructor and start/stop functions
|
|
# ------------------------------------------------------------------------------
|
|
|
|
proc init*(T: type TickerRef; cb: TickerStatsUpdater): T =
|
|
## Constructor
|
|
T(statsCb: cb)
|
|
|
|
proc start*(t: TickerRef) =
|
|
## Re/start ticker unconditionally
|
|
#debug "Started ticker"
|
|
t.logTicker = safeSetTimer(Moment.fromNow(tickerStartDelay), runLogTicker, t)
|
|
if t.started == Moment.default:
|
|
t.started = Moment.now()
|
|
|
|
proc stop*(t: TickerRef) =
|
|
## Stop ticker unconditionally
|
|
t.logTicker = nil
|
|
#debug "Stopped ticker"
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Public functions
|
|
# ------------------------------------------------------------------------------
|
|
|
|
proc startBuddy*(t: TickerRef) =
|
|
## Increment buddies counter and start ticker unless running.
|
|
if t.nBuddies <= 0:
|
|
t.nBuddies = 1
|
|
if not t.recovery:
|
|
t.start()
|
|
else:
|
|
t.nBuddies.inc
|
|
|
|
proc startRecovery*(t: TickerRef) =
|
|
## Ditto for recovery mode
|
|
if not t.recovery:
|
|
t.recovery = true
|
|
if t.nBuddies <= 0:
|
|
t.start()
|
|
|
|
proc stopBuddy*(t: TickerRef) =
|
|
## Decrement buddies counter and stop ticker if there are no more registered
|
|
## buddies.
|
|
t.nBuddies.dec
|
|
if t.nBuddies <= 0 and not t.recovery:
|
|
t.stop()
|
|
|
|
proc stopRecovery*(t: TickerRef) =
|
|
## Ditto for recovery mode
|
|
if t.recovery:
|
|
t.recovery = false
|
|
t.stop()
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# End
|
|
# ------------------------------------------------------------------------------
|