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

laskoviymishka 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 d7ced4a7b perf(table): avoid cloning cached snapshot manifests (#2112)
d7ced4a7b is described below

commit d7ced4a7b27bc456066d38236af97c6b2e4e3ee0
Author: Minh Vu <[email protected]>
AuthorDate: Tue Oct 6 10:27:10 2026 +0200

    perf(table): avoid cloning cached snapshot manifests (#2112)
    
    * perf(table): avoid cloning cached snapshot manifests
    
    * test(table): benchmark cached manifest access
    
    * perf(table): document borrowed manifest slice contract
---
 table/incremental_append_scan.go               |  2 +-
 table/inspect.go                               |  2 +-
 table/inspect_all_manifests.go                 |  2 +-
 table/inspect_files.go                         |  4 ++--
 table/inspect_partitions.go                    |  2 +-
 table/inspect_position_deletes.go              |  2 +-
 table/scanner.go                               |  2 +-
 table/snapshot_manifest_cache.go               | 12 ++++++++++
 table/snapshot_manifest_cache_bench_test.go    | 31 ++++++++++++++++++++++++++
 table/snapshot_manifest_cache_internal_test.go |  4 ++++
 table/table.go                                 |  2 +-
 11 files changed, 56 insertions(+), 9 deletions(-)

diff --git a/table/incremental_append_scan.go b/table/incremental_append_scan.go
index d2e71edb6..cdb0dc565 100644
--- a/table/incremental_append_scan.go
+++ b/table/incremental_append_scan.go
@@ -173,7 +173,7 @@ func (s *IncrementalAppendScan) PlanFiles(ctx 
context.Context) ([]FileScanTask,
                if err != nil {
                        return nil, err
                }
-               for _, manifest := range manifestSet.dataManifests() {
+               for _, manifest := range manifestSet.borrowDataManifests() {
                        if _, ok := appendSnapshots[manifest.SnapshotID()]; !ok 
{
                                continue
                        }
diff --git a/table/inspect.go b/table/inspect.go
index 67ff62033..af1ea364e 100644
--- a/table/inspect.go
+++ b/table/inspect.go
@@ -456,7 +456,7 @@ func (i InspectTable) currentSnapshotManifests(ctx 
context.Context) ([]iceberg.M
                return nil, err
        }
 
-       return manifestSet.allManifests(), nil
+       return manifestSet.borrowAllManifests(), nil
 }
 
 func appendManifestBound(builder *array.StringBuilder, typ iceberg.Type, 
transform iceberg.Transform, bound *[]byte) error {
diff --git a/table/inspect_all_manifests.go b/table/inspect_all_manifests.go
index 15ad1bb9d..144f3fbd9 100644
--- a/table/inspect_all_manifests.go
+++ b/table/inspect_all_manifests.go
@@ -50,7 +50,7 @@ func (i InspectTable) AllManifests(ctx context.Context) 
(array.RecordReader, err
                                return nil, err
                        }
 
-                       return manifestSet.allManifests(), nil
+                       return manifestSet.borrowAllManifests(), nil
                }
        }
 
diff --git a/table/inspect_files.go b/table/inspect_files.go
index 4705f785f..f326c0a46 100644
--- a/table/inspect_files.go
+++ b/table/inspect_files.go
@@ -140,7 +140,7 @@ func (i InspectTable) manifestEntryReader(
        if err != nil {
                return nil, err
        }
-       manifests := manifestSet.allManifests()
+       manifests := manifestSet.borrowAllManifests()
 
        return i.manifestEntryReaderFromManifestSource(
                ctx, arrowSchema, fs, discardDeleted, includeManifest, 
appendEntry,
@@ -193,7 +193,7 @@ func (i InspectTable) allManifestEntryReader(
 
                                        return
                                }
-                               for _, manifest := range 
manifestSet.allManifests() {
+                               for _, manifest := range 
manifestSet.borrowAllManifests() {
                                        if _, ok := seen[manifest.FilePath()]; 
ok {
                                                continue
                                        }
diff --git a/table/inspect_partitions.go b/table/inspect_partitions.go
index 65cc753a4..2ae944db9 100644
--- a/table/inspect_partitions.go
+++ b/table/inspect_partitions.go
@@ -154,7 +154,7 @@ func (i InspectTable) partitionAggregates(ctx 
context.Context, partitionType *ic
        if err != nil {
                return nil, err
        }
-       manifests := manifestSet.allManifests()
+       manifests := manifestSet.borrowAllManifests()
 
        snapshotTimes := make(map[int64]int64, len(i.tbl.metadata.Snapshots()))
        for _, snapshot := range i.tbl.metadata.Snapshots() {
diff --git a/table/inspect_position_deletes.go 
b/table/inspect_position_deletes.go
index 72ad08c51..e3bbc3977 100644
--- a/table/inspect_position_deletes.go
+++ b/table/inspect_position_deletes.go
@@ -89,7 +89,7 @@ func (i InspectTable) currentPositionDeleteManifests(
                return nil, nil, err
        }
 
-       return fs, manifestSet.allManifests(), nil
+       return fs, manifestSet.borrowAllManifests(), nil
 }
 
 type positionDeleteRecordAppender struct {
diff --git a/table/scanner.go b/table/scanner.go
index 9592212b0..60507598d 100644
--- a/table/scanner.go
+++ b/table/scanner.go
@@ -1062,7 +1062,7 @@ func (scan *Scan) 
fetchPartitionSpecFilteredManifestsWithSchema(
                return nil, err
        }
 
-       return scan.filterManifestsWithSchema(manifestSet.allManifests(), 
schema, acc, partitionFilters)
+       return scan.filterManifestsWithSchema(manifestSet.borrowAllManifests(), 
schema, acc, partitionFilters)
 }
 
 func (scan *Scan) manifestSet(
diff --git a/table/snapshot_manifest_cache.go b/table/snapshot_manifest_cache.go
index b4bfedd24..867c99d53 100644
--- a/table/snapshot_manifest_cache.go
+++ b/table/snapshot_manifest_cache.go
@@ -77,6 +77,18 @@ func (s snapshotManifestSet) dataManifests() 
[]iceberg.ManifestFile {
        return slices.Clone(s.data)
 }
 
+// borrowAllManifests returns the cache-owned manifest slice without copying.
+// Callers must treat the slice as read-only and must not reorder or replace 
entries.
+func (s snapshotManifestSet) borrowAllManifests() []iceberg.ManifestFile {
+       return s.all
+}
+
+// borrowDataManifests returns the cache-owned data-manifest slice without 
copying.
+// Callers must treat the slice as read-only and must not reorder or replace 
entries.
+func (s snapshotManifestSet) borrowDataManifests() []iceberg.ManifestFile {
+       return s.data
+}
+
 func snapshotManifestSetSize(set snapshotManifestSet) int {
        return len(set.all)
 }
diff --git a/table/snapshot_manifest_cache_bench_test.go 
b/table/snapshot_manifest_cache_bench_test.go
index 1624d8c4c..9a92a1c87 100644
--- a/table/snapshot_manifest_cache_bench_test.go
+++ b/table/snapshot_manifest_cache_bench_test.go
@@ -23,6 +23,7 @@ import (
        "sync"
        "testing"
 
+       "github.com/apache/iceberg-go"
        iceio "github.com/apache/iceberg-go/io"
 )
 
@@ -96,3 +97,33 @@ func (fs *snapshotManifestCacheBenchmarkIO) Open(name 
string) (iceio.File, error
 
        return fs.IO.Open(name)
 }
+
+var snapshotManifestSliceBenchmarkSink []iceberg.ManifestFile
+
+func BenchmarkInspectSnapshotManifestCacheHit(b *testing.B) {
+       for _, manifestCount := range []int{1, 1000, 10000} {
+               b.Run(fmt.Sprintf("manifests=%d", manifestCount), func(b 
*testing.B) {
+                       tbl := newRowLimitPlanningBenchmarkTable(b, 
manifestCount)
+                       inspector := tbl.Inspect()
+                       ctx := b.Context()
+                       manifests, err := 
inspector.currentSnapshotManifests(ctx)
+                       if err != nil {
+                               b.Fatal(err)
+                       }
+                       if len(manifests) != manifestCount {
+                               b.Fatalf("got %d manifests, want %d", 
len(manifests), manifestCount)
+                       }
+                       b.ReportAllocs()
+                       for b.Loop() {
+                               manifests, err := 
inspector.currentSnapshotManifests(ctx)
+                               if err != nil {
+                                       b.Fatal(err)
+                               }
+                               if len(manifests) != manifestCount {
+                                       b.Fatalf("got %d manifests, want %d", 
len(manifests), manifestCount)
+                               }
+                               snapshotManifestSliceBenchmarkSink = manifests
+                       }
+               })
+       }
+}
diff --git a/table/snapshot_manifest_cache_internal_test.go 
b/table/snapshot_manifest_cache_internal_test.go
index 662646fe6..30a47056b 100644
--- a/table/snapshot_manifest_cache_internal_test.go
+++ b/table/snapshot_manifest_cache_internal_test.go
@@ -97,6 +97,10 @@ func 
TestSnapshotManifestCacheSeparatesContentAndProtectsSlices(t *testing.T) {
        require.NoError(t, err)
        require.Len(t, set.allManifests(), 2)
        require.Len(t, set.dataManifests(), 1)
+       borrowedAll := set.borrowAllManifests()
+       borrowedData := set.borrowDataManifests()
+       assert.Same(t, &set.all[0], &borrowedAll[0])
+       assert.Same(t, &set.data[0], &borrowedData[0])
 
        all := set.allManifests()
        all[0] = nil
diff --git a/table/table.go b/table/table.go
index c77d66517..9bb3df320 100644
--- a/table/table.go
+++ b/table/table.go
@@ -417,7 +417,7 @@ func (t Table) AllManifests(ctx context.Context) 
iter.Seq2[iceberg.ManifestFile,
                                        if err != nil {
                                                return err
                                        }
-                                       manifests := manifestSet.allManifests()
+                                       manifests := 
manifestSet.borrowAllManifests()
 
                                        select {
                                        case ch <- list{Index: i, Value: 
manifests, Last: i == n-1}:

Reply via email to