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

lidavidm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-adbc.git


The following commit(s) were added to refs/heads/main by this push:
     new c7c3b0395 feat(go/adbc/driver/flightsql): implement tracing for core 
Flight SQL operations (#4486)
c7c3b0395 is described below

commit c7c3b03954127ac1bacf091d141b8099eedfda83
Author: Artur Rakhmatulin <[email protected]>
AuthorDate: Fri Jul 10 10:00:40 2026 +0100

    feat(go/adbc/driver/flightsql): implement tracing for core Flight SQL 
operations (#4486)
    
    ## Summary
    This change wires OpenTelemetry tracing into the core Flight SQL
    execution path and ensures configured trace exporters are flushed when
    the database is closed.
    
    Before this change, the Flight SQL driver accepted tracing configuration
    but did not emit trace data for the main open/query path. The Flight SQL
    database close path also did not delegate to the shared database close
    implementation, so tracer shutdown and exporter flush did not run.
    
    Closes #4487
---
 go/adbc/driver/flightsql/flightsql_adbc_test.go  | 56 ++++++++++++++++++++++++
 go/adbc/driver/flightsql/flightsql_database.go   |  9 +++-
 go/adbc/driver/flightsql/flightsql_statement.go  | 15 ++++++-
 go/adbc/driver/internal/driverbase/connection.go |  3 +-
 go/adbc/driver/internal/driverbase/database.go   | 27 +++++++++++-
 5 files changed, 104 insertions(+), 6 deletions(-)

diff --git a/go/adbc/driver/flightsql/flightsql_adbc_test.go 
b/go/adbc/driver/flightsql/flightsql_adbc_test.go
index 93d8b2b68..92c00e5b4 100644
--- a/go/adbc/driver/flightsql/flightsql_adbc_test.go
+++ b/go/adbc/driver/flightsql/flightsql_adbc_test.go
@@ -58,6 +58,7 @@ import (
        "google.golang.org/grpc/credentials"
        "google.golang.org/grpc/metadata"
        "google.golang.org/protobuf/proto"
+       _ "modernc.org/sqlite"
 )
 
 type HeaderServerMiddleware struct {
@@ -316,6 +317,61 @@ func TestADBCFlightSQL(t *testing.T) {
        suite.Run(t, &DomainSocketTests{db: db})
 }
 
+func TestFlightSQLTracingProducesTraceFiles(t *testing.T) {
+       // TODO: Reuse example.CreateDB() here after apache/arrow-go#916 
isolates
+       // the shared in-memory SQLite database per call.
+       db, err := sql.Open("sqlite", 
fmt.Sprintf("file:%s-%d?mode=memory&cache=private", strings.ToLower(t.Name()), 
time.Now().UnixNano()))
+       require.NoError(t, err)
+       defer validation.CheckedClose(t, db)
+
+       q := &FlightSQLQuirks{db: db}
+       drv := q.SetupDriver(t)
+       defer q.TearDownDriver(t, drv)
+
+       traceDir := t.TempDir()
+       opts := q.DatabaseOptions()
+       opts[adbc.OptionKeyTelemetryTracesExporter] = 
string(adbc.TelemetryExporterAdbcFile)
+       opts[adbc.OptionKeyTelemetryTracesFolderPath] = traceDir
+
+       func() {
+               adbcDB, err := drv.NewDatabase(opts)
+               require.NoError(t, err)
+               defer validation.CheckedClose(t, adbcDB)
+
+               cnxn, err := adbcDB.Open(context.Background())
+               require.NoError(t, err)
+               defer validation.CheckedClose(t, cnxn)
+
+               stmt, err := cnxn.NewStatement()
+               require.NoError(t, err)
+               defer validation.CheckedClose(t, stmt)
+
+               require.NoError(t, stmt.SetSqlQuery("SELECT 1"))
+               reader, _, err := stmt.ExecuteQuery(context.Background())
+               require.NoError(t, err)
+               defer reader.Release()
+
+               for reader.Next() {
+               }
+               require.NoError(t, reader.Err())
+       }()
+
+       files, err := filepath.Glob(filepath.Join(traceDir, "*.jsonl"))
+       require.NoError(t, err)
+       require.NotEmpty(t, files)
+
+       var traceOutput strings.Builder
+       for _, file := range files {
+               data, err := os.ReadFile(file)
+               require.NoError(t, err)
+               traceOutput.Write(data)
+       }
+
+       output := traceOutput.String()
+       require.Contains(t, output, "FlightSQLDatabase.Open")
+       require.Contains(t, output, "FlightSQLStatement.ExecuteQuery")
+}
+
 // Run the test suite, but validating that a header set on the database is 
ALWAYS passed
 
 type FlightSQLWithHeaderQuirks struct {
diff --git a/go/adbc/driver/flightsql/flightsql_database.go 
b/go/adbc/driver/flightsql/flightsql_database.go
index 259e534ed..15b2f90c2 100644
--- a/go/adbc/driver/flightsql/flightsql_database.go
+++ b/go/adbc/driver/flightsql/flightsql_database.go
@@ -30,6 +30,7 @@ import (
        "time"
 
        "github.com/apache/arrow-adbc/go/adbc"
+       "github.com/apache/arrow-adbc/go/adbc/driver/internal"
        "github.com/apache/arrow-adbc/go/adbc/driver/internal/driverbase"
        "github.com/apache/arrow-go/v18/arrow/array"
        "github.com/apache/arrow-go/v18/arrow/flight"
@@ -373,7 +374,7 @@ func (d *databaseImpl) Close() error {
                        "target", d.uri.String(),
                )
        }
-       return nil
+       return d.DatabaseImplBase.Close()
 }
 
 func getFlightClient(ctx context.Context, loc string, d *databaseImpl, 
authMiddle *bearerAuthMiddleware, cookies flight.CookieMiddleware) 
(*flightsql.Client, error) {
@@ -480,7 +481,11 @@ type support struct {
        transactions bool
 }
 
-func (d *databaseImpl) Open(ctx context.Context) (adbc.Connection, error) {
+func (d *databaseImpl) Open(ctx context.Context) (_ adbc.Connection, err 
error) {
+       ctx, span := internal.StartSpan(ctx, "FlightSQLDatabase.Open", d)
+       // TODO(apache/arrow-adbc#4494): replace with a shared telemetry helper.
+       defer func() { internal.EndSpan(span, err) }()
+
        authMiddle := &bearerAuthMiddleware{hdrs: d.hdrs.Copy(), logger: 
safeLogger(d.Logger)}
        var cookies flight.CookieMiddleware
        if d.enableCookies {
diff --git a/go/adbc/driver/flightsql/flightsql_statement.go 
b/go/adbc/driver/flightsql/flightsql_statement.go
index 61705ae6f..14283380e 100644
--- a/go/adbc/driver/flightsql/flightsql_statement.go
+++ b/go/adbc/driver/flightsql/flightsql_statement.go
@@ -29,6 +29,7 @@ import (
        "unsafe"
 
        "github.com/apache/arrow-adbc/go/adbc"
+       "github.com/apache/arrow-adbc/go/adbc/driver/internal"
        "github.com/apache/arrow-go/v18/arrow"
        "github.com/apache/arrow-go/v18/arrow/array"
        "github.com/apache/arrow-go/v18/arrow/flight"
@@ -530,6 +531,10 @@ func (s *statement) ExecuteQuery(ctx context.Context) (rdr 
array.RecordReader, n
                }
        }
 
+       ctx, span := internal.StartSpan(ctx, "FlightSQLStatement.ExecuteQuery", 
s.cnxn)
+       // TODO(apache/arrow-adbc#4494): replace with a shared telemetry helper.
+       defer func() { internal.EndSpan(span, err) }()
+
        // Handle bulk ingest
        if s.targetTable != "" {
                nrec, err = s.executeIngest(ctx)
@@ -602,6 +607,10 @@ func (s *statement) ExecuteUpdate(ctx context.Context) (n 
int64, err error) {
                }
        }
 
+       ctx, span := internal.StartSpan(ctx, 
"FlightSQLStatement.ExecuteUpdate", s.cnxn)
+       // TODO(apache/arrow-adbc#4494): replace with a shared telemetry helper.
+       defer func() { internal.EndSpan(span, err) }()
+
        // Handle bulk ingest
        if s.targetTable != "" {
                return s.executeIngest(ctx)
@@ -647,7 +656,11 @@ func (s *statement) ExecuteUpdate(ctx context.Context) (n 
int64, err error) {
 
 // Prepare turns this statement into a prepared statement to be executed
 // multiple times. This invalidates any prior result sets.
-func (s *statement) Prepare(ctx context.Context) error {
+func (s *statement) Prepare(ctx context.Context) (err error) {
+       ctx, span := internal.StartSpan(ctx, "FlightSQLStatement.Prepare", 
s.cnxn)
+       // TODO(apache/arrow-adbc#4494): replace with a shared telemetry helper.
+       defer func() { internal.EndSpan(span, err) }()
+
        startTime := time.Now()
        s.log.InfoContext(ctx, "FlightSQL Prepare start", s.queryAttrs()...)
 
diff --git a/go/adbc/driver/internal/driverbase/connection.go 
b/go/adbc/driver/internal/driverbase/connection.go
index 272504c10..d716edc5c 100644
--- a/go/adbc/driver/internal/driverbase/connection.go
+++ b/go/adbc/driver/internal/driverbase/connection.go
@@ -154,7 +154,8 @@ func (base *ConnectionImplBase) Rollback(context.Context) 
error {
 
 func (base *ConnectionImplBase) GetInfo(ctx context.Context, infoCodes 
[]adbc.InfoCode) (reader array.RecordReader, err error) {
        _, span := internal.StartSpan(ctx, "ConnectionImplBase.GetInfo", base)
-       defer internal.EndSpan(span, err)
+       // TODO(apache/arrow-adbc#4494): replace with a shared telemetry helper.
+       defer func() { internal.EndSpan(span, err) }()
 
        if len(infoCodes) == 0 {
                infoCodes = base.DriverInfo.InfoSupportedCodes()
diff --git a/go/adbc/driver/internal/driverbase/database.go 
b/go/adbc/driver/internal/driverbase/database.go
index 5cff37a84..991e7270e 100644
--- a/go/adbc/driver/internal/driverbase/database.go
+++ b/go/adbc/driver/internal/driverbase/database.go
@@ -20,6 +20,7 @@ package driverbase
 import (
        "context"
        "errors"
+       "io"
        "log/slog"
        "os"
        "strings"
@@ -423,7 +424,24 @@ func newOtlpTraceExporters(ctx context.Context) 
([]sdktrace.SpanExporter, error)
        return []sdktrace.SpanExporter{grpcExporter, httpExporter}, nil
 }
 
-func newAdbcFileExporter(driverName, folderPath string) 
(*stdouttrace.Exporter, error) {
+type closableSpanExporter struct {
+       exporter sdktrace.SpanExporter
+       closer   io.Closer
+}
+
+func (e *closableSpanExporter) ExportSpans(ctx context.Context, spans 
[]sdktrace.ReadOnlySpan) error {
+       return e.exporter.ExportSpans(ctx, spans)
+}
+
+func (e *closableSpanExporter) Shutdown(ctx context.Context) error {
+       err := e.exporter.Shutdown(ctx)
+       if e.closer != nil {
+               err = errors.Join(err, e.closer.Close())
+       }
+       return err
+}
+
+func newAdbcFileExporter(driverName, folderPath string) 
(sdktrace.SpanExporter, error) {
        fullyQualifiedDriverName := strings.ToLower(driverNamespace + "." + 
driverName)
        writerOpts := 
[]rotatingFileWriterOption{WithLogNamePrefix(fullyQualifiedDriverName)}
        if strings.TrimSpace(folderPath) != "" {
@@ -433,7 +451,12 @@ func newAdbcFileExporter(driverName, folderPath string) 
(*stdouttrace.Exporter,
        if err != nil {
                return nil, err
        }
-       return stdouttrace.New(stdouttrace.WithWriter(fileWriter))
+       exporter, err := stdouttrace.New(stdouttrace.WithWriter(fileWriter))
+       if err != nil {
+               _ = fileWriter.Close()
+               return nil, err
+       }
+       return &closableSpanExporter{exporter: exporter, closer: fileWriter}, 
nil
 }
 
 func newTracerProvider(exporters ...sdktrace.SpanExporter) 
(*sdktrace.TracerProvider, error) {

Reply via email to