@@ -435,8 +435,8 @@ func (s *Store[H]) flushLoop() {
435
435
s .pending .Append (headers ... )
436
436
// always inform heightSub about new headers seen.
437
437
s .heightSub .Notify (getHeights (headers ... )... )
438
- // advance contiguousHead if we don't have gaps.
439
- s .advanceContiguousHead (ctx , s . heightSub . Height () )
438
+ // advance head and tail if we don't have gaps.
439
+ s .advanceHeadAndTail (ctx )
440
440
// don't flush and continue if pending batch is not grown enough,
441
441
// and Store is not stopping(headers == nil)
442
442
if s .pending .Len () < s .Params .WriteBatchSize && headers != nil {
@@ -550,33 +550,53 @@ func (s *Store[H]) get(ctx context.Context, hash header.Hash) ([]byte, error) {
550
550
return data , nil
551
551
}
552
552
553
- // advanceContiguousHead updates contiguousHead and heightSub if a higher
554
- // contiguous header exists on a disk.
555
- func (s * Store [H ]) advanceContiguousHead (ctx context.Context , height uint64 ) {
556
- newHead := s .nextContiguousHead (ctx , height )
557
- if newHead .IsZero () || newHead .Height () <= height {
558
- return
553
+ // advanceHeadAndTail moves contiguous Head and Tail if a new or older exists respectively.
554
+ // It looks throw caches, pending headers and datastore to find the new Head and Tail.
555
+ // TODO(@Wondertan): Beware of the performance penalty of this approach, which always makes a at least one
556
+ // datastore lookup for both Tail and Head.
557
+ func (s * Store [H ]) advanceHeadAndTail (ctx context.Context ) {
558
+ newHead , changed := s .nextHead (ctx )
559
+ if changed {
560
+ s .contiguousHead .Store (& newHead )
561
+ s .heightSub .SetHeight (newHead .Height ())
562
+ log .Infow ("new head" , "height" , newHead .Height (), "hash" , newHead .Hash ())
563
+ s .metrics .newHead (newHead .Height ())
564
+ }
565
+
566
+ newTail , changed := s .nextTail (ctx )
567
+ if changed {
568
+ s .tailHeader .Store (& newTail )
569
+ log .Infow ("new tail" , "height" , newTail .Height (), "hash" , newTail .Hash ())
570
+ // TODO(@Wondertan): tail metric?
559
571
}
572
+ }
560
573
561
- s .contiguousHead .Store (& newHead )
562
- s .heightSub .SetHeight (newHead .Height ())
563
- log .Infow ("new head" , "height" , newHead .Height (), "hash" , newHead .Hash ())
564
- s .metrics .newHead (newHead .Height ())
574
+ // nextHead finds the new contiguous Head by iterating the current Head up until the newer height Head is found.
575
+ // Returns true if the newer one was found.
576
+ func (s * Store [H ]) nextHead (ctx context.Context ) (head H , changed bool ) {
577
+ head = * s .contiguousHead .Load ()
578
+ for {
579
+ h , err := s .getByHeight (ctx , head .Height ()+ 1 )
580
+ if err != nil {
581
+ return head , changed
582
+ }
583
+ head = h
584
+ changed = true
585
+ }
565
586
}
566
587
567
- // nextContiguousHead iterates up header by header until it finds a gap .
568
- // if height+1 header not found returns a default header .
569
- func (s * Store [H ]) nextContiguousHead (ctx context.Context , height uint64 ) H {
570
- var newHead H
588
+ // nextTail finds the new contiguous Tail by iterating the current Tail down until the older height Tail is found .
589
+ // Returns true if the older one was found .
590
+ func (s * Store [H ]) nextTail (ctx context.Context ) ( tail H , changed bool ) {
591
+ tail = * s . tailHeader . Load ()
571
592
for {
572
- height ++
573
- h , err := s .getByHeight (ctx , height )
593
+ h , err := s .getByHeight (ctx , tail .Height ()- 1 )
574
594
if err != nil {
575
- break
595
+ return tail , changed
576
596
}
577
- newHead = h
597
+ tail = h
598
+ changed = true
578
599
}
579
- return newHead
580
600
}
581
601
582
602
func (s * Store [H ]) loadHeadAndTail (ctx context.Context ) error {
0 commit comments