Expand description
A shard runtime: render once per shard, share the changes, not the views.
§The model in one page
The pipeline this crate replaces rendered the whole world as seen by one connection, once per connection. Everything else followed mechanically: N connections each holding an O(N) view means materializing every view costs Θ(N²), and no scheduler or cache fixes that, because it is in the type.
Here, a shard renders once. Each fact is held once, whoever ends up seeing it. What connections receive is the resulting delta, filtered:
business state changes
| handle.wake()
v
render -> plan -> journal -> per-connection: filter, splice, collapseA three-operation delta filtered N times costs O(N·|D|), not O(N·W). That property holds even when every view is different, which is what makes it robust rather than merely fast.
§Three independent mechanisms
Wanting to unify them is the mistake that breeds special cases.
| mechanism | shape | what it solves | cost |
|---|---|---|---|
| shared view + scopes | a scope tree, one scope per element, a ScopeSet per observer | parties, teams, spectators, staff - the 95% | O(W) once + O(|D|) per connection |
| private overlay | a few elements visible to one connection | vanish, private channel, per-observer placement | O(|overlay|) |
| audio relation | a directed relation per receiver | all of the audio | O(N + edges) |
The rule that says which to use: a scope describes a group, an overlay describes an individual exception. A scope with one observer is an overlay in disguise. A role - player, host, spectator, staff - is a group by nature even when it has one member.
§The only coupling, and it is not ours
A receiver must see the sender.
The Mumble client discards audio whose sender session it does not know, so this is a protocol constraint rather than a design choice, and it is checked on the render’s output. A corollary worth stating: there is no separate “right to speak”. Speaking somewhere implies being visible there.
REF: docs/design/guide-implementation.md
REF: references/mumble/src/mumble/ServerHandler.cpp : handleVoicePacket
Re-exports§
pub use build::BuildError;pub use build::ChannelRef;pub use build::MAX_ACTIONS;pub use build::Narrow;pub use build::PrivateBuilder;pub use build::Rendered;pub use build::ShardBuilder;pub use build::UserRef;pub use compose::collapse;pub use compose::filter;pub use compose::splice;pub use emit::TextTarget;pub use emit::action_key;pub use emit::action_name;pub use emit::actions;pub use emit::denied_permission;pub use emit::emit;pub use emit::perm;pub use emit::permission_query;pub use emit::permissions_of;pub use emit::relayed;pub use emit::spoken;pub use emit::user_stats;pub use ids::ActionKey;pub use ids::ChannelId;pub use ids::ChannelKey;pub use ids::ConnectionId;pub use ids::Exhausted;pub use ids::IdAllocator;pub use ids::Occupant;pub use ids::SessionId;pub use ids::ShardId;pub use ids::SyntheticId;pub use journal::Journal;pub use journal::TooFarBehind;pub use plan::ChannelPatch;pub use plan::ElementId;pub use plan::OverlayOps;pub use plan::PlanOp;pub use plan::PlannedOp;pub use plan::UserPatch;pub use plan::plan;pub use plan::plan_elements;pub use queue::MAX_DEPTH_FOR_VOICE;pub use queue::OutboundQueue;pub use queue::Refused;pub use queue::VoiceAdmission;pub use reply::Audience;pub use reply::Effect;pub use reply::Effects;pub use reply::Reply;pub use reply::Spoken;pub use reply::Word;pub use routing::AudioRelation;pub use routing::AudioRouting;pub use routing::DomainId;pub use routing::Silence;pub use routing::compile;pub use scope::MAX_DEPTH;pub use scope::MAX_OBSERVED;pub use scope::Scope;pub use scope::ScopeSet;pub use scope::TooManyScopes;pub use shard::ActionTarget;pub use shard::AttachedConnection;pub use shard::Handover;pub use shard::MIN_INTERVAL;pub use shard::ReconcileReport;pub use shard::Shard;pub use shard::ShardCommand;pub use shard::ShardHandle;pub use shard::ShardLogic;pub use shard::VoiceEvent;pub use shard::run;pub use shard::spawn_parts;pub use view::Action;pub use view::Actions;pub use view::Channel;pub use view::On;pub use view::Overlay;pub use view::ShardView;pub use view::User;pub use view::UserFlags;
Modules§
- build
- The render builder: the only way a flavor produces a view.
- compose
- Composing one connection’s transition out of the shared delta.
- emit
- Translating a composed transition into Mumble control messages.
- ids
- Identifiers, and the allocator that keeps them stable.
- journal
- The shard’s delta journal.
- plan
- Planning a transition, with the scope as part of the element’s identity.
- queue
- One connection’s bounded output queue.
- reply
- What a flavor writes back while it observes an event.
- routing
- The audio relation, and the routing table compiled from it.
- scope
- Scopes: the shared-view visibility mechanism.
- shard
- The shard: one render, one journal, one task.
- view
- The rendered shard view, and the private overlays layered on top of it.