Take every down peer out of the table in one walk (closes #225)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
The prefix handler ran one PeerDown per peer, one after another, and stopped taking messages at 20,000 held-back messages. Sessions going down together queued up their walks, so a returning full-table peer could reach that limit. The table's PeersDown now takes several peers out of every prefix in one walk. One walk runs at a time. The next one takes every peer whose session went down meanwhile, so a message is held back for two walks at most. Each walk takes about 2.6 seconds for each full-table peer it takes out. The limit is now 150,000 messages, about 150 MiB: enough when four full-table peers go down together. The status page shows how many messages each handler holds back. Model: opus-5-5
This commit is contained in:
@@ -28,7 +28,7 @@ const (
|
||||
keptTimesDegree = 32
|
||||
|
||||
// walkBatch is how many prefixes a walk over the whole table visits in
|
||||
// one hold of the lock. Overview and PeerDown each walk the whole table.
|
||||
// one hold of the lock. Overview and PeersDown each walk the whole table.
|
||||
// Callers waiting for the lock get it between batches.
|
||||
walkBatch = 10000
|
||||
|
||||
@@ -205,20 +205,29 @@ func (t *Table) Withdraw(prefix netip.Prefix, peer string) {
|
||||
}
|
||||
}
|
||||
|
||||
// PeerDown applies a peer's session going down: the peer leaves every peer
|
||||
// set, and every route of the peer is dropped. When the peer carries any
|
||||
// prefix, PeerDown visits every prefix in the table, in batches of walkBatch.
|
||||
// After each batch, it lets the table's lock go for as long as it held the
|
||||
// lock for the batch. During a walk over many batches, reads and updates can
|
||||
// then take the lock about half of the time. An announcement by the peer
|
||||
// applied during the walk may be dropped too. So the caller applies the peer's
|
||||
// later updates only once PeerDown has returned.
|
||||
func (t *Table) PeerDown(peer string) {
|
||||
// PeersDown applies the sessions of peers going down: each of the peers leaves
|
||||
// every peer set, and every route of each is dropped. When any of them carries
|
||||
// a prefix, PeersDown visits every prefix in the table once, in batches of
|
||||
// walkBatch. After each batch, it lets the table's lock go for as long as it
|
||||
// held the lock for the batch. During a walk over many batches, reads and
|
||||
// updates can then take the lock about half of the time. An announcement by
|
||||
// one of the peers applied during the walk may be dropped too. So the caller
|
||||
// applies those peers' later updates only once PeersDown has returned.
|
||||
func (t *Table) PeersDown(peers []string) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
number, known := t.peerNumbers[peer]
|
||||
if !known || t.peerPrefixes[number] == 0 {
|
||||
// numbers holds the number of each of the peers that carries a prefix.
|
||||
numbers := make([]uint, 0, len(peers))
|
||||
|
||||
for _, peer := range peers {
|
||||
number, known := t.peerNumbers[peer]
|
||||
if known && t.peerPrefixes[number] > 0 {
|
||||
numbers = append(numbers, number)
|
||||
}
|
||||
}
|
||||
|
||||
if len(numbers) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -228,9 +237,15 @@ func (t *Table) PeerDown(peer string) {
|
||||
for _, f := range []*family{&t.ipv4, &t.ipv6} {
|
||||
for _, prefixes := range f.byLength {
|
||||
for prefix, origins := range prefixes {
|
||||
o := originWithPeer(origins, number)
|
||||
if o != nil {
|
||||
t.leave(prefix, o, number)
|
||||
for _, number := range numbers {
|
||||
o := originWithPeer(origins, number)
|
||||
if o != nil {
|
||||
t.leave(prefix, o, number)
|
||||
|
||||
// leave may have removed an origin of the
|
||||
// prefix, or the prefix itself.
|
||||
origins = prefixes[prefix]
|
||||
}
|
||||
}
|
||||
|
||||
// Go allows the map to change before the next step of
|
||||
|
||||
Reference in New Issue
Block a user