xiangfu0 commented on code in PR #19817:
URL: https://github.com/apache/pinot/pull/19817#discussion_r4278298688


##########
pinot-core/src/main/java/org/apache/pinot/core/data/manager/realtime/RealtimeSegmentDataManager.java:
##########
@@ -1765,26 +1768,40 @@ public void goOnlineFromConsuming(SegmentZKMetadata 
segmentZKMetadata)
 
   protected void downloadSegmentAndReplace(SegmentZKMetadata segmentZKMetadata)
       throws Exception {
-    boolean allowedDuringDownload = 
_parallelSegmentConsumptionPolicy.isAllowedDuringDownload();
     // A local build that failed or mismatched the committed CRC has already 
released the semaphore when the policy
     // allows consumption during build, so the next consuming segment can be 
running during this download as well
     boolean releasedDuringBuild =
         _parallelSegmentConsumptionPolicy.isAllowedDuringBuild() && 
!_consumerSemaphoreAcquired.get();
-    if ((allowedDuringDownload || releasedDuringBuild) && 
_partitionUpsertMetadataManager != null
-        && 
_partitionUpsertMetadataManager.shouldRevertMetadataOnInconsistency(_realtimeSegment))
 {
-      // Table config validation rejects this in PROTECTED mode. It still 
happens when the mode is switched after the
-      // table is created, when the server-level default allows partial upsert 
consumption during commit, or when a
-      // pauseless table falls back to a download after its local build fails.
-      _segmentLogger.error("Next consuming segment can run during download 
while this replace reverts upsert "
-          + "metadata. Its snapshot can miss the rows the revert restores. Use 
DISALLOW_ALWAYS");
-      _serverMetrics.addMeteredTableValue(_clientId, 
ServerMeter.UPSERT_REVERT_WITH_CONSUMPTION_DURING_DOWNLOAD, 1L);
+    // Hold the semaphore when the replace rewrites the key map, so the next 
consuming segment does not start against
+    // a partially reverted one, and does not replay rows against keys this 
replace is about to remove.
+    boolean holdsConsumerSemaphore = holdsConsumerSemaphoreForMetadata();
+    boolean allowedDuringDownload =
+        _parallelSegmentConsumptionPolicy.isAllowedDuringDownload() && 
!holdsConsumerSemaphore;
+    if (releasedDuringBuild && holdsConsumerSemaphore) {

Review Comment:
   The error log and the `UPSERT_REVERT_WITH_CONSUMPTION_DURING_DOWNLOAD` meter 
now only fire when `releasedDuringBuild` is true. For 
`ALLOW_DURING_DOWNLOAD_ONLY`, `isAllowedDuringBuild()` is false, so 
`releasedDuringBuild` is structurally always false and this branch can never 
run — yet that policy with an upsert revert is exactly what the old code 
flagged. The new code safely holds the semaphore there instead of releasing, 
which is good, but the operator loses the "your PROTECTED config is unsafe, 
switch to DISALLOW_ALWAYS" signal entirely. Consider still logging/metering 
when `holdsConsumerSemaphore && isAllowedDuringDownload()` so a 
silently-overridden policy is still visible.



##########
pinot-core/src/main/java/org/apache/pinot/core/data/manager/realtime/RealtimeSegmentDataManager.java:
##########
@@ -1207,7 +1207,10 @@ SegmentBuildDescriptor getSegmentBuildDescriptor() {
   @Nullable
   protected SegmentBuildDescriptor buildSegmentInternal(boolean forCommit)
       throws SegmentBuildFailureException {
-    if (_parallelSegmentConsumptionPolicy.isAllowedDuringBuild()) {
+    // Not dedup: it removes a segment's keys when the mutable segment is 
offloaded, which is after the replace, so

Review Comment:
   Same as the `holdsConsumerSemaphoreForMetadata` doc below: "nothing it owns 
is rewritten while the build runs" isn't accurate — the dedup key re-point 
happens at the replace that the build leads to, on both the build and download 
paths. Releasing during build is still fine (the exposed window at build-end is 
tiny), but it'd help to phrase the justification as a window-size trade-off 
rather than "nothing is rewritten."



##########
pinot-core/src/main/java/org/apache/pinot/core/data/manager/realtime/RealtimeSegmentDataManager.java:
##########
@@ -1765,26 +1768,40 @@ public void goOnlineFromConsuming(SegmentZKMetadata 
segmentZKMetadata)
 
   protected void downloadSegmentAndReplace(SegmentZKMetadata segmentZKMetadata)
       throws Exception {
-    boolean allowedDuringDownload = 
_parallelSegmentConsumptionPolicy.isAllowedDuringDownload();
     // A local build that failed or mismatched the committed CRC has already 
released the semaphore when the policy
     // allows consumption during build, so the next consuming segment can be 
running during this download as well
     boolean releasedDuringBuild =
         _parallelSegmentConsumptionPolicy.isAllowedDuringBuild() && 
!_consumerSemaphoreAcquired.get();
-    if ((allowedDuringDownload || releasedDuringBuild) && 
_partitionUpsertMetadataManager != null
-        && 
_partitionUpsertMetadataManager.shouldRevertMetadataOnInconsistency(_realtimeSegment))
 {
-      // Table config validation rejects this in PROTECTED mode. It still 
happens when the mode is switched after the
-      // table is created, when the server-level default allows partial upsert 
consumption during commit, or when a
-      // pauseless table falls back to a download after its local build fails.
-      _segmentLogger.error("Next consuming segment can run during download 
while this replace reverts upsert "
-          + "metadata. Its snapshot can miss the rows the revert restores. Use 
DISALLOW_ALWAYS");
-      _serverMetrics.addMeteredTableValue(_clientId, 
ServerMeter.UPSERT_REVERT_WITH_CONSUMPTION_DURING_DOWNLOAD, 1L);
+    // Hold the semaphore when the replace rewrites the key map, so the next 
consuming segment does not start against
+    // a partially reverted one, and does not replay rows against keys this 
replace is about to remove.
+    boolean holdsConsumerSemaphore = holdsConsumerSemaphoreForMetadata();
+    boolean allowedDuringDownload =
+        _parallelSegmentConsumptionPolicy.isAllowedDuringDownload() && 
!holdsConsumerSemaphore;
+    if (releasedDuringBuild && holdsConsumerSemaphore) {
+      // The build holds the semaphore for these tables, so reaching this 
means it was released on another path.
+      _segmentLogger.error("Next consuming segment can run during download 
while this replace rewrites upsert or "
+          + "dedup metadata. Its rows can be dropped against keys this replace 
removes. Use DISALLOW_ALWAYS");
+      if (revertsUpsertMetadata()) {
+        _serverMetrics.addMeteredTableValue(_clientId, 
ServerMeter.UPSERT_REVERT_WITH_CONSUMPTION_DURING_DOWNLOAD, 1L);
+      }
     }
     if (allowedDuringDownload) {
       closeStreamConsumerAndReleaseSemaphore();
     }
     
_realtimeTableDataManager.downloadAndReplaceConsumingSegment(segmentZKMetadata);
   }
 
+  /// The replace rewrites the key map, so the next consuming segment must not 
run against it during the download:
+  /// upsert reverts those keys, dedup removes them when the mutable segment 
is offloaded.

Review Comment:
   This premise isn't quite right: dedup rewrites the key map at the *replace*, 
not at offload. `handleDedup` -> `replaceSegment` re-points every key from the 
mutable to the new immutable segment inside both `replaceConsumingSegment` 
(build path) and `downloadAndReplaceConsumingSegment`; offload only removes 
whatever keys remain. The held-during-download behavior is still safe, but the 
stated reason is misleading — the real distinction is the length of the window 
T2 is exposed, not where the rewrite happens.



##########
pinot-core/src/main/java/org/apache/pinot/core/data/manager/realtime/RealtimeSegmentDataManager.java:
##########
@@ -1207,7 +1207,10 @@ SegmentBuildDescriptor getSegmentBuildDescriptor() {
   @Nullable
   protected SegmentBuildDescriptor buildSegmentInternal(boolean forCommit)
       throws SegmentBuildFailureException {
-    if (_parallelSegmentConsumptionPolicy.isAllowedDuringBuild()) {
+    // Not dedup: it removes a segment's keys when the mutable segment is 
offloaded, which is after the replace, so
+    // nothing it owns is rewritten while the build runs. An upsert revert is 
rewritten at the replace the build
+    // leads to, and the next consuming segment must not already be running 
against the key map by then.
+    if (_parallelSegmentConsumptionPolicy.isAllowedDuringBuild() && 
!revertsUpsertMetadata()) {

Review Comment:
   This new build-side guard is the core behavior change, but no test covers it 
— the test `FakeRealtimeSegmentDataManager.buildSegmentInternal` override 
replaces this whole method, so the `&& !revertsUpsertMetadata()` branch is 
never exercised. Combined with the still-open P2 (no 
`ALLOW_DURING_DOWNLOAD_ONLY`/`ALLOW_ALWAYS` + revert download case), the 
semaphore-hold behavior could regress silently. Worth a focused test asserting 
the permit stays held through build/download when reverting and is released 
after offload.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to