@@ -505,7 +505,8 @@ private ScanResult linearScan0(@NotNull final Wire wire,
505505 fromKnownIndex = lastIndex ;
506506 }
507507
508- bytes .readPositionUnlimited (knownAddress );
508+ bytes .readLimitToCapacity ();
509+ bytes .readPosition (knownAddress );
509510
510511 for (long i = fromKnownIndex ; ; i ++) {
511512 try {
@@ -565,10 +566,26 @@ long linearScanByPosition(@NotNull final Wire wire,
565566 final long indexOfNext ,
566567 final long startAddress ,
567568 boolean inclusive ) throws EOFException {
569+ return linearScanByPosition (wire , toPosition , indexOfNext , startAddress , inclusive , "linearScan by position" );
570+ }
571+
572+ /**
573+ * Variant that lets the caller supply a description for the perf log so different scan
574+ * origins (fast path from {@code writePosition}, fall-through from indexed anchor, etc.)
575+ * are distinguishable in logs -- important because the whole point of the
576+ * {@code MAX_VALUE} fast path is to <em>avoid</em> the brute-force fall-through scan, and
577+ * we want to be able to verify that from log output rather than guess.
578+ */
579+ long linearScanByPosition (@ NotNull final Wire wire ,
580+ final long toPosition ,
581+ final long indexOfNext ,
582+ final long startAddress ,
583+ boolean inclusive ,
584+ String desc ) throws EOFException {
568585 long start = REPORT_LINEAR_SCAN ? System .nanoTime () : 0 ;
569586 long index = linearScanByPosition0 (wire , toPosition , indexOfNext , startAddress , inclusive );
570587 if (REPORT_LINEAR_SCAN ) {
571- printLinearScanTime (index , startAddress , start , "linearScan by position" );
588+ printLinearScanTime (index , startAddress , start , desc );
572589 }
573590 return index ;
574591 }
@@ -664,12 +681,13 @@ long linearScanByPosition0(@NotNull final Wire wire,
664681 * @return The starting index for the scan.
665682 */
666683 private long calculateInitialValue (long toPosition , long indexOfNext , long startAddress , Bytes <?> bytes , long lastAddress , long lastIndex ) {
684+ bytes .readLimit (bytes .capacity ());
667685 if (lastAddress > 0 && toPosition == lastAddress
668686 && lastIndex != Sequence .NOT_FOUND && lastIndex != Sequence .NOT_FOUND_RETRY ) {
669- bytes .readPositionUnlimited (toPosition );
687+ bytes .readPosition (toPosition );
670688 return lastIndex - 1 ;
671689 } else {
672- bytes .readPositionUnlimited (startAddress );
690+ bytes .readPosition (startAddress );
673691 return indexOfNext - 1 ;
674692 }
675693 }
@@ -709,6 +727,44 @@ public long nextEntryToBeIndexed() {
709727 return nextEntryToBeIndexed .getVolatileValue ();
710728 }
711729
730+ long sequenceForMaxPosition (@ NotNull ExcerptContext ec ,
731+ boolean inclusive ) throws StreamCorruptedException {
732+ int retry = 0 ;
733+ for (; retry < 128 ; retry ++) {
734+ long lastWritePos = writePosition .getVolatileValue ();
735+ long latestSeq = sequence .getSequence (lastWritePos );
736+ if (latestSeq >= 0 ) {
737+ if (retry > 0 ) {
738+ Jvm .perf ().on (getClass (),
739+ "sequenceForPosition(MAX_VALUE) tracker-read retried" + " " + retry + " times" );
740+ }
741+ try {
742+ return linearScanByPosition (ec .wireForIndex (), Long .MAX_VALUE , latestSeq , lastWritePos , inclusive ,
743+ "linearScan from writePosition (sequenceForPosition fast path)" );
744+ } catch (EOFException e ) {
745+ throw new UncheckedIOException (e );
746+ }
747+ }
748+ if (latestSeq == Sequence .NOT_FOUND )
749+ break ;
750+
751+ // NOT_FOUND_RETRY: writer raced. Spin-retry for the first couple of iterations
752+ // (the race window is sub-microsecond); after that yield to let the writer make
753+ // progress instead of starving them with our volatile reads.
754+ if (retry > 2 )
755+ Thread .yield ();
756+ }
757+ if (retry >= 128 ) {
758+ // Retry budget exhausted in a row of NOT_FOUND_RETRY -- surface this clearly:
759+ // it means we're about to do a brute-force indexed-anchor scan instead of the
760+ // efficient writePos-anchored fast-path scan. The log makes a regression visible.
761+ Jvm .perf ().on (getClass (),
762+ "sequenceForPosition(MAX_VALUE) tracker-read retry loop exhausted" + " after " + retry +
763+ " attempts; falling through to indexed lookup" );
764+ }
765+ return sequenceForPosition (ec , Long .MAX_VALUE , inclusive );
766+ }
767+
712768 /**
713769 * Returns the sequence number for a given position in the wire.
714770 * If an exact match is found for the position, the corresponding index is returned;
@@ -773,7 +829,7 @@ long sequenceForPosition(@NotNull ExcerptContext ec,
773829 }
774830 try {
775831 // Perform a linear scan if no exact match is found.
776- return linearScanByPosition (wire , position , indexOfNext , lastKnownAddress , inclusive );
832+ return linearScanByPosition (wire , position , indexOfNext , lastKnownAddress , inclusive , "linearScan from indexed anchor (sequenceForPosition fall-through)" );
777833 } catch (EOFException e ) {
778834 throw new UncheckedIOException (e );
779835 }
@@ -976,13 +1032,16 @@ public long lastSequenceNumber(@NotNull ExcerptContext ec)
9761032 break ;
9771033 try {
9781034 Wire wireForIndex = ec .wireForIndex ();
979- return wireForIndex == null ? sequence : linearScanByPosition (wireForIndex , Long .MAX_VALUE , sequence , address , true );
1035+ return wireForIndex == null ? sequence : linearScanByPosition (wireForIndex , Long .MAX_VALUE , sequence , address , true ,
1036+ "linearScan from writePosition (lastSequenceNumber tail-check)" );
9801037 } catch (EOFException e ) {
9811038 throw new UncheckedIOException (e );
9821039 }
9831040 }
9841041 }
9851042
1043+ // The write-position retry budget has already been spent above. Fall through
1044+ // directly to the indexed lookup instead of restarting the MAX_VALUE fast path.
9861045 return sequenceForPosition (ec , Long .MAX_VALUE , false );
9871046 }
9881047
0 commit comments