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/06/24 19:38:04 UTC
(iceberg-go) branch main updated: fix(table): release empty scan batches (#1290)
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 d60220fb fix(table): release empty scan batches (#1290)
d60220fb is described below
commit d60220fbfa3120d5d16897051c80d46fe816fe34
Author: Minh Vu <vu...@gmail.com>
AuthorDate: Wed Jun 24 21:37:59 2026 +0200
fix(table): release empty scan batches (#1290)
Summary
Release zero-row record batches before `createIterator` skips them. The
iterator owns batches after pulling them from the sequenced channel, so
an empty batch still needs the same cleanup as any other batch that is
not yielded.
The test uses a checked allocator and a zero-row slice backed by a real
Arrow buffer, so it catches the leak directly instead of relying on a
full scan setup.
Testing
- `go test ./table -run '^TestCreateIteratorReleasesEmptyRecordBatches$'
-count=1`
- `go test ./table -count=1`
- `go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.11.4
run --timeout=10m ./table/...`
- `git diff --check`
---
table/arrow_scanner.go | 2 ++
table/arrow_scanner_test.go | 45 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 47 insertions(+)
diff --git a/table/arrow_scanner.go b/table/arrow_scanner.go
index 3643de66..26b18d19 100644
--- a/table/arrow_scanner.go
+++ b/table/arrow_scanner.go
@@ -1277,6 +1277,8 @@ func createIterator(ctx context.Context, numWorkers uint, records <-chan enumera
if rec.NumRows() == 0 {
// skip empty records
+ rec.Release()
+
continue
}
diff --git a/table/arrow_scanner_test.go b/table/arrow_scanner_test.go
index b527d896..6cbe7170 100644
--- a/table/arrow_scanner_test.go
+++ b/table/arrow_scanner_test.go
@@ -18,6 +18,7 @@
package table
import (
+ "context"
"errors"
iofs "io/fs"
"strconv"
@@ -34,6 +35,7 @@ import (
"github.com/apache/arrow-go/v18/parquet/pqarrow"
"github.com/apache/iceberg-go"
iceio "github.com/apache/iceberg-go/io"
+ "github.com/apache/iceberg-go/table/internal"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -325,3 +327,46 @@ func TestReleasePerFilePosDeletes(t *testing.T) {
require.NotPanics(t, func() { releasePerFilePosDeletes(m) })
})
}
+
+func TestCreateIteratorReleasesEmptyRecordBatches(t *testing.T) {
+ mem := memory.NewCheckedAllocator(memory.DefaultAllocator)
+ defer mem.AssertSize(t, 0)
+
+ bldr := array.NewInt64Builder(mem)
+ bldr.AppendValues([]int64{1, 2}, nil)
+ arr := bldr.NewArray()
+ bldr.Release()
+
+ emptyArr := array.NewSlice(arr, 0, 0)
+ arr.Release()
+
+ schema := arrow.NewSchema([]arrow.Field{
+ {Name: "id", Type: arrow.PrimitiveTypes.Int64},
+ }, nil)
+ batch := array.NewRecordBatch(schema, []arrow.Array{emptyArr}, 0)
+ emptyArr.Release()
+
+ records := make(chan enumeratedRecord, 1)
+ records <- enumeratedRecord{
+ Record: internal.Enumerated[arrow.RecordBatch]{
+ Value: batch,
+ Index: 0,
+ Last: true,
+ },
+ Task: internal.Enumerated[FileScanTask]{
+ Index: 0,
+ Last: true,
+ },
+ }
+ close(records)
+
+ ctx, cancel := context.WithCancelCause(context.Background())
+ itr := createIterator(ctx, 1, records, nil, cancel, 0)
+ for rec, err := range itr {
+ require.NoError(t, err)
+ if rec != nil {
+ rec.Release()
+ }
+ require.Fail(t, "empty record batch should not be yielded")
+ }
+}