Svc/FileDownlink: add Reset command and stall warning to recover from a wedged downlink - #5598
Svc/FileDownlink: add Reset command and stall warning to recover from a wedged downlink#5598rvaccone wants to merge 4 commits into
Conversation
I'm not sure I agree with this reasoning. The existing CANCEL behavior follows the general flow-control protocol of file downlink (wait until the last packet comes back before allowing a new one to be sent). The new proposed behavior breaks the protocol. While breaking the protocol may be needed in some off-nominal cases, it seems dangerous to automatically break the protocol in every case that a transmission is canceled. In most cases, the operator probably just wants to end the transmission prematurely, not try to recover from a system fault. Also, it seems the new behavior defaults to masking memory leaks. A true memory leak (unrecoverable dropped packet) seems like a system design flaw that should not be silently ignored. Have you considered the following undesirable behaviors that the new design may introduce?
Have you considered keeping the CANCEL behavior as is, and adding a new command RESET to break the protocol by explicit command, in cases where there is no other way to recover from a memory leak? |
|
Agreed. Your concern #1 is stronger than stated. Even a slow but healthy downstream can break: a nominal Cancel plus a new downlink rewrites Rework plan (converting to draft meanwhile):
For the fan out case in #5447, Question: after Disclosure per AI_POLICY.md: AI assisted (Claude Code). Analysis reviewed against |
It seems reasonable to me to follow the same pattern as the ComQueue. @LeStarch do you have thoughts here? |
9794aa5 to
8a2572c
Compare
|
v2 is up. Worth a check: the drain is bounded by the configured queue depth, not a live Disclosure per AI_POLICY.md: AI assisted (Claude Code). Reviewed against |
8a2572c to
fa33880
Compare
fa33880 to
b7dd5ad
Compare
sylvesterkaczmarek
left a comment
There was a problem hiding this comment.
One concern with the new Reset drain: with the default FILEDOWNLINK_COMMAND_FAILURES_DISABLED = true, requests that were never transmitted are reported to port clients as STATUS_OK. As the SDD notes, DpCatalog can then mark the product transmitted and remove it. Could Reset's synthetic drop responses always report STATUS_ERROR to port clients, while preserving the legacy command-response mapping if needed?
b7dd5ad to
a74fc0d
Compare
|
Agreed. The flag's own comment scopes it to keeping a sequence from aborting, so it belongs on the command path only. Pushed a commit that does this:
Two things I left alone, called out in the SDD: Cancel still reports STATUS_OK to port clients, and the pre-existing failure paths (open error, zero size, bad offset, read error) still map to OK under the flag. Making those honest would let a missing or unreadable product wedge DpCatalog's catalog transmit, since it stops on any error and retries the same entry first. That needs a DpCatalog-side policy before FileDownlink can change, so I'd handle it separately. Happy to align Cancel here if you'd prefer. |
sylvesterkaczmarek
left a comment
There was a problem hiding this comment.
Re-reviewed the Reset response semantics. Active and queued port-originated requests now always receive STATUS_ERROR through abortStatus(), independent of FILEDOWNLINK_COMMAND_FAILURES_DISABLED, while command-originated responses retain the legacy flag mapping. The reset-drain tests pin both the active and queued port cases. This resolves the DpCatalog delivery-status issue I raised.
…Transfer declaration All three are leftovers from the timeout removal in nasa#4555: m_timeout and m_bufferSize are never written or read, and exitFileTransfer has no definition.
Once a transfer is in flight, the state machine advances only on bufferReturn: if the downstream component never returns the outstanding buffer, the component is stuck in WAIT (or CANCEL, since Cancel also waits on the return) until reboot, and Svc.Health cannot see the failure because pingIn still responds (nasa#5447). Cancel keeps its protocol-following behavior. The new Reset command is the explicit escape hatch: it force-completes the active transfer (cancel packet, DownlinkCanceled, response to the originator, cooldown) and drains the file queue with an error response per queued request, following the ComQueue.FLUSH_QUEUE pattern except that FileDownlink queue entries carry caller context, so each drained request is answered rather than silently discarded. Reset also advances m_lastBufferId past all handed-out buffer IDs so a late return of a formerly outstanding buffer is dropped as stale; without this, a cancel-packet buffer returned during COOLDOWN would trip the mode assertion in bufferReturn_handler (verified by removing the bump: ResetWedgedDownlink then dies on FileDownlink.cpp:155).
A downlink stuck in WAIT (or CANCEL) because the downstream component never returns the outstanding buffer produces no fault indication: pingIn still responds, so Svc.Health cannot see the failure. Add an optional stall timeout to configure(); when a downlink has waited that long for a buffer return, emit a DownlinkStalled warning once per wait. The default of 0 disables the warning, and existing configure() callers are unaffected. The warning is observational only. It triggers no automatic action, so it does not reintroduce the automatic timeout removed in nasa#4555; it tells operators that Reset may be needed. It is deliberately not count-throttled: the emission is already rate-limited to one per wait by construction, and a count throttle could be exhausted by a slow link and then stay silent through the very wedge the event exists to report. FileHandlingConfig gains a stallTimeout constant, defaulted to 0 and passed through by the FileHandling subtopology, so deployments can opt in by overriding config rather than by calling configure() a second time (which would hit ALREADY_CREATED on the file queue and assert at startup).
Reset never completes a transfer, but the active request and the drained queue were answered with STATUS_OK under the default FILEDOWNLINK_COMMAND_FAILURES_DISABLED, so a port client such as DpCatalog would mark an unsent product as transmitted and drop it from its catalog. Port clients now always receive STATUS_ERROR for every request Reset abandons, active or queued; command responses keep the flag mapping. A single abortStatus() helper holds the rule so the two paths cannot drift. Claude-Session: https://claude.ai/code/session_0199H7ZRDBT2m3kq7ovBmyuy
a74fc0d to
466bcd5
Compare
sylvesterkaczmarek
left a comment
There was a problem hiding this comment.
Rechecked the rebased head. The Reset response implementation and the active/queued port-client regressions relevant to my earlier review are unchanged from the approved head, so the STATUS_ERROR semantics remain intact. The rebase also brings unrelated sandbox/subtopology changes from devel, but they do not alter this Reset path. My approval stands.
|
We appreciate you working on this. We have reconsidered this approach and agreed that the RESET approach needs discussion with the CCB. The original issue was to work on the CANCEL command, it was correct to identify that the RESET approach was much better. However, there were many issues we realized with the RESET approach (i.e. memory leaks, ownership leaks, corruption, etc). Given these potential problems, we would like to reconsider the RESET approach. To that end we have submitted: #5864 for that exact purpose. In that issue we have recorded the original CANCEL request, and we have recorded this implementation, which will influence the discussion. For now I will close this PR, but it is tracked in the new ticket, so it can be revived once the CCB has a strong recommendation for reset. |
|
Thanks for taking the time to explain, and for capturing the history in #5864. That all makes sense. If it's useful, the stall warning and the dead-member cleanup don't depend on Reset. Happy to split those into a small separate PR. Either way, I'll follow #5864 and can pick this back up once the CCB decides. |
Change Description
A downlink whose buffer is never returned wedges
FileDownlinkinWAITuntil reboot.Cancelcan't clear it because it waits on the same return, andSvc::Healthcan't see itbecause
pingInstill answers.Per @bocchino's review,
Cancelis unchanged — the protocol break is a new command:Reset(0x03) completes the active transfer without waiting on the buffer return, thendrains the file queue, answering each dropped request rather than discarding it
(
ComQueue.FLUSH_QUEUEpattern, except entries here carry caller context). EmitsDownlinkReset(0x13) with the drop count.STATUS_ERRORto port clients for every request it drops, regardless ofFILEDOWNLINK_COMMAND_FAILURES_DISABLED, soDpCatalogkeeps the product for a retry.Command responses keep the flag mapping.
DownlinkStalled(0x14) warns once per wait when a downlink sits too long on a bufferreturn. Optional
stallTimeoutonconfigure(), default off, wired throughFileHandlingConfig. Observation only, no automatic action.m_timeout/m_bufferSize/exitFileTransferremoved; SDD §3.5.2 corrected.Testing/Review Recommendations
15 UTs (6 new), green under ASan/UBSan; all 4 commits build and pass individually.
Two calls worth a look: the drain is bounded by queue depth rather than a live
getMessagesAvailable()count, which isn't serialized against a concurrent enqueue; andDownlinkStalledis intentionally not throttled, since a count throttle only clears attransfer start and a slow link could exhaust it, then go silent through the actual wedge.
AI Usage (see policy)
Written with Claude Code and reviewed by me.
IAMAI