jiafu1115 commented on code in PR #20913:
URL: https://github.com/apache/kafka/pull/20913#discussion_r3295457433


##########
storage/src/main/java/org/apache/kafka/server/log/remote/storage/RemoteLogManager.java:
##########
@@ -925,18 +927,73 @@ List<EnrichedLogSegment> candidateLogSegments(UnifiedLog 
log, Long fromOffset, L
             List<EnrichedLogSegment> candidateLogSegments = new ArrayList<>();
             List<LogSegment> segments = log.logSegments(fromOffset, 
Long.MAX_VALUE);
             if (!segments.isEmpty()) {
+                long currentTimeMs = time.milliseconds();
+                long totalLogSize = UnifiedLog.sizeInBytes(segments);
+                long cumulativeSize = 0;
                 for (int idx = 1; idx < segments.size(); idx++) {
                     LogSegment previousSeg = segments.get(idx - 1);
                     LogSegment currentSeg = segments.get(idx);
                     if (currentSeg.baseOffset() <= lastStableOffset) {
-                        candidateLogSegments.add(new 
EnrichedLogSegment(previousSeg, currentSeg.baseOffset()));
+                        cumulativeSize += previousSeg.size();
+                        if (isEligibleForUpload(log.config(), previousSeg, 
currentTimeMs, totalLogSize, cumulativeSize)) {
+                            candidateLogSegments.add(new 
EnrichedLogSegment(previousSeg, currentSeg.baseOffset()));
+                        } else {
+                            break;
+                        }
                     }
                 }
                 // Discard the last active segment
             }
             return candidateLogSegments;
         }
 
+        private boolean isEligibleForUpload(LogConfig logConfig, LogSegment 
previousSeg, long currentTimeMs, long totalLogSize, long cumulativeSize) {
+            long copyLagMs = logConfig.remoteCopyLagMs();
+            long copyLagBytes = logConfig.remoteCopyLagBytes();
+            if (logger.isTraceEnabled()) {
+                logger.trace("delayCopy check for segment {}: copyLagMs={}, 
copyLagBytes={}, currentTimeMs={}, totalLogSize={}, cumulativeSize={}, 
sizeLagBytes={}",
+                        previousSeg, copyLagMs, copyLagBytes, currentTimeMs, 
totalLogSize, cumulativeSize, totalLogSize - cumulativeSize);
+            }
+
+            if (copyLagMs == 0 || copyLagBytes == 0) {
+                return true;
+            }
+
+            boolean limitedCopyLagMsCheck =  copyLagMs > 0;
+            boolean limitedCopyLagSizeCheck = copyLagBytes > 0;
+
+            if (limitedCopyLagMsCheck && eligibleUploadByTime(previousSeg, 
currentTimeMs, copyLagMs)) {
+                return true;
+            }
+
+            return limitedCopyLagSizeCheck && 
eligibleUploadBySize(previousSeg, totalLogSize, cumulativeSize, copyLagBytes);
+        }
+
+        private boolean eligibleUploadByTime(LogSegment segment, long 
currentTimeMs, long copyLagMs) {
+            try {
+                long segmentAgeMs = currentTimeMs - segment.largestTimestamp();
+                boolean eligibleUpload = segmentAgeMs < 0 || segmentAgeMs >= 
copyLagMs;

Review Comment:
   @kamalcph @chia7712 thanks for raising so interesting topic (future 
timestamp). Let me try to undersand it.
   For local segment of this case. It and the segements after it won't be 
deleted due to follow logic:
   <img width="1136" height="186" alt="image" 
src="https://github.com/user-attachments/assets/82106771-16b5-4eae-9dfe-0df5a9cdc570";
 />
   
   So, local segments will not be deleted regardless of whether they have been 
uploaded to remote storage, so there is no performance concern here. In the 
worst case, we simply achieve less cost reduction for this scenario.
   
   In practice, if the goal is to reduce max possible costs while preserving 
the existing behavior, delaying the upload would also work. However, I am 
concerned that the local deletion logic may evolve in the future. For that 
reason, I though allowing the upload is more reasonable for this special case. 
And we may have some extra solution for this case:
   
   (1) We can document this limitation clearly and define the basic principle 
of this KIP as: we aim to reduce storage costs, although this special case may 
not fully benefit from the optimization.
   (2) How about this solution?  if we found it contain future record. we use 
LogSegment#lastModified to compare the time?
   (3) Delay the upload to reach the max cost saving no consider the local 
delete logic for this case changed in future.
   
   And. for the proposed solution: " remote.copy.before.deletion.ms". it seems 
a little more complex to be understand than the existed solution. I am worring 
if worth to revise the whole logic for this special case. But I can take more 
time to consider it and give feedback before Kafka 4.4.0's code freeze.  
   
   BTW, For my proposed three solutions. WDYT?
   
   Thanks @chia7712 @kamalcph 



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

Reply via email to