924060929 commented on code in PR #69298:
URL: https://github.com/apache/doris/pull/69298#discussion_r4321962000


##########
fe/fe-connector/fe-connector-iceberg/src/main/java/org/apache/doris/connector/iceberg/IcebergScanPlanProvider.java:
##########
@@ -632,11 +620,16 @@ public void close() {
      * streaming source's {@code hasNext}. Cache disabled -> the SDK path, 
byte-unchanged.
      */
     private CloseableIterable<FileScanTask> streamingFileScanTasks(TableScan 
scan, ConnectorSession session,
-            Table table, Optional<ConnectorExpression> filter, long sliceSize) 
{
+            Table table, Optional<ConnectorExpression> filter, long sliceSize,
+            ManifestReadControl readControl, IcebergTableHandle readHandle) {
         if (isManifestCacheEnabled()) {
             try {
                 return TableScanUtil.splitFiles(
-                        cacheBackedFileScanTasks(scan, session, table, filter, 
null), sliceSize);
+                        cacheBackedFileScanTasks(scan, session, table, filter, 
null, readControl, readHandle),
+                        sliceSize);
+            } catch (CancellationException e) {

Review Comment:
   Fixed in 17859e3ff14d197db98233fdf329b7f27c847d98. QueryTaskGroups.get now 
reports terminal CancellationException for the already-closed captured query, 
rather than a retryable rejection. Iceberg checks that lifetime before touching 
scan metadata and before every SDK fallback (streaming, materialized and both 
COUNT variants). Added a real engine-session test closing the scope before 
first scheduler acquisition, plus all four fallback paths under acquisition 
cancellation and cache-failure/closure interleavings; no SDK planFiles restart 
is allowed. Offline empty-table planning remains supported. Focused/package 
tests, full FE build and Checkstyle passed.



##########
fe/fe-connector/fe-connector-iceberg/src/main/java/org/apache/doris/connector/iceberg/IcebergManifestCache.java:
##########
@@ -274,46 +285,61 @@ private void sweepExpiredStats(long nowNanos) {
         statsByQuery.entrySet().removeIf(e -> nowNanos - 
e.getValue().lastTouchNanos >= statsTtlNanos);
     }
 
-    /**
-     * {@code specsById} is the scan's spec map (bound to the scan's schema), 
the same one its residuals and delete
-     * index use: the reader converts partition values to these types, so they 
must match what the scan evaluates.
-     */
-    private static IcebergManifestEntryKey keyOf(ManifestFile manifest, 
Map<Integer, PartitionSpec> specsById) {
-        PartitionSpec boundSpec = specsById.get(manifest.partitionSpecId());
-        Preconditions.checkArgument(boundSpec != null, "No bound partition 
spec %s for iceberg manifest %s",
-                manifest.partitionSpecId(), manifest.path());
-        return IcebergManifestEntryKey.of(manifest, boundSpec);
-    }
-
     private static ManifestCacheValue loadManifestCacheValue(ManifestFile 
manifest, Table table,
-            Map<Integer, PartitionSpec> specsById, ManifestContent content, 
boolean estimateWeight) {
+            ManifestContent content, boolean estimateWeight) {
         try {
             if (content == ManifestContent.DELETES) {
-                return 
ManifestCacheValue.forDeleteFiles(loadDeleteFiles(manifest, table, specsById), 
estimateWeight);
+                return 
ManifestCacheValue.forDeleteFiles(loadDeleteFiles(manifest, table), 
estimateWeight);
             }
-            return ManifestCacheValue.forDataFiles(loadDataFiles(manifest, 
table, specsById), estimateWeight);
+            return ManifestCacheValue.forDataFiles(loadDataFiles(manifest, 
table), estimateWeight);
         } catch (IOException e) {
             throw new RuntimeException("Failed to read iceberg manifest " + 
manifest.path(), e);
         }
     }
 
-    private static List<DataFile> loadDataFiles(ManifestFile manifest, Table 
table,
-            Map<Integer, PartitionSpec> specsById) throws IOException {
+    private static List<DataFile> loadDataFiles(ManifestFile manifest, Table 
table) throws IOException {
         List<DataFile> dataFiles = new ArrayList<>();
         // .copy() is mandatory: the ManifestReader iterator reuses the same 
object across iterations.
-        try (ManifestReader<DataFile> reader = ManifestFiles.read(manifest, 
table.io(), specsById)) {
+        try (ManifestReader<DataFile> reader = ManifestFiles.read(manifest, 
table.io(),
+                manifestReadSpecs(manifest, table))) {
             for (DataFile dataFile : reader) {
                 dataFiles.add(dataFile.copy());
             }
         }
         return dataFiles;
     }
 
-    private static List<DeleteFile> loadDeleteFiles(ManifestFile manifest, 
Table table,
-            Map<Integer, PartitionSpec> specsById) throws IOException {
+    /** Keep dropped partition sources readable for historical queries without 
reopening the manifest header. */
+    private static Map<Integer, PartitionSpec> manifestReadSpecs(ManifestFile 
manifest, Table table) {
+        PartitionSpec spec = table.specs().get(manifest.partitionSpecId());
+        boolean missingSource = spec.fields().stream()
+                .anyMatch(field -> spec.schema().findField(field.sourceId()) 
== null);
+        if (!missingSource) {
+            return Collections.singletonMap(spec.specId(), spec);
+        }
+        Map<Integer, Types.NestedField> sources = new LinkedHashMap<>();
+        for (PartitionField field : spec.fields()) {
+            Types.NestedField source = 
spec.schema().findField(field.sourceId());
+            if (source == null) {
+                source = table.schemas().entrySet().stream()
+                        .sorted(Map.Entry.<Integer, 
Schema>comparingByKey().reversed())
+                        .map(entry -> 
entry.getValue().findField(field.sourceId()))
+                        .filter(Objects::nonNull).findFirst()
+                        .orElseThrow(() -> new IllegalArgumentException(

Review Comment:
   Completed the remaining identity/truncate case in 
17859e3ff14d197db98233fdf329b7f27c847d98. If expiration has removed every 
source schema while a manifest is still live, the shared data/delete loader now 
lets the SDK recover the physical spec from that manifest header. This 
exceptional cleanup path uses the SDK header and entry streams; normal/promoted 
types and fixed-result transforms still open one stream, and warm cache reads 
open none. Added real cleanExpiredMetadata tests for identity/truncate data and 
position-delete manifests, physical-value preservation and delete-index 
matching. Shared cached values are not mutated. Package tests and FE build 
passed.



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