You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Goal: keep the ~4-round-trip refresh latency off the hot path during an actively-querying
session by refreshing both tokens in the background ahead of the CP token's ~5-min expiry. This is
a pure latency optimization — correctness already lives in child 3's on-request refresh — so it
lands after a functional PoC, not as a table-stakes hurdle before one.
Opt-in, never default. Child 3's synchronous on-request refresh is already functional, so the
daemon (and the whole park/refcount lifecycle below) must stay dormant unless a caller explicitly
asks for it. A default connect(auth="oauth") stays byte-for-byte child-3 behavior: no thread, no
park lifecycle, synchronous on-request refresh. The complexity here switches on only for the opt-in
caller.
"on_demand" = child-3 behavior; "background" = run this daemon. Plus an exported preset naming
the intent, e.g. OAUTH_LOW_LATENCY = OAuthPolicy(on_reauthentication_required="auto", token_refresh_mode="background"), alongside [refactor] Fold OAuth lifecycle knobs into an OAuthPolicy struct + preset globals #198's OAUTH_INTERACTIVE / OAUTH_UNATTENDED.
A fourth cell this shape allows but names no preset for -- OAuthPolicy(on_reauthentication_required="raise", token_refresh_mode="background") -- is real, not degenerate: a long-running unattended job that
wants the daemon's latency benefit while the session is alive, but wants the eventual 8h-wall
death to surface as an immediate exception on the next query rather than a doomed browser-login
attempt that blocks for a full login timeout before failing anyway. Left unnamed deliberately;
construct it directly if needed.
Cross-connection disagreement is a hard InterfaceError — and the guard already exists.[refactor] Fold OAuth lifecycle knobs into an OAuthPolicy struct + preset globals #198
makes the holder's one-identity guard compare the whole frozen OAuthPolicy ("one process = one
OAuth identity and one policy"): a second connection whose explicitoauth_policy differs
from the established login is refused, exactly as for environment and org; an omitted (None)
policy inherits and never conflicts. So this child inherits that behavior for free the moment token_refresh_mode becomes a field — no new guard clause, no per-field sentinel. Multi-connection
is only expected via a factory minting identical connections, so the guard is a misconfig
backstop that essentially never fires in practice.
thread the resolved policy.token_refresh_mode == "background" through oauth.acquire() (as a background_refresh: bool); the holder starts the daemon on the shared provider when requested. OAuthProvider gains an idempotent start_background_refresh() + stop, and close()/shutdown()
stop the daemon.
a background daemon thread that sleeps on a threading.Event until cp_expires_at − window
(the CP token is the shorter clock), wakes, and runs _refresh() through the same single-flight
gate as the on-request path; the Event lets close() interrupt it promptly.
the holder-side lifecycle the daemon necessitates: refcount of open OAuth Connections, park-don't-evict at refcount 0 (stop the daemon, keep the tokens — reversible), a short
cancel-on-acquire linger to absorb dbt churn, Connection.close() → holder.release()
gaining teeth, and daemon=True as the never-hang backstop. Because the daemon is opt-in, at
refcount 0 with no connection having requested background there is simply nothing to park — the
holder falls back to child-4's keep-alive-until-shutdown_all(), so this lifecycle only engages
for the opt-in path.
Out of scope: the on-request refresh (child 3, still the correctness floor — the daemon only
pre-empts its latency); the OAuthPolicy struct, presets, and whole-policy guard themselves ([refactor] Fold OAuth lifecycle knobs into an OAuthPolicy struct + preset globals #198,
which this child only extends by one field); proactive re-login ahead of the 8h wall (child 8, which
rides this daemon).
Prior art:ide-sidecar — RefreshCCloudTokensBean (@Scheduled refreshTokens() every 60s, ConcurrentExecution.SKIP) with the proactive decision shouldAttemptTokenRefresh() (refresh iff
a token expires before the next tick). mcp-confluent — AuthContext.startRefreshLoop() / scheduleNextRefresh() (setTimeout at controlPlaneExpiresAt − window). Neither borrows the
refcount / park-don't-evict / linger lifecycle, nor an opt-in toggle — both simply refresh for
the whole process / connection lifetime; that lifecycle, and keeping it opt-in, is our own, driven
by dbt's open-close-reopen churn and by child 3 already being a sufficient correctness floor.
session by refreshing both tokens in the background ahead of the CP token's ~5-min expiry. This is
a pure latency optimization — correctness already lives in child 3's on-request refresh — so it
lands after a functional PoC, not as a table-stakes hurdle before one.
daemon (and the whole park/refcount lifecycle below) must stay dormant unless a caller explicitly
asks for it. A default
connect(auth="oauth")stays byte-for-byte child-3 behavior: no thread, nopark lifecycle, synchronous on-request refresh. The complexity here switches on only for the opt-in
caller.
Activation is a new field on the
OAuthPolicystruct introduced in [refactor] Fold OAuth lifecycle knobs into anOAuthPolicystruct + preset globals #198, not a newconnect()kwarg and not a newauth=value. [refactor] Fold OAuth lifecycle knobs into anOAuthPolicystruct + preset globals #198 replaces Graceful re-authentication: in-place recovery +reauth=policy #156's flatreauthscalar with afrozen
OAuthPolicydataclass (passed asconnect(oauth_policy=...)) precisely so this childadds a field, not another look-alike
oauth_*parameter. [refactor] Fold OAuth lifecycle knobs into anOAuthPolicystruct + preset globals #198 named its fieldon_reauthentication_required(rather than a barereauth) specifically so arefresh-shapedfield here wouldn't read as a near-duplicate of it; this child follows the same instinct and
spells its own field
token_refresh_moderather than a barerefresh. This child adds:"on_demand"= child-3 behavior;"background"= run this daemon. Plus an exported preset namingthe intent, e.g.
OAUTH_LOW_LATENCY = OAuthPolicy(on_reauthentication_required="auto", token_refresh_mode="background"), alongside [refactor] Fold OAuth lifecycle knobs into anOAuthPolicystruct + preset globals #198'sOAUTH_INTERACTIVE/OAUTH_UNATTENDED.A fourth cell this shape allows but names no preset for --
OAuthPolicy(on_reauthentication_required="raise", token_refresh_mode="background")-- is real, not degenerate: a long-running unattended job thatwants the daemon's latency benefit while the session is alive, but wants the eventual 8h-wall
death to surface as an immediate exception on the next query rather than a doomed browser-login
attempt that blocks for a full login timeout before failing anyway. Left unnamed deliberately;
construct it directly if needed.
Cross-connection disagreement is a hard
InterfaceError— and the guard already exists. [refactor] Fold OAuth lifecycle knobs into anOAuthPolicystruct + preset globals #198makes the holder's one-identity guard compare the whole frozen
OAuthPolicy("one process = oneOAuth identity and one policy"): a second connection whose explicit
oauth_policydiffersfrom the established login is refused, exactly as for environment and org; an omitted (
None)policy inherits and never conflicts. So this child inherits that behavior for free the moment
token_refresh_modebecomes a field — no new guard clause, no per-field sentinel. Multi-connectionis only expected via a factory minting identical connections, so the guard is a misconfig
backstop that essentially never fires in practice.
token_refresh_modefield onOAuthPolicy+ itsOAUTH_LOW_LATENCYpreset (per above);validation is already handled by [refactor] Fold OAuth lifecycle knobs into an
OAuthPolicystruct + preset globals #198'soauth_policyguard (rejecting a non-Nonepolicy whenauth != "oauth"), so no new_resolve_oauth_configclause is needed here.policy.token_refresh_mode == "background"throughoauth.acquire()(as abackground_refresh: bool); the holder starts the daemon on the shared provider when requested.OAuthProvidergains an idempotentstart_background_refresh()+ stop, andclose()/shutdown()stop the daemon.
threading.Eventuntilcp_expires_at − window(the CP token is the shorter clock), wakes, and runs
_refresh()through the same single-flightgate as the on-request path; the
Eventletsclose()interrupt it promptly.park-don't-evict at refcount 0 (stop the daemon, keep the tokens — reversible), a short
cancel-on-acquire linger to absorb dbt churn,
Connection.close()→holder.release()gaining teeth, and
daemon=Trueas the never-hang backstop. Because the daemon is opt-in, atrefcount 0 with no connection having requested
backgroundthere is simply nothing to park — theholder falls back to child-4's keep-alive-until-
shutdown_all(), so this lifecycle only engagesfor the opt-in path.
pre-empts its latency); the
OAuthPolicystruct, presets, and whole-policy guard themselves ([refactor] Fold OAuth lifecycle knobs into anOAuthPolicystruct + preset globals #198,which this child only extends by one field); proactive re-login ahead of the 8h wall (child 8, which
rides this daemon).
OAuthPolicystruct + preset globals #198 for the frozenOAuthPolicystruct itself (one field,on_reauthentication_required, no holder/guard changes) -- this child is what adds the secondfield and the whole-policy cross-connection guard on top of it; neither exists yet as of [refactor] Fold OAuth lifecycle knobs into an
OAuthPolicystruct + preset globals #198.Through [refactor] Fold OAuth lifecycle knobs into an
OAuthPolicystruct + preset globals #198, also childrenCCloudOAuthprovider: login + on-request refresh + two auth views #153, Process-wide holder: one OAuth identity per process #154.RefreshCCloudTokensBean(@Scheduled refreshTokens()every 60s,ConcurrentExecution.SKIP) with the proactive decisionshouldAttemptTokenRefresh()(refresh iffa token expires before the next tick). mcp-confluent —
AuthContext.startRefreshLoop()/scheduleNextRefresh()(setTimeoutatcontrolPlaneExpiresAt − window). Neither borrows therefcount / park-don't-evict / linger lifecycle, nor an opt-in toggle — both simply refresh for
the whole process / connection lifetime; that lifecycle, and keeping it opt-in, is our own, driven
by dbt's open-close-reopen churn and by child 3 already being a sufficient correctness floor.
OAuthPolicyfield & preset +concurrency tests, layered on an already-shipped provider/holder and [refactor] Fold OAuth lifecycle knobs into an
OAuthPolicystruct + preset globals #198's already-guarded policysurface.