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


##########
pinot-controller/src/main/java/org/apache/pinot/controller/api/upload/SegmentValidationUtils.java:
##########
@@ -130,6 +135,51 @@ private static String 
getPartitionColumn(InstanceAssignmentConfig instanceAssign
     return replicaGroupPartitionConfig != null ? 
replicaGroupPartitionConfig.getPartitionColumn() : null;
   }
 
+  /// When `controller.segment.upload.rejectOutOfRetention.enabled` is true on 
the controller, rejects the upload if
+  /// the segment's data end time (from segment file metadata) is past the 
table's `retentionTimeValue` /
+  /// `retentionTimeUnit` window, using the same boundary as the controller 
retention manager. **Index creation time is
+  /// not** used as a fallback here: it can reflect upstream segment 
timestamps (for example upsert compaction) and is a
+  /// poor proxy for the data window at upload time.
+  ///
+  /// Rejection uses HTTP 403 so callers that enable the controller flag can 
treat the response as a hard failure.
+  /// Clients that do not enable the flag see no behavior change.
+  ///
+  /// Controller wiring: {@link 
org.apache.pinot.controller.api.resources.PinotSegmentUploadDownloadRestletResource}
+  /// invokes this for single-segment upload only. METADATA batch upload 
(`POST /segments/batchUpload`) and
+  /// reingested-segment upload do not call it.
+  ///
+  /// For tables where the retention manager does not apply time-based 
retention to completed segments (offline tables
+  /// whose batch ingestion type is not `APPEND`), this method returns without 
evaluating retention.
+  public static void rejectUploadIfOutOfRetention(SegmentMetadata 
segmentMetadata, TableConfig tableConfig,
+      long currentTimeMs, boolean controllerRejectOutOfRetentionEnabled,
+      @Nullable ControllerMetrics controllerMetrics) {
+    if (!controllerRejectOutOfRetentionEnabled) {
+      return;
+    }
+    SegmentsValidationAndRetentionConfig validationConfig = 
tableConfig.getValidationConfig();
+    if (validationConfig == null) {
+      return;
+    }
+    if (!RetentionUtils.shouldManageTimeBasedDataRetention(tableConfig)) {
+      return;
+    }
+    OptionalLong retentionMsOpt = 
RetentionUtils.parseTableDataRetentionMillis(validationConfig);
+    if (retentionMsOpt.isEmpty()) {
+      return;
+    }
+    long retentionMs = retentionMsOpt.getAsLong();
+    if (RetentionUtils.isPurgeable(tableConfig.getTableName(), 
segmentMetadata, retentionMs, currentTimeMs, false)) {

Review Comment:
   This also applies to minion-produced uploads. 
`BaseMultipleSegmentsConversionExecutor` pushes to `POST /segments` (unless 
`batchSegmentUpload` is on, which goes to `/segments/batchUpload` and skips 
this check). For RealtimeToOffline, `postProcess` only advances the watermark 
after a successful upload, so a 403 here makes the generator retry the same 
window on every run. That continues until the source realtime segments age out, 
and indefinitely with the hybrid retention strategy, because the time boundary 
can't advance.
   
   Could we either exempt task-originated uploads or at least document this 
interaction on the config key so operators know before enabling it? Today the 
only signal is the `SEGMENT_ZK_METADATA_CUSTOM_MAP_MODIFIER` header that minion 
sends, so a cleaner exemption would need a dedicated marker.



##########
pinot-common/src/main/java/org/apache/pinot/common/utils/RetentionUtils.java:
##########
@@ -69,4 +109,43 @@ public static boolean isPurgeable(String tableNameWithType, 
SegmentZKMetadata se
     }
     return false;
   }
+
+  /// Parses {@link 
SegmentsValidationAndRetentionConfig#getRetentionTimeUnit()} and
+  /// {@link SegmentsValidationAndRetentionConfig#getRetentionTimeValue()} 
into table data retention duration in
+  /// milliseconds (same interpretation as controller time-based retention).
+  ///
+  /// @return millis, or empty if unset or not parseable
+  public static OptionalLong parseTableDataRetentionMillis(

Review Comment:
   This duplicates the unit/value parsing in 
`TableConfigRetentionUtils.buildRetentionStrategy`, and the two already drifted 
once (the `Locale.ROOT` fix). Since this now lives in `pinot-common`, could 
`buildRetentionStrategy` call `parseTableDataRetentionMillis`? Then the upload 
check and the purge path can't disagree about what the table's retention is.



-- 
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