villebro opened a new pull request, #43689:
URL: https://github.com/apache/superset/pull/43689
### SUMMARY
Delivers per-tab realtime `task-status` fanout for async chart-data, against
the
umbrella branch (#43407). Builds on the per-tab subscription refcount from
#43685
(`private.task.consumers`).
**Problem.** Realtime `task-status` is delivered at principal grain:
`publish_task_status` names the task's subscriber principals and the
`superset-websocket` server fans each out to `realtime:user:<id>`, so
**every tab
a user has open receives status for every task any of their tabs is
watching**,
and the browser discards the irrelevant ones by `task_id`. Now that each
tab's
subscription is tracked per-tab, we can route `task-status` to only the tab
that
is watching a given task.
This realizes the three channel tiers:
1. **tab-specific** → `realtime:user:<id>:<tabId>` (new: per-tab
`task-status`)
2. **user-specific** → `realtime:user:<id>` (all of a user's tabs — kept for
non-per-tab tasks and future per-user notices)
3. **all users+tabs** → the existing `entity-changes:*` broadcast tier
### DESIGN
- **Auth unchanged: one user-specific cookie, one ws connection per tab.**
The
cookie authorizes the principal (`channel: user:<id>`, identity-bound — the
token integrity check is kept); each tab opens its own `WebSocket` and
advertises its `getTabId()` on the **connect URL** (`?tab_id=…`), *not* in
the
token. The server derives the per-tab channel by prefixing the authorized
principal channel, so a client-supplied `tab_id` can never address another
principal's sockets. No per-tab cookies, no bearer token.
- **Socket dual-registration.** On connect the socket is registered under
`user:<id>` (principal — all-tabs/broadcast delivery + the per-principal
connection cap) and, when a `tab_id` is present, also `user:<id>:<tabId>`.
One
socket id, two channel keys.
- **Fanout routes by channel-key strings.** The `task-status` wire payload
changes from `subscribers: [{principal_type, sub}]` to `channels: [str]` —
the
producer computes the exact routing keys and the ws server just delivers to
`realtime:<key>` (this also simplifies the server: no
re-derivation/validation
of principal keys in the fanout path).
- **Routing keys come from the task type's subscription policy.** New
`TaskSubscriptionPolicy.routing_channels(task) -> list[str] | None`
(concrete
default `None`). Chart-data returns its `consumers` list (already the
per-tab
keys); any task type without a policy — or a chart task with no recorded
tab —
falls back to principal-grain keys from `get_subscriber_principals`
(**unchanged
behavior for non-chart tasks**).
- **Broadcast dedupe fix.** `broadcastToAll` now iterates the unique socket
registry (via an extracted `sendToSocket` helper) instead of per-channel,
so a
dual-registered socket receives each entity-change nudge exactly once.
### BEFORE/AFTER
- **Before:** two tabs of one user watching different charts each receive
every
chart's `task-status`; the client filters by `task_id`.
- **After:** each tab receives realtime `task-status` only for its own
tasks, on
`realtime:user:<id>:<tabId>`. Entity-change list nudges still reach every
tab.
### TESTING INSTRUCTIONS
- `pytest tests/unit_tests/tasks/test_manager.py
tests/unit_tests/tasks/test_async_queries.py`
- `npm --prefix superset-websocket run test && npm --prefix
superset-websocket run lint`
- `npm run test -- realtime`
- Manual (worker + Redis coordination + ws server): open the same chart in
two
tabs; confirm each tab's `task-status` arrives only on its own
`realtime:user:<id>:<tabId>` channel (inspect ws frames) while
entity-change
list nudges reach both tabs exactly once; stop the ws server and confirm
polling still resolves charts.
### ADDITIONAL INFORMATION
Wire-contract change (`subscribers` → `channels`) is a coordinated
Python↔Node
change with no version field, so deploy the Flask app and the
`superset-websocket`
server together. During a mismatched rolling upgrade each side drops the
other's
`task-status` payload and degrades to client polling (no crash, no leak).
- [ ] Has associated issue:
- [x] Required feature flags: `GLOBAL_ASYNC_QUERIES` /
`GLOBAL_TASK_FRAMEWORK`; optional `WEBSOCKET_ENABLE`
- [ ] Changes UI
- [ ] Includes DB Migration
- [x] Introduces new feature or API
- [ ] Removes existing feature or API
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]