pub struct WsHub { /* private fields */ }Expand description
High-performance WebSocket broadcast hub with sharded client storage.
Supports 10k+ concurrent connections with bounded thread count. Uses NUM_SHARDS (16) shards to reduce lock contention.
Architecture:
- Client connections are assigned to shards via round-robin (id % NUM_SHARDS).
- Each shard has a dedicated broadcast worker thread that consumes from a channel.
- Broadcast calls are non-blocking for the caller: they push to each shard’s channel and return immediately.
- Read-side threads use 64KB stacks (vs 2-8MB default) to keep memory bounded.
- Total thread count: NUM_SHARDS broadcast workers + 1 per connected client (with minimal stack), plus the accept thread.
Implementations§
Source§impl WsHub
impl WsHub
pub fn new() -> Arc<Self> ⓘ
Sourcepub fn subscriptions(&self) -> &Arc<CrdtSubscriptions> ⓘ
pub fn subscriptions(&self) -> &Arc<CrdtSubscriptions> ⓘ
Access the per-client CRDT subscription registry. The notifier
looks up subscribers via subscriptions().subscribers(entity, row)
and feeds them to broadcast_binary_to.
Sourcepub fn broadcast(&self, event: &ChangeEvent)
pub fn broadcast(&self, event: &ChangeEvent)
Broadcast a change event to ALL connected clients across all shards. Non-blocking: pushes to each shard’s channel and returns immediately.
Serializes the event JSON exactly once into an Arc<str> and
shares it across the 16 shard senders. Each shard’s worker
thread receives the same Arc and pays only a refcount bump.
Sourcepub fn broadcast_presence(&self, msg: &str)
pub fn broadcast_presence(&self, msg: &str)
Broadcast a raw string message to all clients (used for presence updates).
Sourcepub fn broadcast_binary(&self, bytes: Vec<u8>)
pub fn broadcast_binary(&self, bytes: Vec<u8>)
Broadcast a binary frame to every connected client across all
shards. Used for CRDT updates (see pylon_router::encode_crdt_frame
for the wire shape). The bytes are wrapped in an Arc so each
shard’s per-client fanout shares one allocation; the per-send
to_vec() cost is the tungstenite 0.24 contract.
Synchronous fanout — iterates shards directly rather than going
through the per-shard mpsc workers. CRDT writes happen at most
once per logical mutation so the throughput shape is “occasional
burst” not “every keystroke”, and direct fanout avoids growing a
second per-shard channel (Arc<u8> can’t share the Arc
Sourcepub fn broadcast_binary_to(&self, client_ids: &[u64], bytes: Vec<u8>)
pub fn broadcast_binary_to(&self, client_ids: &[u64], bytes: Vec<u8>)
Send a binary frame to a specific subset of client IDs only.
Used by the CRDT broadcast path to fan out only to clients
subscribed to the row that just changed (instead of every
connected client). Routes each id to its owning shard via
id % NUM_SHARDS.
client_ids typically comes from CrdtSubscriptions::subscribers.
An empty list is a no-op — the row had no subscribers, so the
CRDT write is durable on the server but no client sees the
binary frame (they’ll learn about the change via the JSON
change-event broadcast which always fires).
Sourcepub fn send_binary_to_one(&self, client_id: u64, bytes: Vec<u8>)
pub fn send_binary_to_one(&self, client_id: u64, bytes: Vec<u8>)
Send a binary frame to a single client by id. Used by the subscribe path: when a client subscribes to a row, the server immediately ships the current snapshot so the new subscriber has the up-to-date state without waiting for the next write.
Sourcepub fn client_count(&self) -> usize
pub fn client_count(&self) -> usize
Total number of connected clients across all shards.
Auto Trait Implementations§
impl !Freeze for WsHub
impl RefUnwindSafe for WsHub
impl Send for WsHub
impl Sync for WsHub
impl Unpin for WsHub
impl UnsafeUnpin for WsHub
impl UnwindSafe for WsHub
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Paint for Twhere
T: ?Sized,
impl<T> Paint for Twhere
T: ?Sized,
Source§fn fg(&self, value: Color) -> Painted<&T>
fn fg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the foreground set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like red() and
green(), which have the same functionality but are
pithier.
§Example
Set foreground color to white using fg():
use yansi::{Paint, Color};
painted.fg(Color::White);Set foreground color to white using white().
use yansi::Paint;
painted.white();Source§fn bright_black(&self) -> Painted<&T>
fn bright_black(&self) -> Painted<&T>
Source§fn bright_red(&self) -> Painted<&T>
fn bright_red(&self) -> Painted<&T>
Source§fn bright_green(&self) -> Painted<&T>
fn bright_green(&self) -> Painted<&T>
Source§fn bright_yellow(&self) -> Painted<&T>
fn bright_yellow(&self) -> Painted<&T>
Source§fn bright_blue(&self) -> Painted<&T>
fn bright_blue(&self) -> Painted<&T>
Source§fn bright_magenta(&self) -> Painted<&T>
fn bright_magenta(&self) -> Painted<&T>
Source§fn bright_cyan(&self) -> Painted<&T>
fn bright_cyan(&self) -> Painted<&T>
Source§fn bright_white(&self) -> Painted<&T>
fn bright_white(&self) -> Painted<&T>
Source§fn bg(&self, value: Color) -> Painted<&T>
fn bg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the background set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like on_red() and
on_green(), which have the same functionality but
are pithier.
§Example
Set background color to red using fg():
use yansi::{Paint, Color};
painted.bg(Color::Red);Set background color to red using on_red().
use yansi::Paint;
painted.on_red();Source§fn on_primary(&self) -> Painted<&T>
fn on_primary(&self) -> Painted<&T>
Source§fn on_magenta(&self) -> Painted<&T>
fn on_magenta(&self) -> Painted<&T>
Source§fn on_bright_black(&self) -> Painted<&T>
fn on_bright_black(&self) -> Painted<&T>
Source§fn on_bright_red(&self) -> Painted<&T>
fn on_bright_red(&self) -> Painted<&T>
Source§fn on_bright_green(&self) -> Painted<&T>
fn on_bright_green(&self) -> Painted<&T>
Source§fn on_bright_yellow(&self) -> Painted<&T>
fn on_bright_yellow(&self) -> Painted<&T>
Source§fn on_bright_blue(&self) -> Painted<&T>
fn on_bright_blue(&self) -> Painted<&T>
Source§fn on_bright_magenta(&self) -> Painted<&T>
fn on_bright_magenta(&self) -> Painted<&T>
Source§fn on_bright_cyan(&self) -> Painted<&T>
fn on_bright_cyan(&self) -> Painted<&T>
Source§fn on_bright_white(&self) -> Painted<&T>
fn on_bright_white(&self) -> Painted<&T>
Source§fn attr(&self, value: Attribute) -> Painted<&T>
fn attr(&self, value: Attribute) -> Painted<&T>
Enables the styling Attribute value.
This method should be used rarely. Instead, prefer to use
attribute-specific builder methods like bold() and
underline(), which have the same functionality
but are pithier.
§Example
Make text bold using attr():
use yansi::{Paint, Attribute};
painted.attr(Attribute::Bold);Make text bold using using bold().
use yansi::Paint;
painted.bold();Source§fn rapid_blink(&self) -> Painted<&T>
fn rapid_blink(&self) -> Painted<&T>
Source§fn quirk(&self, value: Quirk) -> Painted<&T>
fn quirk(&self, value: Quirk) -> Painted<&T>
Enables the yansi Quirk value.
This method should be used rarely. Instead, prefer to use quirk-specific
builder methods like mask() and
wrap(), which have the same functionality but are
pithier.
§Example
Enable wrapping using .quirk():
use yansi::{Paint, Quirk};
painted.quirk(Quirk::Wrap);Enable wrapping using wrap().
use yansi::Paint;
painted.wrap();Source§fn clear(&self) -> Painted<&T>
👎Deprecated since 1.0.1: renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
fn clear(&self) -> Painted<&T>
renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
Source§fn whenever(&self, value: Condition) -> Painted<&T>
fn whenever(&self, value: Condition) -> Painted<&T>
Conditionally enable styling based on whether the Condition value
applies. Replaces any previous condition.
See the crate level docs for more details.
§Example
Enable styling painted only when both stdout and stderr are TTYs:
use yansi::{Paint, Condition};
painted.red().on_yellow().whenever(Condition::STDOUTERR_ARE_TTY);