Skip to content

feat(go/adbc/driver/flightsql): add logging for Flight SQL#4322

Open
davidhcoe wants to merge 5 commits into
apache:mainfrom
davidhcoe:dev/flightsql-logging
Open

feat(go/adbc/driver/flightsql): add logging for Flight SQL#4322
davidhcoe wants to merge 5 commits into
apache:mainfrom
davidhcoe:dev/flightsql-logging

Conversation

@davidhcoe
Copy link
Copy Markdown
Contributor

  • Adds Open Telemetry logging for Flight SQL
  • Adds support for passing values as parameters to support TOML integration

@davidhcoe davidhcoe requested a review from zeroshade as a code owner May 18, 2026 20:38
#if defined(_WIN32)
#include <windows.h> // Must come first

// These version macros gate which Win32 APIs the SDK headers declare. They MUST
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to make this change to get the linter to work correctly.

Comment thread go/adbc/adbc.go
// the host process's environment. When neither this option nor the
// environment variable is set, the driver falls back to the
// process-global OpenTelemetry tracer provider.
OptionKeyTelemetryTracesExporter = "adbc.telemetry.traces_exporter"
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is so we can pass values using the driver manager / TOML values and not need to use environment variables.

@davidhcoe davidhcoe requested a review from lidavidm as a code owner May 18, 2026 21:24
headers := md.Get("authorization")
if len(headers) > 0 {
b.mutex.Lock()
defer b.mutex.Unlock()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the switch to an explicit unlock vs defer?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call — switched back to defer Unlock(). Both methods now go through a small rotateAuth(headers ...) (previous []string, logger *slog.Logger) helper that holds the mutex with defer b.mutex.Unlock(), mutates b.hdrs, and returns a snapshot of the previous auth value plus the current logger. The structured log emission happens in HeadersReceived / SetHeader outside the critical section so we don't hold the lock across the slog call.

Comment thread go/adbc/driver/flightsql/flightsql_database.go
// been open at the time of failure. Without these records the operator
// otherwise has only the bare gRPC EOF to work with, which carries no
// progress or location information.
func newRecordReader(ctx context.Context, alloc memory.Allocator, cl *flightsql.Client, info *flight.FlightInfo, clCache gcache.Cache, bufferSize int, logger *slog.Logger, opts ...grpc.CallOption) (rdr array.RecordReader, err error) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this argument list is getting rather long, could we condense some of these arguments into a struct as a config thing?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — extracted a recordReaderConfig struct (alloc, cl, info, clientCache, bufferSize, logger) and changed the signature to newRecordReader(ctx context.Context, cfg recordReaderConfig, opts ...grpc.CallOption). Logger is still optional (nil → no-op via safeLogger). Updated all 13 call sites.

// the lengths of every column buffer in the batch. The total is useful for
// answering questions such as "did the stream fail after receiving 10 KB or
// 10 MB?" when triaging a mid-stream EOF.
func estimateBatchBytes(rec arrow.RecordBatch) int64 {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched to util.TotalRecordSize. Removed the local estimateBatchBytes helper.

Comment on lines +256 to +271
// doGetWithLogger is the logging-aware DoGet implementation used by every
// caller in the driver. Earlier revisions exposed a plain `doGet` wrapper
// that supplied a nil logger; it had no remaining callers and was removed
// to satisfy the unused-function linter — every call site now passes its
// own *slog.Logger (or relies on safeLogger to materialize a no-op one).
//
// When a logger is provided every endpoint resolution attempt is logged
// individually so that failures hopping between Flight locations can be
// traced. The function also accumulates per-location attempt errors and, if
// every attempt fails, joins them into a single returned error so that the
// caller can see *all* the locations that were tried (and how each one
// failed) rather than just the last one. This is critical for diagnosing
// "[FlightSQL] error reading from server: EOF" reports where the empty
// "endpoint N: []" indicates the Location list was actually empty: with the
// enhanced error the operator can see whether multiple alternate locations
// were attempted and which one ultimately served (or failed) the request.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can trim this? e.g. I don't think we need codebase archaeology here.

Comment on lines +74 to +91
// tracesExporter records the value of
// adbc.OptionKeyTelemetryTracesExporter that was supplied (if any)
// at database construction time. The tracer itself is already
// initialized by driverbase by the time SetOptions runs, so this
// field is retained purely so GetOption can echo back the
// configured value (callers expect "get returns what was set") and
// so SetOption can return a precise diagnostic when a caller tries
// to change the exporter after the database has been opened.
tracesExporter string
// tracesFolderPath records the value of
// adbc.OptionKeyTelemetryTracesFolderPath that was supplied at
// construction time. The on-disk RotatingFileWriter behind the
// "adbcfile" exporter is created during NewDatabase*, so this field
// is retained for symmetry with tracesExporter: GetOption can echo
// the configured value back, and SetOption can surface a precise
// diagnostic when a caller tries to retarget the folder after the
// writer is already running.
tracesFolderPath string
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the stated purpose, why can't driverbase handle the get/set?

var cnxnSupport support

info, err := cl.GetSqlInfo(ctx, []flightsql.SqlInfo{flightsql.SqlInfoFlightSqlServerTransaction}, d.timeout)
// Capture the resolved gRPC peer address from the first call so the
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this actually stable? I thought there were circumstances under which gRPC would reconnect, or there may be round-robin DNS load balancing in play, etc. (Albeit I doubt anyone wants to try to deploy Flight SQL with such a setup...)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants