4.9 KiB
SignalR — Known Issues & Limitations
Protocol
PROTO-1: Server-side IsRawBytesData pre-serialize
Status: Planned removal
Affects: AcWebSignalRHubBase.SendMessageToClient
The server forwards the client's IsRawBytesData flag in the response SignalParams. This causes the protocol to return raw byte[] instead of deserializing. The original design pre-serialized on the server side, but with the zero-copy typed deserialization path (SignalDataType), this is redundant.
Plan: Remove IsRawBytesData forwarding from server response path. The client should use SignalDataType for typed deserialization and explicit byte[] type for raw data.
PROTO-2: Parameter serialization is per-parameter
Status: Known performance concern
Affects: SignalParams.SetParameterValues / GetParameterValues
Each parameter is individually serialized via ToBinary() / BinaryTo(Type) — N context pool acquire/release cycles. For many small primitives (int, bool, string) the per-call overhead may exceed a single bulk serialization.
Possible optimization: Batch fast-path — single serialization context for all parameters. Benchmark first.
PROTO-3: Parameter serialization is AcBinary only
Status: Limitation
Affects: SignalParams.SetParameterValues / GetParameterValues
Uses ToBinary() / BinaryTo() exclusively. JSON parameter support would require dispatching on DataSerializerType + AcJsonSerializer reference. Low priority — binary is the primary transport.
Transport
TRANS-1: BufferWriterChunkSize defaults to 64KB for SignalR
Status: DONE
Affects: AcBinaryHubProtocol constructor, write path
BufferWriterChunkSize = 4096 set in AcBinaryHubProtocol constructor. Aligns with Kestrel slab size, reduces latency-to-first-byte. Non-SignalR paths keep 64KB default.
TRANS-2: WebSocket buffer sizes are hardcoded
Status: Acceptable
Affects: AcSignalRClientBase connection setup
Transport max message size (30MB) and application buffer (30MB) are hardcoded. Sufficient for current payloads but not configurable per-deployment.
DataSource
DS-1: GetAll returns raw byte[] for populate/merge
Status: By design
Affects: AcSignalRDataSource.LoadDataSourceAsync
The GetAll path uses IsRawBytesData = true to receive raw byte[] from the protocol, then deserializes into the existing list via PopulateMerge. This avoids allocating a temporary List<T> for merge. The extra copy (pipe → byte[]) is the trade-off.
Possible optimization: Direct typed deserialization with merge support in the deserializer (PopulateMerge from ReadOnlySequence<byte>). Requires deserializer API changes.
Client-side Setup & DI
CONN-1: HubConnectionBuilder inner DI isolation
Status: Workaround-in-place (dedicated options-passing overload)
Affects: Consumer client setup in Program.cs (MAUI, WASM, ASP.NET Core server prerender)
HubConnectionBuilder.Services is a separate IServiceCollection from the outer host DI. services.Configure<AcBinaryHubProtocolOptions>(...) registered in the outer container does NOT flow into HubConnectionBuilder.Services. Calling hubBuilder.AddAcBinaryProtocol() with no args silently falls back to default options.
Known workaround: Use the overload AddAcBinaryProtocol(IHubConnectionBuilder, AcBinaryHubProtocolOptions, Action<...>? = null) that accepts pre-resolved options explicitly. Canonical consumer pattern:
var protocolOpts = sp.GetRequiredService<IOptions<AcBinaryHubProtocolOptions>>().Value;
hubBuilder.AddAcBinaryProtocol(protocolOpts);
Dispatch
DISPATCH-1: First-call null response (observed)
Status: Open — not diagnosed
Affects: PostDataAsync<T> awaiter / OnReceiveMessage → pending-request correlation
Observed symptom: first GetProductDtos_80-style call returns null despite server serializing and sending a valid ~80KB chunked response. Second call (client-side retry) works normally.
Log timeline:
- Server:
Serialize end (chunked) dataBytes=80571 chunkCount=20 - Client:
Deserialize end (chunked)— successful - Client:
OnReceiveMessage ... requestId=5 - Client: ~410ms later —
Client received null response. ... requestId=5 - Client: auto-retry (requestId=6) → full 338 items
Hypothesis (unverified): PostDataAsync<T> awaiter's null-mapping path misroutes the parsed result, or requestId → Task<T> correlation has a race on the first response of a fresh connection. Client auto-retry hides the user-visible impact.
Related TODO: SIGNALR_TODO.md#todo-01
Cross-cutting (also tracked in serializer-side docs)
XCUT-1: JSON-in-Binary request parameters — cross-ref
Same tech debt as ../../AyCode.Core/docs/BINARY_ISSUES.md#xcut-1. Planned replacement: migrate client→server request parameters from JSON-in-Binary envelope to direct Binary serialization. Coordinated change across all consuming projects.