tdfs4ds.utils.reconnect¶
tdfs4ds.utils.reconnect
¶
Teradata reconnection helper.
Re-establishes the teradataml context after the connection is lost (idle
timeout, network blip, server restart). Credentials are read from the same
TDFS4DS_TD_* environment variables / .env used by the server bootstrap:
TDFS4DS_TD_HOST Teradata host (required)
TDFS4DS_TD_USERNAME Teradata user (required for user/password auth)
TDFS4DS_TD_PASSWORD Teradata password (required for user/password auth)
TDFS4DS_TD_LOGDATA token payload, e.g. 'token=<jwt>' (alternative to
username/password; implies LOGMECH=JWT by default)
TDFS4DS_TD_LOGMECH logon mechanism (optional)
TDFS4DS_TD_DATABASE default database (optional)
TDFS4DS_TD_ENCRYPT wire encryption — 'true' or 'false' (optional)
Used two ways
- manually —
tdfs4ds.reconnect_teradata(); - automatically —
execute_querydetects a dropped-connection error, calls :func:reconnect_teradataonce, and retries the query.
_CONNECTION_ERROR_MARKERS
module-attribute
¶
_CONNECTION_ERROR_MARKERS = ('socket', 'broken pipe', 'connection reset', 'econnreset', 'connection refused', 'econnrefused', 'connection aborted', 'connection closed', 'connection is closed', 'closed the connection', 'server closed', 'forcibly closed', 'not connected', 'no longer connected', 'failure sending request', 'failure receiving response', 'no more sessions', 'session has been aborted', 'connection timed out', 'connect timeout', 'sqlstate 08s01', 'sqlstate 08003')
_CREDENTIAL_REFUSAL_MARKERS
module-attribute
¶
_CREDENTIAL_REFUSAL_MARKERS = ('[error 544]', 'requires username and password', '[error 8017]', 'sqlstate 28000', 'invalid authorization')
_chained_text
¶
Every message along exc's __cause__ chain, lower-cased and joined.
teradataml wraps the driver error rather than replacing it, so the words that
identify the fault usually live in __cause__: TDML_2000 Failed to
connect to Teradata Vantage. says nothing on its own.
__context__ is deliberately NOT followed, which is the one difference from
:func:_describe_exception. __context__ is whatever exception happened to
be in flight when this one was raised — frequently unrelated, since the
package issues cleanup statements from inside except blocks — and letting
that text into a classification makes two independent failures contaminate
each other. A human reading a log wants everything; a decision wants only what
was explicitly chained with raise ... from.
Cycle-safe and bounded; never raises.
is_connection_error
¶
True when exc means the connection is gone — not that a statement failed.
The distinction matters because the only automatic caller acts on it by tearing
the session down (execute_query → :func:reconnect_teradata). A false
positive therefore destroys a healthy context, and with it every volatile
table and volatile ART the session holds, in response to an error that had
nothing to do with connectivity.
So the test is deliberately asymmetric: a server response anywhere in the chain proves the connection was alive and vetoes the classification before any positive marker is considered. A missed reconnect merely surfaces the real error to the caller, which is recoverable; a spurious one is not.
The cost of the veto is bounded and accepted: if the database answers a statement and then the session dies (a DBA abort, say), the next statement fails driver-side with a transport error and reconnects correctly — one extra surfaced error, no lost context.
encrypt_setting
¶
TDFS4DS_TD_ENCRYPT (or the Connect form's field) as a Python bool, or None.
The value must reach tdml.create_context as a bool: teradataml
upper-cases every string connection parameter (for TMODE), so
encryptdata='false' reached the driver as "FALSE", which teradatasql
20.0.69 refuses ("[Error 169] Unable to parse JSON connection parameters ...
trying to unmarshal "FALSE" into bool"); only a bool is lower-cased. The raw
driver wants the lower-case string instead (:func:_driver_kwargs).
None / '' = not set. Anything not a yes/no word raises ValueError
naming source.
_connect_kwargs
¶
Build create_context kwargs from TDFS4DS_TD_*; None if incomplete.
Two credential shapes are accepted, because the platforms differ:
- user/password —
HOST+USERNAME+PASSWORD, optionally withLOGMECH. The historical shape. - token —
HOST+LOGDATA(+LOGMECH, normally'JWT'), which is how a Teradata session is opened in an environment that authenticates its users with a platform token rather than a password:create_context(host=…, logmech='JWT', logdata='token=…'). There is no username or password to supply, so requiring them here made a JWT deployment inexpressible — a long-lived agent could open a context by hand but could never recover one, since every automatic reconnect funnels through this function.
LOGDATA is a credential (it carries the token), so it is redacted by
export_config like any other and never logged.
_driver_kwargs
¶
Map create_context kwargs onto raw teradatasql.connect kwargs.
teradatasql takes every parameter as a string: a bool becomes 'true'/'false'.
_credentials_probe
¶
Can these credentials open a session? (True | False | None, detail).
False only when the logon was refused — a malformed credential set, or
a rejected one. None means inconclusive (unreachable host, no driver),
which is the normal state of affairs during a real outage and must never be
read as a refusal.
Raw teradatasql, never tdml.create_context: teradataml holds ONE
process-wide context and assigns its global engine before testing it, so a
failed create_context is not a question — it is an answer that has already
broken the caller's session. Same constraint, and the same solution, as
:func:jwt_logon_verdict and the benchmark module.
This adds no failed logon attempt over the previous behaviour: on a refusal it
replaces the create_context that used to fail, rather than preceding it.
jwt_logon_verdict
¶
Does the database accept TDFS4DS_TD_LOGDATA as a JWT logon?
Returns (True | False | None, detail) — None when it could not be
established, which is not a failure.
This exists because a platform can hand out two tokens that look alike
and are not interchangeable: a session/SSO token, which authenticates
against the platform's own services, and one minted for a Vantage logon.
Only the second is a database credential. The first fails with the
database's own Error 8017 / SQLState 28000 — never an HTTP 401 —
everywhere it is used as one, including inside a gateway that opens a
session on the caller's behalf, which is why such a failure surfaces far
from the setting that caused it. Confirmed live on AI Studio.
Uses raw teradatasql, never tdml.create_context: teradataml
holds ONE process-wide context, so opening one here would replace the
caller's working connection as a side effect of a check. Same reason the
benchmark module talks to the driver directly.
The token is read from the named TDFS4DS_TD_LOGDATA. The platform's
unprefixed OAUTH_ACCESS_TOKEN is deliberately NOT read (v0.3.1.154): a
generic name can be owned by an unrelated component, and forwarding some
other component's credential to the database is exactly the mistake that
rule prevents.
_describe_exception
¶
Render exc with its chained causes, not just its first line.
TDML_2000 Failed to connect to Teradata Vantage. says nothing about
WHY, and teradataml wraps the driver error rather than replacing it - so
the actionable half lives in __cause__: a DNS miss, a refused
connection and a rejected logon are three different faults with three
different fixes, and they all render identically at the top level.
Logging only str(exc).splitlines()[0] discarded exactly that. A live
AgentOps pod failed here in 87 ms - far too fast for a TCP timeout, so
a name that did not resolve - and the log was indistinguishable from an
unreachable host or a bad password.
Cycle-safe and bounded; used only for logging, so it never raises.
reconnect_teradata
¶
Re-establish the teradataml context from TDFS4DS_TD_* credentials.
Returns True on success, False on failure (or raises when raise_error=True).
A no-op-safe call: if the credentials are not configured it logs a warning and
returns False.
It refuses to destroy a working context on the way to a broken one. The
credentials are probed with the raw driver first, and a refusal leaves the
existing context untouched. This is not hypothetical tidiness: create_context
calls remove_context internally and assigns teradataml's global engine
before testing it, so a failed reconnect used to leave get_context()
returning a poisoned engine — every later statement in the process then failed
with that logon error instead of its own, and the session could not self-heal.
That is how one nominal "table already exists" during connect() ended a
notebook's session.