Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions CONNECTION_PARAMETERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,21 +213,23 @@ left unset (the default), a server-side feature flag decides whether wrapper tel
active; setting it explicitly overrides the flag. On the kernel backend, the Go wrapper
skips its telemetry interceptor so it does not duplicate kernel-owned telemetry for the
same connection and statements, and forwards the kernel-owned telemetry knobs into the
kernel config.
kernel config only when `enableTelemetry` is explicitly supplied.

| DSN parameter | Thrift | Kernel | Default | Notes |
|---|:---:|:---:|---|---|
| `enableTelemetry` | ✅ | ✅ | unset (server flag decides wrapper telemetry; kernel telemetry defaults on) | Force Go wrapper telemetry on/off on the Thrift path, overriding the server feature flag. On the kernel path, forwarded to kernel-owned telemetry; unset forwards enabled. |
| `telemetry_batch_size` | ✅ | ✅ | `200` wrapper default; kernel default when unset | Events per batch. Forwarded to the kernel only when explicitly set. |
| `telemetry_flush_interval` | ✅ | ✅ | `30s` wrapper default; kernel default when unset | Flush interval. Forwarded to the kernel only when explicitly set. |
| `enableTelemetry` | ✅ | ✅ | unset (server flag decides wrapper telemetry; kernel default decides kernel telemetry) | Force Go wrapper telemetry on/off on the Thrift path, overriding the server feature flag. On the kernel path, forwarded to kernel-owned telemetry only when explicitly set. |
| `telemetry_batch_size` | ✅ | ✅ | `200` wrapper default; kernel default when `enableTelemetry` is unset | Events per batch. Forwarded to the kernel only when `enableTelemetry` is explicitly set. |
| `telemetry_flush_interval` | ✅ | ✅ | `30s` wrapper default; kernel default when `enableTelemetry` is unset | Flush interval. Forwarded to the kernel only when `enableTelemetry` is explicitly set. |
| `telemetry_retry_count` | ⚠️ | ⚠️ | — | **Deprecated and ignored** (retries are owned by the HTTP client + circuit breaker); logs a one-time warning. |
| `telemetry_retry_delay` | ⚠️ | ⚠️ | — | **Deprecated and ignored** (see above). |

These telemetry knobs are **DSN-only** — there are no `WithX` connector options for them.
An app assembled with `NewConnector(...)` options rather than a DSN cannot tune telemetry:
wrapper telemetry falls back to the server feature flag (since `enableTelemetry` is
unset), kernel telemetry defaults on, and `telemetry_batch_size` /
`telemetry_flush_interval` use their backend defaults.
unset), and kernel telemetry uses the kernel defaults. On the kernel path,
`telemetry_batch_size` and `telemetry_flush_interval` are applied only with an explicit
`enableTelemetry` value because the kernel C ABI does not accept an unset enabled flag
alongside tuning fields.

The Go wrapper telemetry interceptor is skipped on the kernel path so it does not
duplicate kernel-owned telemetry for the same connection and statements. See
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -362,8 +362,9 @@ left unset (the default), a **server-side feature flag** decides whether wrapper
telemetry is active — so it may be enabled without an explicit opt-in. Setting
`enableTelemetry` explicitly overrides the flag. On the kernel backend, the Go wrapper
skips its telemetry interceptor entirely so it does not duplicate kernel-owned
telemetry; `enableTelemetry` and `telemetry_batch_size` are forwarded into the kernel
telemetry config instead.
telemetry; when `enableTelemetry` is explicitly supplied, it and any telemetry tuning
knobs are forwarded into the kernel telemetry config. When `enableTelemetry` is unset,
telemetry config is left for the kernel default.

