diff --git a/snow/engine/avalanche/bootstrapper.go b/snow/engine/avalanche/bootstrapper.go index d5e5e7f..3ed58c7 100644 --- a/snow/engine/avalanche/bootstrapper.go +++ b/snow/engine/avalanche/bootstrapper.go @@ -17,7 +17,12 @@ import ( ) const ( - cacheSize = 100000 + // We cache processed vertices where height = c * stripeDistance for c = {1,2,3...} + // This forms a "stripe" of cached DAG vertices at height stripeDistance, 2*stripeDistance, etc. + // This helps to limit the number of repeated DAG traversals performed + stripeDistance = 2000 + stripeWidth = 5 + cacheSize = 100000 ) // BootstrapConfig ... @@ -126,11 +131,14 @@ func (b *bootstrapper) fetch(vtxID ids.ID) error { } // Process vertices -func (b *bootstrapper) process(vtx avalanche.Vertex) error { +func (b *bootstrapper) process(vtxs ...avalanche.Vertex) error { toProcess := newMaxVertexHeap() - if _, ok := b.processedCache.Get(vtx.ID()); !ok { // only process if we haven't already - toProcess.Push(vtx) + for _, vtx := range vtxs { + if _, ok := b.processedCache.Get(vtx.ID()); !ok { // only process if we haven't already + toProcess.Push(vtx) + } } + for toProcess.Len() > 0 { vtx := toProcess.Pop() switch vtx.Status() { @@ -172,7 +180,9 @@ func (b *bootstrapper) process(vtx avalanche.Vertex) error { toProcess.Push(parent) } } - b.processedCache.Put(vtx.ID(), nil) + if vtx.Height()%stripeDistance < stripeWidth { + b.processedCache.Put(vtx.ID(), nil) + } } } @@ -217,14 +227,19 @@ func (b *bootstrapper) MultiPut(vdr ids.ShortID, requestID uint32, vtxs [][]byte return b.fetch(neededVtxID) } - for _, vtxBytes := range vtxs { // Parse/persist all the vertices - if _, err := b.State.ParseVertex(vtxBytes); err != nil { // Persists the vtx + processVertices := make([]avalanche.Vertex, 1, len(vtxs)) + processVertices[0] = neededVtx + + for _, vtxBytes := range vtxs[1:] { // Parse/persist all the vertices + if vtx, err := b.State.ParseVertex(vtxBytes); err != nil { // Persists the vtx b.BootstrapConfig.Context.Log.Debug("Failed to parse vertex: %w", err) b.BootstrapConfig.Context.Log.Verbo("vertex: %s", formatting.DumpBytes{Bytes: vtxBytes}) + } else { + processVertices = append(processVertices, vtx) } } - return b.process(neededVtx) + return b.process(processVertices...) } // GetAncestorsFailed is called when a GetAncestors message we sent fails @@ -245,15 +260,17 @@ func (b *bootstrapper) ForceAccepted(acceptedContainerIDs ids.Set) error { err) } + storedVtxs := make([]avalanche.Vertex, 0, acceptedContainerIDs.Len()) for _, vtxID := range acceptedContainerIDs.List() { if vtx, err := b.State.GetVertex(vtxID); err == nil { - if err := b.process(vtx); err != nil { - return err - } + storedVtxs = append(storedVtxs, vtx) } else if err := b.fetch(vtxID); err != nil { return err } } + if err := b.process(storedVtxs...); err != nil { + return err + } b.processedStartingAcceptedFrontier = true if numPending := b.outstandingRequests.Len(); numPending == 0 {