@@ -796,9 +796,10 @@ private String createNewSegmentMetadata(TableConfig tableConfig, IdealState idea
796796 // segment. For tables with pauseless mode enabled, this size is unavailable at this step because the
797797 // segment has not yet been built.
798798
799- createNewSegmentZKMetadata (tableConfig , streamConfigs .get (0 ), newLLCSegment , newSegmentCreationTimeMs ,
799+ createNewSegmentZKMetadataWithOffsetAutoReset (tableConfig , streamConfigs .get (0 ), newLLCSegment ,
800+ newSegmentCreationTimeMs ,
800801 committingSegmentDescriptor , committingSegmentZKMetadata , instancePartitions , partitionIds .size (),
801- numReplicas );
802+ numReplicas , false );
802803 newConsumingSegmentName = newLLCSegment .getSegmentName ();
803804 LOGGER .info ("Created new segment metadata for segment: {} with status: {}." , newConsumingSegmentName ,
804805 Status .IN_PROGRESS );
@@ -928,10 +929,9 @@ private boolean isPeerURL(String segmentLocation) {
928929 private void createNewSegmentZKMetadata (TableConfig tableConfig , StreamConfig streamConfig ,
929930 LLCSegmentName newLLCSegmentName , long creationTimeMs , CommittingSegmentDescriptor committingSegmentDescriptor ,
930931 @ Nullable SegmentZKMetadata committingSegmentZKMetadata , InstancePartitions instancePartitions , int numPartitions ,
931- int numReplicas ) {
932+ int numReplicas , String startOffset ) {
932933 String realtimeTableName = tableConfig .getTableName ();
933934 String segmentName = newLLCSegmentName .getSegmentName ();
934- String startOffset = committingSegmentDescriptor .getNextOffset ();
935935
936936 LOGGER .info (
937937 "Creating segment ZK metadata for new CONSUMING segment: {} with start offset: {} and creation time: {}" ,
@@ -962,6 +962,72 @@ private void createNewSegmentZKMetadata(TableConfig tableConfig, StreamConfig st
962962 persistSegmentZKMetadata (realtimeTableName , newSegmentZKMetadata , -1 );
963963 }
964964
965+ /**
966+ * Creates and persists segment ZK metadata for the new CONSUMING segment.
967+ */
968+ private void createNewSegmentZKMetadataWithOffsetAutoReset (TableConfig tableConfig , StreamConfig streamConfig ,
969+ LLCSegmentName newLLCSegmentName , long creationTimeMs , CommittingSegmentDescriptor committingSegmentDescriptor ,
970+ @ Nullable SegmentZKMetadata committingSegmentZKMetadata , InstancePartitions instancePartitions , int numPartitions ,
971+ int numReplicas , boolean skipAutoReset ) {
972+ String oldStartOffset = committingSegmentDescriptor .getNextOffset ();
973+ String startOffset = skipAutoReset ? oldStartOffset : computeStartOffset (
974+ oldStartOffset , streamConfig , newLLCSegmentName .getPartitionGroupId ());
975+ createNewSegmentZKMetadata (tableConfig , streamConfig , newLLCSegmentName , creationTimeMs ,
976+ committingSegmentDescriptor , committingSegmentZKMetadata , instancePartitions , numPartitions , numReplicas ,
977+ startOffset );
978+ }
979+
980+ private String computeStartOffset (
981+ String nextOffset , StreamConfig streamConfig , int partitionId ) {
982+ long timeThreshold = streamConfig .getOffsetAutoResetTimeSecThreshold ();
983+ int offsetThreshold = streamConfig .getOffsetAutoResetOffsetThreshold ();
984+ if (timeThreshold <= 0 && offsetThreshold <= 0 ) {
985+ return nextOffset ;
986+ }
987+ String clientId =
988+ PinotLLCRealtimeSegmentManager .class .getSimpleName () + "-" + streamConfig .getTableNameWithType () + "-"
989+ + streamConfig .getTopicName ();
990+ StreamConsumerFactory consumerFactory = StreamConsumerFactoryProvider .create (streamConfig );
991+ StreamPartitionMsgOffset offsetAtSLA ;
992+ StreamPartitionMsgOffset latestOffset ;
993+ try (StreamMetadataProvider metadataProvider = consumerFactory .createPartitionMetadataProvider (
994+ clientId , partitionId )) {
995+ // Fetching timestamp from an offset is an expensive operation which requires reading the data,
996+ // while fetching offset from timestamp is lightweight and only needs to read metadata.
997+ // Hence, instead of checking if latestOffset's time - nextOffset's time < SLA, we would check
998+ // (CurrentTime - SLA)'s offset > nextOffset.
999+ // TODO: it is relying on System.currentTimeMillis() which might be affected by time drift. If we are able to
1000+ // get nextOffset's time, we should instead check (nextOffset's time + SLA)'s offset < latestOffset
1001+ latestOffset = metadataProvider .fetchStreamPartitionOffset (OffsetCriteria .LARGEST_OFFSET_CRITERIA , 5000 );
1002+ LOGGER .info ("Latest offset of topic {} and partition {} is {}" ,
1003+ streamConfig .getTopicName (), partitionId , latestOffset );
1004+ offsetAtSLA = metadataProvider .getOffsetAtTimestamp (
1005+ partitionId , System .currentTimeMillis () - timeThreshold * 1000 );
1006+ LOGGER .info ("Offset at SLA of topic {} and partition {} is {}" ,
1007+ streamConfig .getTopicName (), partitionId , offsetAtSLA );
1008+ } catch (Exception e ) {
1009+ LOGGER .warn ("Not able to fetch the offset metadata, skip auto resetting offsets" , e );
1010+ return nextOffset ;
1011+ }
1012+ try {
1013+ if (timeThreshold > 0 && offsetAtSLA != null
1014+ && Long .valueOf (offsetAtSLA .toString ()) > Long .valueOf (nextOffset )) {
1015+ LOGGER .info ("Auto reset offset from {} to {} on partition {} because time threshold reached" ,
1016+ nextOffset , latestOffset , partitionId );
1017+ return latestOffset .toString ();
1018+ }
1019+ if (offsetThreshold > 0
1020+ && Long .valueOf (latestOffset .toString ()) - Long .valueOf (nextOffset ) > offsetThreshold ) {
1021+ LOGGER .info ("Auto reset offset from {} to {} on partition {} because number of offsets threshold reached" ,
1022+ nextOffset , latestOffset , partitionId );
1023+ return latestOffset .toString ();
1024+ }
1025+ } catch (Exception e ) {
1026+ LOGGER .warn ("Not able to convert the offset to LONG type, skip auto resetting offsets" , e );
1027+ }
1028+ return nextOffset ;
1029+ }
1030+
9651031 @ Nullable
9661032 private SegmentPartitionMetadata getPartitionMetadataFromTableConfig (TableConfig tableConfig , int partitionId ,
9671033 int numPartitionGroups ) {
@@ -1585,8 +1651,10 @@ IdealState ensureAllPartitionsConsuming(TableConfig tableConfig, List<StreamConf
15851651 CommittingSegmentDescriptor committingSegmentDescriptor =
15861652 new CommittingSegmentDescriptor (latestSegmentName ,
15871653 (offsetFactory .create (latestSegmentZKMetadata .getEndOffset ()).toString ()), 0 );
1588- createNewSegmentZKMetadata (tableConfig , streamConfigs .get (0 ), newLLCSegmentName , currentTimeMs ,
1589- committingSegmentDescriptor , latestSegmentZKMetadata , instancePartitions , numPartitions , numReplicas );
1654+ createNewSegmentZKMetadataWithOffsetAutoReset (tableConfig , streamConfigs .get (0 ), newLLCSegmentName ,
1655+ currentTimeMs ,
1656+ committingSegmentDescriptor , latestSegmentZKMetadata , instancePartitions , numPartitions , numReplicas ,
1657+ false );
15901658 updateInstanceStatesForNewConsumingSegment (instanceStatesMap , latestSegmentName , newSegmentName ,
15911659 segmentAssignment , instancePartitionsMap );
15921660 } else { // partition group reached end of life
@@ -1711,8 +1779,8 @@ private void createNewConsumingSegment(TableConfig tableConfig, StreamConfig str
17111779 LLCSegmentName newLLCSegmentName = getNextLLCSegmentName (latestLLCSegmentName , currentTimeMs );
17121780 CommittingSegmentDescriptor committingSegmentDescriptor =
17131781 new CommittingSegmentDescriptor (latestSegmentZKMetadata .getSegmentName (), startOffset .toString (), 0 );
1714- createNewSegmentZKMetadata (tableConfig , streamConfig , newLLCSegmentName , currentTimeMs , committingSegmentDescriptor ,
1715- latestSegmentZKMetadata , instancePartitions , numPartitions , numReplicas );
1782+ createNewSegmentZKMetadataWithOffsetAutoReset (tableConfig , streamConfig , newLLCSegmentName , currentTimeMs ,
1783+ committingSegmentDescriptor , latestSegmentZKMetadata , instancePartitions , numPartitions , numReplicas , false );
17161784 String newSegmentName = newLLCSegmentName .getSegmentName ();
17171785 updateInstanceStatesForNewConsumingSegment (instanceStatesMap , null , newSegmentName , segmentAssignment ,
17181786 instancePartitionsMap );
@@ -1790,8 +1858,8 @@ private String setupNewPartitionGroup(TableConfig tableConfig, StreamConfig stre
17901858 String newSegmentName = newLLCSegmentName .getSegmentName ();
17911859
17921860 CommittingSegmentDescriptor committingSegmentDescriptor = new CommittingSegmentDescriptor (null , startOffset , 0 );
1793- createNewSegmentZKMetadata (tableConfig , streamConfig , newLLCSegmentName , creationTimeMs ,
1794- committingSegmentDescriptor , null , instancePartitions , numPartitions , numReplicas );
1861+ createNewSegmentZKMetadataWithOffsetAutoReset (tableConfig , streamConfig , newLLCSegmentName , creationTimeMs ,
1862+ committingSegmentDescriptor , null , instancePartitions , numPartitions , numReplicas , true );
17951863
17961864 return newSegmentName ;
17971865 }
0 commit comments