@@ -121,10 +121,9 @@ private boolean isLocalViewBehind(Context ctx) {
121121 String lastKnownInstantFromClient =
122122 ctx .queryParam (RemoteHoodieTableFileSystemView .LAST_INSTANT_TS , HoodieTimeline .INVALID_INSTANT_TS );
123123 String timelineHashFromClient = ctx .queryParam (RemoteHoodieTableFileSystemView .TIMELINE_HASH , "" );
124+ String numInstantsFromClient = ctx .queryParam (RemoteHoodieTableFileSystemView .NUM_INSTANTS , "-1" );
124125 HoodieTimeline localTimeline =
125126 viewManager .getFileSystemView (basePath ).getTimeline ().filterCompletedAndCompactionInstants ();
126- String localLastKnownInstant = localTimeline .lastInstant ().isPresent () ? localTimeline .lastInstant ().get ().getTimestamp ()
127- : HoodieTimeline .INVALID_INSTANT_TS ;
128127 if (LOG .isDebugEnabled ()) {
129128 LOG .debug ("Client [ LastTs=" + lastKnownInstantFromClient + ", TimelineHash=" + timelineHashFromClient
130129 + "], localTimeline=" + localTimeline .getInstants ().collect (Collectors .toList ()));
@@ -138,14 +137,31 @@ private boolean isLocalViewBehind(Context ctx) {
138137 String localTimelineHash = localTimeline .getTimelineHash ();
139138 // refresh if timeline hash mismatches and if local's last known instant < client's last known instant (if config is enabled)
140139 if (!localTimelineHash .equals (timelineHashFromClient )
141- && (!timelineServiceConfig .refreshTimelineBasedOnLatestCommit || HoodieTimeline .compareTimestamps (localLastKnownInstant , HoodieTimeline .LESSER_THAN , lastKnownInstantFromClient ))) {
140+ && (!timelineServiceConfig .refreshTimelineBasedOnLatestCommit
141+ || localTimelineBehind (localTimeline , lastKnownInstantFromClient , numInstantsFromClient ))) {
142142 return true ;
143143 }
144144
145145 // As a safety check, even if hash is same, ensure instant is present
146146 return !localTimeline .containsOrBeforeTimelineStarts (lastKnownInstantFromClient );
147147 }
148148
149+ private static boolean localTimelineBehind (HoodieTimeline localTimeline , String lastKnownInstantFromClient , String numInstantsFromClient ) {
150+ String localLastKnownInstant = localTimeline .lastInstant ().isPresent () ? localTimeline .lastInstant ().get ().getTimestamp ()
151+ : HoodieTimeline .INVALID_INSTANT_TS ;
152+ // Why comparing the num commits ?
153+ // Assumes there are 4 commits on the timeline:
154+ // timestamp(action): ts_0(commit), ts_1(commit), ts_2(clean), ts_3(commit)
155+ // when ts_1 is in INFLIGHT state, ts_2 clean action is already finished,
156+ // after ts_1 triggers #sync, the local timeline is refreshed as [ts_0, ts_2],
157+ // when ts_1 switches state from INFLIGHT to COMPLETED, no #sync triggers.
158+ // at ts_3, when the fs view snapshot is requested, the ts_3 client timeline should be [ts_0, ts_1, ts_2],
159+ // if we only compare the latest commit, the local timeline is NOT behind, but the fs view is not complete
160+ // because ts_1 is lost.
161+ return HoodieTimeline .compareTimestamps (localLastKnownInstant , HoodieTimeline .LESSER_THAN , lastKnownInstantFromClient )
162+ || localTimeline .countInstants () < Integer .parseInt (numInstantsFromClient );
163+ }
164+
149165 /**
150166 * Syncs data-set view if local view is behind.
151167 */
0 commit comments