fix: propagate SPRT discovery bind/broadcast failures instead of swallowing them
SprtDiscoveryService caught SocketException on the UDP 9100 bind and the broadcast send and returned an empty list either way. That defeated the CompositeDiscoveryService fix that rethrows only when every child discovery service fails: on a real network fault the composite would see one child throw (ENPC) and one child silently succeed empty (SPRT), conclude discovery worked and found nothing, and let PrinterDiscoveryBackgroundTask expire and deregister live Epson printers after the retention window - discarding their queued jobs. Move the policy decision to the composite, which already has it right: the child now reports bind/broadcast failure honestly by rethrowing, and CompositeDiscoveryService absorbs a single child's failure while still rethrowing when every child fails. The per-datagram receive-loop catch is unchanged - a single bad receive still shouldn't abort the pass. Rewrote SprtDiscoveryServiceTests' bind-failure test to assert the throw, and added an end-to-end CompositeDiscoveryServiceTests case with a real SprtDiscoveryService failing to bind alongside a stub that succeeds, proving the composite still degrades gracefully rather than losing the pass. Updated the POS895 design doc to match the new contract. Claude-Session: https://claude.ai/code/session_01E6N3P4nSZLtp4BC2LXVref
This commit is contained in:
@@ -58,13 +58,21 @@ listens for the timeout, and maps each 39-byte reply to a `DiscoveredPrinter`:
|
||||
Replies that do not parse as a 17-character dashed MAC are logged and skipped,
|
||||
never thrown.
|
||||
|
||||
**Binding UDP 9100 can fail** when another process holds it. On `SocketException`
|
||||
the service logs a warning and returns an empty list, so ENPC discovery still
|
||||
runs. Discovery degrades; it does not crash.
|
||||
**Binding UDP 9100 can fail** when another process holds it, and the broadcast
|
||||
send can fail too. On `SocketException` the service logs a warning and
|
||||
**rethrows** - it does not swallow the fault into an empty list. A silent empty
|
||||
list is indistinguishable from "no SPRT printers on this network", and
|
||||
`CompositeDiscoveryService` is the layer that needs to tell those apart, not
|
||||
this service.
|
||||
|
||||
**`CompositeDiscoveryService`** - takes `IEnumerable<IDiscoveryService>`, runs
|
||||
them concurrently on the shared timeout, merges results by `IPAddress` with
|
||||
first-wins, and forwards each child's `onPrinterDiscovered` callback as it fires.
|
||||
It absorbs a single child's failure (logs it, keeps the other children's
|
||||
results) but rethrows when every child fails, since that means discovery
|
||||
itself is broken rather than that the network simply has no printers on it.
|
||||
Discovery still degrades gracefully overall - it just does so at the composite
|
||||
layer, which is the only layer that can see all children and make that call.
|
||||
|
||||
**Wiring** - `PrintServerBootstrapper.cs:77` changes from
|
||||
`Bind<IDiscoveryService>().To<DiscoveryService>()` to the composite over both
|
||||
|
||||
Reference in New Issue
Block a user