You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iceberg.apache.org by ze...@apache.org on 2026/08/27 21:57:26 UTC

(iceberg-go) branch main updated: perf(rest): reuse referenced deletion vectors during scan task decode (#1922)

This is an automated email from the ASF dual-hosted git repository.

zeroshade pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-go.git


The following commit(s) were added to refs/heads/main by this push:
     new b1f7f7c97 perf(rest): reuse referenced deletion vectors during scan task decode (#1922)
b1f7f7c97 is described below

commit b1f7f7c972a0ae61b0a89f5543e9609c50a80730
Author: Minh Vu <vu...@gmail.com>
AuthorDate: Thu Aug 27 23:57:19 2026 +0200

    perf(rest): reuse referenced deletion vectors during scan task decode (#1922)
---
 catalog/rest/scan_task_decoder.go            |  8 ++-
 catalog/rest/scan_task_decoder_bench_test.go | 86 ++++++++++++++++++++++++++++
 2 files changed, 91 insertions(+), 3 deletions(-)

diff --git a/catalog/rest/scan_task_decoder.go b/catalog/rest/scan_task_decoder.go
index 56141500e..31539af29 100644
--- a/catalog/rest/scan_task_decoder.go
+++ b/catalog/rest/scan_task_decoder.go
@@ -207,9 +207,11 @@ func DecodeScanTasks(
 				dvOwners[ref] = dataFile.FilePath()
 				// Derive referenced-data-file from the FileScanTask association
 				// when the server omits Java's optional extension field.
-				deleteFile, err = decodeRESTDeleteFile(wireDelete, metadata, dataFile.FilePath())
-				if err != nil {
-					return nil, fmt.Errorf("%w: decoding scan tasks: delete-files[%d]: %w", ErrRESTError, ref, err)
+				if wireDelete.ReferencedDataFile == nil {
+					deleteFile, err = decodeRESTDeleteFile(wireDelete, metadata, dataFile.FilePath())
+					if err != nil {
+						return nil, fmt.Errorf("%w: decoding scan tasks: delete-files[%d]: %w", ErrRESTError, ref, err)
+					}
 				}
 			}
 
diff --git a/catalog/rest/scan_task_decoder_bench_test.go b/catalog/rest/scan_task_decoder_bench_test.go
new file mode 100644
index 000000000..5e2f7c974
--- /dev/null
+++ b/catalog/rest/scan_task_decoder_bench_test.go
@@ -0,0 +1,86 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package rest
+
+import (
+	"fmt"
+	"testing"
+)
+
+var decodeScanTasksBenchmarkSink int
+
+func BenchmarkDecodeScanTasksDeletionVectors(b *testing.B) {
+	metadata := newScanTaskDecoderMetadata()
+
+	for _, tc := range []struct {
+		name          string
+		taskCount     int
+		explicitOwner bool
+	}{
+		{name: "explicit-owner/tasks=100", taskCount: 100, explicitOwner: true},
+		{name: "derived-owner/tasks=100", taskCount: 100},
+		{name: "explicit-owner/tasks=1000", taskCount: 1000, explicitOwner: true},
+		{name: "derived-owner/tasks=1000", taskCount: 1000},
+		{name: "explicit-owner/tasks=10000", taskCount: 10000, explicitOwner: true},
+		{name: "derived-owner/tasks=10000", taskCount: 10000},
+	} {
+		wire := deletionVectorScanTasksWire(tc.taskCount, tc.explicitOwner)
+		b.Run(tc.name, func(b *testing.B) {
+			b.ReportAllocs()
+			b.ResetTimer()
+			for b.Loop() {
+				tasks, err := DecodeScanTasks(wire, metadata, metadata.schema, nil)
+				if err != nil {
+					b.Fatal(err)
+				}
+				decodeScanTasksBenchmarkSink = len(tasks)
+			}
+		})
+	}
+}
+
+func deletionVectorScanTasksWire(taskCount int, explicitOwner bool) ScanTasks {
+	base := validScanTasksWire()
+	dataFile := *base.FileScanTasks[0].DataFile
+	deleteFile := base.DeleteFiles[0]
+	deleteFile.FileFormat = "puffin"
+	deleteFile.ContentOffset = int64Ptr(10)
+	deleteFile.ContentSizeInBytes = int64Ptr(20)
+
+	wire := ScanTasks{
+		FileScanTasks: make([]RESTFileScanTask, taskCount),
+		DeleteFiles:   make([]RESTDeleteFile, taskCount),
+	}
+	for i := range taskCount {
+		data := dataFile
+		data.FilePath = fmt.Sprintf("s3://bucket/table/data-%d.parquet", i)
+		delete := deleteFile
+		delete.FilePath = fmt.Sprintf("s3://bucket/table/delete-%d.puffin", i)
+		if explicitOwner {
+			delete.ReferencedDataFile = stringPtr(data.FilePath)
+		}
+
+		wire.FileScanTasks[i] = RESTFileScanTask{
+			DataFile:             &data,
+			DeleteFileReferences: []int{i},
+		}
+		wire.DeleteFiles[i] = delete
+	}
+
+	return wire
+}