```
# force on (regardless of the server flag):
Expand Down
3 changes: 2 additions & 1 deletion doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,8 @@ Features above the backend seam are inherited unchanged: the database/sql connec
pool and connection lifecycle. The Go wrapper telemetry interceptor is skipped on the
kernel path so it does not duplicate kernel-owned telemetry for the same connection and
statements; `enableTelemetry`, `telemetry_batch_size`, and
`telemetry_flush_interval` are forwarded to kernel-owned telemetry config. Result types render byte-for-byte identical to the
`telemetry_flush_interval` are forwarded to kernel-owned telemetry config only when
`enableTelemetry` is explicitly supplied. Result types render byte-for-byte identical to the
Thrift backend: scalars, DECIMAL (exact string), TIMESTAMP / TIMESTAMP_NTZ (shifted
into the session time zone), INTERVAL, nested Array/Map/Struct and VARIANT (as JSON),
and GEOMETRY / GEOGRAPHY (WKT). The server query id is surfaced on the success path, so a
Expand Down
5 changes: 3 additions & 2 deletions internal/backend/kernel/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,9 @@ func requestTimeoutMilliseconds(timeout time.Duration) int64 {

// TelemetryConfig is the kernel telemetry subset exposed by the Go driver. It
// mirrors the kernel telemetry C ABI: Enabled follows the user-supplied
// enableTelemetry value, defaulting to true when unset. Zero-valued tuning fields
// keep the kernel defaults until applyTelemetry fills them for the setter.
// enableTelemetry value. A nil Config.Telemetry means enableTelemetry was unset,
// so the kernel default is left untouched. Zero-valued tuning fields keep the
// kernel defaults until applyTelemetry fills them for the setter.
type TelemetryConfig struct {
Enabled bool
BatchSize int
Expand Down
10 changes: 5 additions & 5 deletions kernel_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,8 @@ func buildKernelConfig(cfg *config.Config, kauth kernel.Auth) kernel.Config {
// server identically with no per-backend translation.
SessionConf: cfg.EffectiveSessionParams(),
// Kernel-owned telemetry. The Go wrapper interceptor is skipped on the
// kernel path, but the kernel still needs the user's telemetry knobs and
// this binding's system identity for its own runtime.
// kernel path. Leave Telemetry nil when enableTelemetry is unset so the
// kernel's own default policy remains authoritative.
Telemetry: kernelTelemetryConfig(cfg),
DriverSystemConfiguration: kernelDriverSystemConfiguration(cfg),
}
Expand Down Expand Up @@ -192,9 +192,9 @@ func buildKernelConfig(cfg *config.Config, kauth kernel.Auth) kernel.Config {
}

func kernelTelemetryConfig(cfg *config.Config) *kernel.TelemetryConfig {
enabled := true
if val, isSet := cfg.EnableTelemetry.Get(); isSet {
enabled = val
enabled, enableSet := cfg.EnableTelemetry.Get()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Low — When enableTelemetry is unset but a user supplies telemetry_batch_size / telemetry_flush_interval, this now returns nil and those tuning knobs are silently dropped (the new test kernel telemetry config is nil when enableTelemetry is unset sets TelemetryBatchSize=17/FlushInterval=9s and asserts Telemetry == nil, confirming the drop). This is a deliberate, documented consequence of the kernel C ABI constraint, but it sits in tension with this repo's "nothing silently ignored" contract — and unlike the sibling retry policy, which is deliberately logged at Debug in OpenSession precisely because forwarding is otherwise invisible, this drop leaves no trace. Consider a one-line Debug log (or a one-time warning) when tuning fields are set while enableTelemetry is unset, so an on-call reviewer can see the knobs were intentionally not forwarded. Not a correctness bug — the behavior matches the docs; this is an observability gap.

if !enableSet {
return nil

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium — When a tuning knob is supplied but enableTelemetry is not (e.g. DSN sets only telemetry_batch_size=200 or telemetry_flush_interval), enableSet is false, so enabled takes the bool zero value false. The nil short-circuit above does not fire (a tuning field is non-zero), so this returns &kernel.TelemetryConfig{Enabled: false, BatchSize: ...}. applyTelemetry then forwards C.bool(t.Enabled) = false to kernel_session_config_set_telemetry_config, which is an explicit disable, not "leave the kernel default."

So tuning telemetry without also passing enableTelemetry=true now turns kernel telemetry OFF — the opposite of the documented contract for this PR (README/doc.go/CONNECTION_PARAMETERS: unset enableTelemetry should let "the kernel default decide"). It's also a regression from the prior behavior, where enabled defaulted to true.

The new test kernel telemetry tuning without enableTelemetry uses kernel enabled default illustrates the mismatch: its name says the kernel's enabled default should apply, but its assertion locks in Enabled=false — which the C setter interprets as a hard disable, not a default.

If the intent is "tune the knobs but leave enablement to the kernel," the Enabled field alone can't express that (there's no tri-state at the C ABI as modeled here). Consider either defaulting enabled to true when only tuning is set (restoring prior behavior), or documenting explicitly that supplying a tuning knob without enableTelemetry disables telemetry.

(Anchored to the nearest changed line — see the description for the exact location.)

}
return &kernel.TelemetryConfig{
Enabled: enabled,
Expand Down
16 changes: 6 additions & 10 deletions kernel_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -566,21 +566,17 @@ func TestBuildKernelConfig(t *testing.T) {
}
})

t.Run("kernel telemetry config defaults enabled and omits unset batch size", func(t *testing.T) {
t.Run("kernel telemetry config is nil when enableTelemetry is unset", func(t *testing.T) {
c := baseKernelConfig()
c.TelemetryBatchSize = 17
c.TelemetryFlushInterval = 9 * time.Second
kc := buildKernelConfig(c, kernel.Auth{Mode: kernel.AuthPAT, Token: "dapi-x"})
if kc.Telemetry == nil {
t.Fatal("Telemetry not forwarded")
}
if !kc.Telemetry.Enabled {
t.Error("Telemetry.Enabled = false, want true when enableTelemetry is unset")
}
if kc.Telemetry.BatchSize != 0 {
t.Errorf("Telemetry.BatchSize = %d, want 0 when telemetry_batch_size is unset", kc.Telemetry.BatchSize)
if kc.Telemetry != nil {
t.Fatalf("Telemetry = %+v, want nil when enableTelemetry is unset", kc.Telemetry)
}
})

t.Run("kernel telemetry config follows explicit enableTelemetry and batch size", func(t *testing.T) {
t.Run("kernel telemetry config follows explicit enableTelemetry and tuning", func(t *testing.T) {
c := baseKernelConfig()
c.EnableTelemetry = config.NewConfigValue(false)
c.TelemetryBatchSize = 17
Expand Down
Loading