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