netconf: classify mid-flight transport drops as ErrNotConnected - #481
Open
steiler wants to merge 4 commits into
Open
netconf: classify mid-flight transport drops as ErrNotConnected#481steiler wants to merge 4 commits into
steiler wants to merge 4 commits into
Conversation
Close the residual gap left by PR #466: the netconf target's three mid-flight transport-drop branches (internalGet's GetConfig, setToDevice's EditConfig and Commit) detected drops via a fragile 'EOF' substring check and returned the raw driver error, so it crossed the wire as codes.Unknown instead of the already-established codes.Unavailable/ErrNotConnected recoverable path. - Add isTransportError in the netconf package: checks errors.Is(io.EOF), errors.As(net.Error), and a substring fallback (EOF, broken pipe, connection reset, i/o timeout) for drivers that don't preserve that wrapping. - Normalize scrapligo's own opaque errConnectionError sentinel (which a real mid-flight EOF actually surfaces as, per tracing scrapligo v1.4.1's channel read loop) into io.EOF at the driver/scrapligo adapter boundary, so nc.go's isTransportError stays driver-agnostic and only scrapligo-specific code knows about scrapligo's sentinels. - Replace all three call sites' substring check with isTransportError, and wrap the returned error with targettypes.ErrNotConnected via a shared handleTransportError helper, so translateInternalToGrpcError maps it to codes.Unavailable like the pre-flight checks already do. - Leave the candidate Discard()-on-error path untouched for non-transport errors, per spec. See .scratch/netconf-midflight-eof-not-connected/spec.md.
- ADR 0001: normalize driver-library errors (scrapligo's util.ErrConnectionError) to io.EOF at the driver/scrapligo adapter boundary, not in nc.go, so isTransportError stays driver-agnostic. - ADR 0002: deliberately exclude scrapligo's util.ErrTimeoutError from transport-error classification, to avoid config-server retrying transactions against slow-but-alive devices. - CONTEXT.md: document the netconf target and note that ErrNotConnected is intentionally reused for both the pre-flight (never connected) and mid-flight (transport dropped during an in-progress RPC) scenarios.
This was referenced Sep 4, 2026
Collaborator
Author
|
Companion fix is now up: sdcio/config-server#487 ("fix(config): correct Type stamped by updateConfigWithError's non-recoverable branch"). Fixes the independent control-plane bug where a non-recoverable |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
An NF restart during NETCONF
edit-config(or the subsequentcommit) returns a raw, unwrapped EOF-shaped error. No retry is triggered on the config-server side after the NF recovers, and status indicators (Target/Config/ConfigSet) incorrectly keep reportingReady=True.Context: sibling/predecessor work
This closes the residual gap left by #466 ("feature/not-connected-error", already merged to
main), which introducedtargettypes.ErrNotConnectedand taughttranslateInternalToGrpcErrorto map it tocodes.Unavailable, and updated the netconf target's two pre-flight connectivity checks (internalGet/Setguarded byt.Status().Err()) accordingly.#466 did not update the three mid-flight transport-drop branches in
pkg/datastore/target/netconf/nc.go, which still detected drops via a fragilestrings.Contains(err.Error(), "EOF")check and returned the raw driver error unwrapped. That raw error propagated ascodes.Unknown, which is exactly the casesdcio/config-server'sisRecoverableTransactionErrortest table already anticipated as a data-server gap to close ("Non-Recoverable Unknown not connected (pre-fix data-server)").Companion fix: sdcio/config-server#487 ("fix(config): correct Type stamped by updateConfigWithError's non-recoverable branch"). Either fix alone resolves the reported symptom for this specific NF-restart scenario; both are needed to fully close the gap — see that PR's Problem statement for why.
Pairs-with: sdcio/config-server#487
What changed
isTransportErrorhelper inpkg/datastore/target/netconf/transport_error.go: classifies a mid-flight transport drop viaerrors.Is(io.EOF)/errors.As(net.Error), with a substring fallback (EOF,broken pipe,connection reset,i/o timeout) for drivers that don't preserve that wrapping.util.ErrConnectionErrorsentinel (what a real mid-flight EOF actually surfaces as, per tracing scrapligo v1.4.1's channel read loop) toio.EOFat thedriver/scrapligoadapter boundary — seepkg/datastore/target/netconf/docs/adr/0001-normalize-driver-errors-at-adapter-boundary.mdfor why this lives at the driver boundary rather than innc.go.util.ErrTimeoutErrorfrom this classification — seedocs/adr/0002-exclude-timeouts-from-transport-error-classification.md.internalGet'sGetConfig,setToDevice'sEditConfig/Commit) withisTransportError, wrapping via a sharedhandleTransportErrorhelper so the error flows through the exact same, already-reviewedtranslateInternalToGrpcError→codes.Unavailablepath as the pre-flight checks.Discard()-on-error path untouched for non-transport errors.pkg/datastore/target/netconf/CONTEXT.mddocumenting thatErrNotConnectedis intentionally reused for both pre-flight and mid-flight scenarios.Testing
transport_error_test.go,nc_transport_error_test.go,driver/scrapligo/scrapligo_test.go.go build ./...,go vet ./...,go test ./...all pass.Out of scope
pkg/datastore/target/gnmi/— the gNMI "Network Disconnect/Reconnect" path already works correctly.reconnect()loop itself.edit-configfrom data-server — that's a config-server responsibility.Spec:
.scratch/netconf-midflight-eof-not-connected/spec.md