Skip to content

fix(db-snowflake): resolve timeoutMs 0 to the default, matching BigQuery - #3022

Merged
mtoy-googly-moogly merged 3 commits into
malloydata:mainfrom
girishjeswani:fix/db-snowflake-timeout-zero-default
Jul 31, 2026
Merged

fix(db-snowflake): resolve timeoutMs 0 to the default, matching BigQuery#3022
mtoy-googly-moogly merged 3 commits into
malloydata:mainfrom
girishjeswani:fix/db-snowflake-timeout-zero-default

Conversation

@girishjeswani

@girishjeswani girishjeswani commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary

The Snowflake connector treated a configured timeoutMs of 0 as "wait
forever", which is inconsistent with the BigQuery connector. In
#3010 BigQuery's 0 was aligned to fall back to the default;
this does the same for Snowflake, so a 0, a negative value, or a blank /
non-numeric value (parsed to NaN) now resolves to the 10-minute default.

Root cause

timeoutMs is resolved in the connection constructor with
options?.timeoutMs ?? TIMEOUT_MS, so ?? only defaults null/undefined and
keeps an explicit 0. That 0 then reaches the executor's
timeoutMs ? setTimeout(cancel, timeoutMs) : undefined, where a falsy value
schedules no timer, i.e. no client-side timeout. A blank or non-numeric
connection value is parsed to NaN by parseInt in index.ts, which is also
falsy and behaves the same way. A negative value, by contrast, is truthy, so it
reaches setTimeout(cancel, <negative>) and aborts the statement almost
immediately.

Change

  • Resolve to a positive number or the default: unset, blank, non-numeric, zero,
    and negative all fall back to the 10-minute default, and only a positive value
    overrides it. Uses Number(...) > 0 rather than a bare ||, because a
    negative value is truthy and would otherwise reach setTimeout(cancel, <negative>) and abort the statement at once. Matches the BigQuery connector,
    and applies to both timeoutMs and the sibling schemaSampleTimeoutMs.
  • No other behavior changes. The per-statement client-side cancel is unchanged:
    on timeout the executor already calls statement.cancel(), which cancels the
    running query server-side. The per-statement model (each statement in a batch
    bounded by timeoutMs) is retained.

Scope

Snowflake-only, and intentionally minimal. This is the consistency follow-up
requested on #3010; it does not add a server-side job timeout.
Snowflake's STATEMENT_TIMEOUT_IN_SECONDS (the server-side cap, analogous to
BigQuery's jobTimeoutMs) is left unset, as today. Shadowing timeoutMs to it
is a reasonable future addition if a per-job rather than per-statement bound is
wanted, but it is a larger change (seconds granularity, session-vs-statement
scoping) and is deliberately out of scope here.

Tests

There is no new unit test. The resolution is a two-line guard
(Number(...) > 0 ? ... : TIMEOUT_MS) whose comment documents intent; a test
for it could only go red if the test and the guard were edited together, so the
guard and its comment stand on their own. The existing live integration test
(cancels long-running batch on timeout, timeoutMs = 500) exercises the
client-side cancel and is unaffected, since a positive value still passes
through.

tsc and eslint pass.

Notes

  • Behavior change worth flagging: a timeoutMs of 0 previously disabled the
    client-side timeout (unbounded) and a negative value cancelled the statement
    almost immediately; both now use the 10-minute default. After this there is no
    value that means "no timeout"; that is intentional and matches the BigQuery
    connector.

Checklist

@mtoy-googly-moogly

Copy link
Copy Markdown
Collaborator

the test reaches into privates through a cast and asserts by argument position, so it breaks on refactors unrelated to timeouts while pinning a two-line guard the comment already explains.

just delete it

The Snowflake connector resolved timeoutMs with 'options?.timeoutMs ?? TIMEOUT_MS', so an explicit 0 was kept and then read by the executor's 'timeoutMs ? setTimeout(cancel, timeoutMs) : undefined' as no timer, i.e. wait forever. A blank or non-numeric value parses to NaN and behaved the same.

Resolve with '|| TIMEOUT_MS' so 0 and NaN fall back to the 10-minute default, matching the BigQuery change in malloydata#3010. Applied to timeoutMs and the sibling schemaSampleTimeoutMs. The per-statement client-side cancel is unchanged; on timeout the executor already cancels the running statement server-side.

Adds snowflake_connection.unit.spec.ts asserting the resolution via a stubbed executor (0, unset, and NaN fall back; a positive value passes through).

Signed-off-by: Girish Jeswani <girish@credibledata.com>
Extend the 0-to-default resolution to negatives: a bare || let a negative through (it is truthy), and the executor would then schedule setTimeout(cancel, <negative>) and abort the statement almost immediately. Resolve with Number(...) > 0 so zero and negative both default, for timeoutMs and the sibling schemaSampleTimeoutMs. Adds a negative-timeoutMs unit test.

Signed-off-by: Girish Jeswani <girish@credibledata.com>
Signed-off-by: Girish Jeswani <girish@credibledata.com>
@mtoy-googly-moogly
mtoy-googly-moogly force-pushed the fix/db-snowflake-timeout-zero-default branch from 4fbdca5 to 5873ede Compare July 31, 2026 12:59
@mtoy-googly-moogly
mtoy-googly-moogly merged commit d6f92b6 into malloydata:main Jul 31, 2026
16 checks passed
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.

2 participants