Skip to content

Commit 8c104d6

Browse files
committed
readme
1 parent 3a62eb7 commit 8c104d6

File tree

1 file changed

+37
-32
lines changed

1 file changed

+37
-32
lines changed

README.md

Lines changed: 37 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -85,56 +85,61 @@ At the heart of Orleans.SignalR sits `OrleansHubLifetimeManager<THub>`. It repla
8585

8686
```mermaid
8787
flowchart LR
88-
hub[ASP.NET Core SignalR Hub]
89-
manager[OrleansHubLifetimeManager<T>]
88+
hub["ASP.NET Core SignalR Hub"]
89+
manager["OrleansHubLifetimeManager<T>"]
9090
subgraph Orleans
91-
grains[Orleans grain topology (coordinators and partitions)]
91+
grains["Grain topology: coordinators & partitions"]
9292
end
93-
clients[Connected clients]
93+
clients["Connected clients"]
9494
9595
hub --> manager --> grains --> clients
9696
```
9797

98-
### Connection Fan-Out Pipeline
98+
1. The ASP.NET Core hub writes to `OrleansHubLifetimeManager<T>` instead of the default SignalR manager.
99+
2. The lifetime manager resolves the Orleans grains that own connections, groups, users, and invocations.
100+
3. Grains fan messages back to clients by invoking the observers recorded for each connection.
99101

100-
1. **Connection observed** — when a client connects, the lifetime manager creates a hub subscription (`ISignalRObserver`).
101-
2. **Coordinator assignment**`SignalRConnectionCoordinatorGrain` maps the connection to a partition via consistent hashing.
102-
3. **Partition grain**`SignalRConnectionPartitionGrain` stores the observer key and relays messages to the client.
103-
4. **Dynamic scaling** — partition counts expand to powers of two when tracked connections exceed `ConnectionsPerPartitionHint`. When the load drops to zero, the count resets to the configured base.
102+
### Connection Fan-Out Pipeline
104103

105104
```mermaid
106105
flowchart TD
107-
connect([Client connect / disconnect])
108-
coordinator{SignalRConnectionCoordinator / consistent hashing}
109-
partitions[[SignalRConnectionPartition(s)]]
110-
observers[[Observer notifications]]
111-
clients[[Connected clients]]
112-
scaling[(Adjust partition count via hints)]
113-
114-
connect --> coordinator --> partitions --> observers --> clients
115-
coordinator -. dynamic scaling .-> scaling -.-> partitions
106+
connect["Client connect / disconnect"]
107+
coordinator["SignalRConnectionCoordinator | consistent hashing"]
108+
partition["SignalRConnectionPartition"]
109+
observers["Observer notifications"]
110+
consumers["Connected clients"]
111+
scaling["Dynamic scaling when hints exceeded"]
112+
113+
connect --> coordinator --> partition --> observers --> consumers
114+
coordinator -.-> scaling
116115
```
117116

118-
### Group Fan-Out Pipeline
117+
1. **Connection observed** — the lifetime manager registers an `ISignalRObserver` when a client connects.
118+
2. **Coordinator assignment**`SignalRConnectionCoordinatorGrain` hashes the connection ID to a partition number.
119+
3. **Partition grain**`SignalRConnectionPartitionGrain` stores the observer handle and relays messages such as `Clients.All`, `Clients.Client`, or `Clients.User`.
120+
4. **Dynamic scaling** — the coordinator grows the partition ring (powers of two) when `ConnectionsPerPartitionHint` is exceeded and shrinks it when load drops to zero.
119121

120-
1. **Group coordinator**`SignalRGroupCoordinatorGrain` tracks group names and membership counts.
121-
2. **Group partition assignment** — groups are consistently hashed to `SignalRGroupPartitionGrain` instances using the same power-of-two heuristic (`GroupPartitionCount` + `GroupsPerPartitionHint`).
122-
3. **Partition state** — each partition stores bidirectional maps of connections-to-groups and group-to-observer links, enabling efficient `SendToGroup`, `SendToGroups`, and exclusions.
123-
4. **Automatic cleanup** — when a group empties, the coordinator is notified so partitions can release unused entries and (if idle) shrink back to the base partition count.
122+
### Group Fan-Out Pipeline
124123

125124
```mermaid
126125
flowchart TD
127-
action([Group operation])
128-
groupCoord{SignalRGroupCoordinator / assign hash partition}
129-
groupPartition[[SignalRGroupPartition (stateful fan-out)]]
130-
membership[(Membership maps (connection ↔ group))]
131-
cleanup([Notify coordinator when empty])
132-
133-
action --> groupCoord --> groupPartition
134-
groupPartition --> membership --> groupPartition
135-
membership --> cleanup -.-> groupCoord
126+
action["Group operation"]
127+
coordinator["SignalRGroupCoordinator | hash to partition"]
128+
partition["SignalRGroupPartition | stateful fan-out"]
129+
membership["Membership map (connection ↔ group)"]
130+
observers["Observer notifications"]
131+
cleanup["Empty group cleanup"]
132+
133+
action --> coordinator --> partition --> observers
134+
partition --> membership --> partition
135+
membership --> cleanup -.-> coordinator
136136
```
137137

138+
1. **Group coordinator**`SignalRGroupCoordinatorGrain` maintains group membership counts.
139+
2. **Partition assignment** — group names are hashed to `SignalRGroupPartitionGrain` instances using the same power-of-two heuristic (`GroupPartitionCount` plus `GroupsPerPartitionHint`).
140+
3. **Partition state** — partitions keep connection↔group maps so `SendToGroup`, `SendToGroups`, and exclusion variants can enumerate the relevant observers quickly.
141+
4. **Automatic cleanup** — when a group becomes empty the coordinator notifies partitions so they can drop state and resize if necessary.
142+
138143
### Connection, Group, and User Grains
139144

140145
- `SignalRConnectionHolderGrain` and `SignalRGroupGrain` remain as non-partitioned fallbacks when partitioning is disabled (`ConnectionPartitionCount = 1` or `GroupPartitionCount = 1`).

0 commit comments

Comments
 (0)