AyCode.Core/AyCode.Services/docs/SIGNALR_ISSUES.md

97 lines
4.9 KiB
Markdown

# 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:
```csharp
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.