Skip to content

Add DASH_SECRET_KEY and DASH_SHARED_STORAGE platform hooks - #4026

Draft
T4rk1n wants to merge 2 commits into
devfrom
feat/platform-streaming-env
Draft

T4rk1n wants to merge 2 commits into
devfrom
feat/platform-streaming-env

Conversation

@T4rk1n

@T4rk1n T4rk1n commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

Streaming callbacks (#3931) and shared storage (#3930) break once an app runs on several workers or pods, and a hosting platform (Plotly Cloud, Dash Enterprise, self-hosted) can't fix that without editing app code. This adds two env vars, following the existing DASH_* convention, and a warning for the failure that is silent today.

Changes

  • DASH_SECRET_KEY: the signing secret is now looked up as server.secret_key > DASH_SECRET_KEY > secret saved in the background-callback store > random per process. Used for Dash's own signing only and never copied onto server.secret_key, so Flask sessions are untouched. An empty value counts as unset.
    • Without it, on 4.5.0rc0 with gunicorn -w 4 and no secret_key, most downlink polls return 403 (25 of 40 measured).
  • DASH_SHARED_STORAGE: picks the backend when the app doesn't pass shared_storage=: local, none, diskcache:///abs/path, or a redis:// / rediss:// URL.
    • shared_storage now has a sentinel default, so an explicit argument, None included, always wins.
    • The value becomes a factory. Nothing is built or connected until app.shared_storage is first read.
    • A missing dash[redis] / dash[diskcache] raises the same ImportError as the explicit path, at Dash() construction.
    • cluster:// is reserved and raises "not supported in this version". Anything else raises InvalidConfig naming the variable and value, with URL credentials replaced by ***.
  • Warning: when a stream request (uplink, downlink, cancel; all three backends) sends a token that fails verification, log a warning once per process pointing at server.secret_key / DASH_SECRET_KEY. Still 403. A request with no token is not logged.

No behavior change for apps that set neither variable.

Tests

  • Unit: secret precedence at all four levels, every storage scheme, garbage values, explicit argument beats env, matching ImportError text, credential redaction, warning fires once.
  • tests/streaming/test_stream_wsgi.py: gunicorn -w 4.
    • With DASH_SECRET_KEY: every poll is 200 and a stream completes in the browser. Fails before this change.
    • Without it: polls on the worker that served the page are 200, all others 403. Keeps the regression visible.
    • The test app tags responses with the worker pid, and polls go out concurrently until at least two workers have answered. Sent one by one, they tend to all land on one idle worker, and the test would prove nothing.
  • dash_duo test selecting Redis through DASH_SHARED_STORAGE. Skips without Redis, like the other Redis tests.

A hosting platform can now give every worker and pod the same signing
secret (DASH_SECRET_KEY) and pick the shared-storage backend
(DASH_SHARED_STORAGE) without editing the app. Without a shared secret,
multi-worker stream requests 403 silently; the first failure in each
process now logs a warning.
@sonarqubecloud

Copy link
Copy Markdown

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Dash performance benchmarks

✅ all within thresholds

scenario metric p90 (ms) median growth baseline p90 note
✅ callback_chain chain_ms 447.8 436.6 0.95x 499.1
✅ callback_chain graph_ms 1.5 1.5 1.0x 2.4
✅ callback_fanout fanout_ms 86.1 77.9 0.85x 92.5
✅ deep_nesting render_ms 54.3 50.8 1.0x 56.8
✅ full_children_replace replace_ms 5509.6 1966.9 19.04x 4697.1
✅ initial_render_large render_ms 591.1 560.4 0.99x 694.4
✅ initial_render_small render_ms 105.2 92.9 0.95x 104.0
✅ patch_append_nested append_ms 132.4 93.0 2.32x 192.3
✅ patch_append_toplevel append_ms 120.7 84.6 2.17x 140.2
✅ patch_scalar_update_large update_ms 161.0 140.1 0.93x 202.9
✅ wildcard_all_resolve wildcard_ms 270.0 263.6 0.97x 313.6
✅ wildcard_all_resolve graph_ms 1.1 1.1 1.0x 1.3

growth = late-third / early-third per-op time; ~1 is flat, a large value means the per-op cost scales with accumulated state.

machine scale vs baseline: 0.97x - divided out of the baseline ratios so they compare like for like (the absolute warn/fail ceilings are left un-scaled); calibrated on initial_render_small.

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.

1 participant