# SignalR — Known Issues & Limitations ## Protocol ### SIG-I-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. ### SIG-I-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. ### SIG-I-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 ### SIG-I-4: 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. ### SIG-I-5: 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 ### SIG-I-6: 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` 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`). Requires deserializer API changes. ## Server-side Setup & DI ### SIG-I-7: Server-side NopCommerce plugin AcBinaryHubProtocolOptions not bound from appsettings **Status:** Open · **Severity:** Minor (works, but hardcoded + bypasses DI logger) · **Area:** Consumer adoption gap in `Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs` The framework overload chain is complete on the server side — `AcSignalRProtocolExtensions.BuildProtocol` already resolves `IOptions` from DI and falls back to defaults otherwise. The consumer plugin does NOT use it: Current `PluginNopStartup.ConfigureServices`: ```csharp .AddAcBinaryProtocol(opts => { opts.ProtocolMode = BinaryProtocolMode.AsyncSegment; // ← HARDCODED opts.Logger = new Logger(nameof(AyCodeBinaryHubProtocol)); // ← BYPASSES ILogger DI }); ``` What's missing: - No `services.Configure(configuration.GetSection("AyCode:SignalR:Protocol"))` → `ProtocolMode`, `BufferSize`, `WaitForFlush`, `FlushTimeout` are all hardcoded / default. - The `appsettings.json` has no `AyCode:SignalR` (or equivalent) section at all — so per-deploy tuning (e.g. increasing `FlushTimeout` for a satellite link, switching `ProtocolMode` for diagnostics) requires a code change + redeploy. - Manual `new Logger(...)` sidesteps the DI `ILogger` auto-resolution that `BuildProtocol` provides → creates a parallel logger instance (see `../../../AyCode.Core/docs/LOGGING/LOGGING_ISSUES.md#log-i-8`). ### Fix direction See `SIGNALR_TODO.md#sig-t-5`. ### Related - `../../../AyCode.Core/docs/LOGGING/LOGGING_ISSUES.md#log-i-8` (sibling gap — same plugin, logger setup) - `SIG-I-8` (client-side equivalent — `HubConnectionBuilder.Services` inner DI isolation forces a different workaround) - Plugin doc drift: `Nop.Plugin.Misc.AIPlugin/docs/SIGNALR/README.md:22` documents `services.AddSingleton(new AcBinaryHubProtocol())` — the actual code uses `.AddAcBinaryProtocol(opts => {...})`. Doc needs a rewrite. ## Client-side Setup & DI ### SIG-I-8: 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(...)` 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>().Value; hubBuilder.AddAcBinaryProtocol(protocolOpts); ``` ## Dispatch ### SIG-I-9: First-call null response (observed) **Status:** Open — not diagnosed **Affects:** `PostDataAsync` 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` awaiter's null-mapping path misroutes the parsed result, or `requestId → Task` 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#sig-t-1` ## Cross-cutting (canonical home: `AyCode.Core` repo's `docs/XCUT/`) ### XCUT-I-1: JSON-in-Binary request parameters — cross-ref Canonical entry: **`../../../AyCode.Core/docs/XCUT/XCUT_ISSUES.md#xcut-i-1`**. Summary: SignalR transport carries `SignalPostJsonDataMessage` (JSON inside a Binary envelope) for request params, while response path is pure Binary. Planned replacement is coordinated across BINARY serializer + SIGNALR transport + all consuming projects.