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 5cfdc159d perf(glue): reuse schema metadata when building Glue columns 
(#2113)
5cfdc159d is described below

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

    perf(glue): reuse schema metadata when building Glue columns (#2113)
    
    * perf(glue): reuse schema metadata when building Glue columns
    
    * test(glue): benchmark documented schema columns
    
    * perf(glue): borrow schema fields during column conversion
---
 catalog/glue/schema.go            |  18 ++++---
 catalog/glue/schema_bench_test.go | 111 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 121 insertions(+), 8 deletions(-)

diff --git a/catalog/glue/schema.go b/catalog/glue/schema.go
index d46be8a5b..c20e370af 100644
--- a/catalog/glue/schema.go
+++ b/catalog/glue/schema.go
@@ -23,6 +23,7 @@ import (
        "strings"
 
        "github.com/apache/iceberg-go"
+       iceberginternal "github.com/apache/iceberg-go/internal"
        "github.com/apache/iceberg-go/table"
        "github.com/aws/aws-sdk-go-v2/aws"
        "github.com/aws/aws-sdk-go-v2/service/glue/types"
@@ -31,6 +32,8 @@ import (
 func schemasToGlueColumns(metadata table.Metadata, existingColumns 
[]types.Column) []types.Column {
        // preserve the current schema's logical column order and then,
        // append columns from historical schemas that are not already present
+       currentSchema := metadata.CurrentSchema()
+       schemas := metadata.Schemas()
        var columns []types.Column
        addedNames := make(map[string]struct{})
 
@@ -44,12 +47,12 @@ func schemasToGlueColumns(metadata table.Metadata, 
existingColumns []types.Colum
                addedNames[name] = struct{}{}
        }
 
-       for _, field := range schemaToGlueColumns(metadata.CurrentSchema(), 
true) {
+       for _, field := range schemaToGlueColumns(currentSchema, true) {
                addColumnWithDedupe(field)
        }
 
-       for _, schema := range metadata.Schemas() {
-               if schema.ID == metadata.CurrentSchema().ID {
+       for _, schema := range schemas {
+               if schema.ID == currentSchema.ID {
                        continue
                }
 
@@ -72,17 +75,16 @@ func schemasToGlueColumns(metadata table.Metadata, 
existingColumns []types.Colum
        // Preserve nil for fields that have never had an Iceberg doc, but keep 
an
        // explicit empty string when a documented field was cleared in a later 
schema.
        clearedComments := make(map[string]struct{})
-       currentSchema := metadata.CurrentSchema()
-       for _, field := range currentSchema.Fields() {
+       for _, field := range 
currentSchema.FieldsRef(iceberginternal.SchemaRef{}) {
                if field.Doc != "" {
                        continue
                }
 
-               for _, schema := range metadata.Schemas() {
+               for _, schema := range schemas {
                        if schema.ID == currentSchema.ID {
                                continue
                        }
-                       if previous, ok := schema.FindFieldByID(field.ID); ok 
&& previous.Doc != "" {
+                       if previous, ok := schema.FindFieldByIDRef(field.ID, 
iceberginternal.SchemaRef{}); ok && previous.Doc != "" {
                                clearedComments[strconv.Itoa(field.ID)] = 
struct{}{}
 
                                break
@@ -112,7 +114,7 @@ func schemasToGlueColumns(metadata table.Metadata, 
existingColumns []types.Colum
 // schemaToGlueColumns converts an Iceberg schema to a list of Glue columns.
 func schemaToGlueColumns(schema *iceberg.Schema, isCurrent bool) 
[]types.Column {
        var columns []types.Column
-       for _, field := range schema.Fields() {
+       for _, field := range schema.FieldsRef(iceberginternal.SchemaRef{}) {
                columns = append(columns, fieldToGlueColumn(field, isCurrent))
        }
 
diff --git a/catalog/glue/schema_bench_test.go 
b/catalog/glue/schema_bench_test.go
new file mode 100644
index 000000000..43a146c68
--- /dev/null
+++ b/catalog/glue/schema_bench_test.go
@@ -0,0 +1,111 @@
+// 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 glue
+
+import (
+       "fmt"
+       "slices"
+       "testing"
+
+       "github.com/apache/iceberg-go"
+       "github.com/apache/iceberg-go/table"
+       "github.com/aws/aws-sdk-go-v2/service/glue/types"
+)
+
+var schemasToGlueColumnsBenchmarkSink []types.Column
+
+func BenchmarkSchemasToGlueColumns(b *testing.B) {
+       for _, tc := range []struct {
+               schemaCount int
+               fieldCount  int
+       }{
+               {schemaCount: 1, fieldCount: 16},
+               {schemaCount: 10, fieldCount: 128},
+               {schemaCount: 100, fieldCount: 128},
+       } {
+               b.Run(fmt.Sprintf("schemas=%d/fields=%d", tc.schemaCount, 
tc.fieldCount), func(b *testing.B) {
+                       metadata := schemaHistoryBenchmarkMetadata(b, 
tc.schemaCount, tc.fieldCount, false)
+
+                       b.ReportAllocs()
+                       b.ResetTimer()
+                       for range b.N {
+                               schemasToGlueColumnsBenchmarkSink = 
schemasToGlueColumns(metadata, nil)
+                       }
+               })
+       }
+}
+
+func schemaHistoryBenchmarkMetadata(b *testing.B, schemaCount, fieldCount int, 
documented bool) table.Metadata {
+       b.Helper()
+
+       fields := make([]iceberg.NestedField, fieldCount)
+       for i := range fields {
+               fields[i] = iceberg.NestedField{
+                       ID:   i + 1,
+                       Name: fmt.Sprintf("field_%d", i+1),
+                       Type: iceberg.PrimitiveTypes.String,
+               }
+               if documented {
+                       fields[i].Doc = "field documentation"
+               }
+       }
+
+       base, err := table.NewMetadata(iceberg.NewSchema(0, fields...), nil,
+               table.SortOrder{}, "s3://glue-schema-benchmark", nil)
+       if err != nil {
+               b.Fatal(err)
+       }
+       builder, err := table.MetadataBuilderFromBase(base, "")
+       if err != nil {
+               b.Fatal(err)
+       }
+
+       for schemaID := 1; schemaID < schemaCount; schemaID++ {
+               schemaFields := slices.Clone(fields)
+               if schemaID == schemaCount-1 {
+                       schemaFields[0].Name = "current_field"
+               } else {
+                       schemaFields[0].Name = 
fmt.Sprintf("historical_field_%d", schemaID)
+               }
+               if err := builder.AddSchema(iceberg.NewSchema(schemaID, 
schemaFields...)); err != nil {
+                       b.Fatal(err)
+               }
+       }
+       if err := builder.SetCurrentSchemaID(schemaCount - 1); err != nil {
+               b.Fatal(err)
+       }
+
+       metadata, err := builder.Build()
+       if err != nil {
+               b.Fatal(err)
+       }
+
+       return metadata
+}
+
+func BenchmarkSchemasToGlueColumnsDocumented(b *testing.B) {
+       for _, schemaCount := range []int{1, 100} {
+               b.Run(fmt.Sprintf("schemas=%d/fields=128", schemaCount), func(b 
*testing.B) {
+                       metadata := schemaHistoryBenchmarkMetadata(b, 
schemaCount, 128, true)
+                       b.ReportAllocs()
+                       for b.Loop() {
+                               schemasToGlueColumnsBenchmarkSink = 
schemasToGlueColumns(metadata, nil)
+                       }
+               })
+       }
+}

Reply via email to