KernelRouter.#deliverGCAction releases the kernel's side before telling the endpoint, and when a remote refuses the delivery it logs and carries on. The comment there says why retrying is worse — GC actions are selected ahead of all other work, so a remote that keeps refusing would be handed the same item every crank and nothing else would run — and it names the gap this leaves:
The next incarnation change reconciles a dropped or retired export, but not a retireImports: forgetEndpointImports keeps only entries whose direction is export. Those stay on the peer's side until it drops them itself.
That was a documented hole rather than a reachable one, because getImporters only enumerated vats, so a remote was never queued a retireImport in the first place. #1022 fixes getImporters to include remotes — it had to, since retireKernelObjects deletes the object once it has told the importers and a remote left out kept a c-list entry naming a kref that no longer exists. The fix is right, and it makes this path reachable.
So: a retireImports to a remote that fails on a full send queue leaves the peer holding an import for an object this kernel has deleted, permanently, with a log line as the only record.
Two directions, neither of them the unbounded retry the comment rightly rejects: re-queue the action for that remote with a bound, or record the orphaned eref so the next incarnation change can sweep it — which would mean forgetEndpointImports stops filtering on direction, or a separate record for the ones it does not cover.
Found by review of #1021 / #1022 / #1023.
KernelRouter.#deliverGCActionreleases the kernel's side before telling the endpoint, and when a remote refuses the delivery it logs and carries on. The comment there says why retrying is worse — GC actions are selected ahead of all other work, so a remote that keeps refusing would be handed the same item every crank and nothing else would run — and it names the gap this leaves:That was a documented hole rather than a reachable one, because
getImportersonly enumerated vats, so a remote was never queued aretireImportin the first place. #1022 fixesgetImportersto include remotes — it had to, sinceretireKernelObjectsdeletes the object once it has told the importers and a remote left out kept a c-list entry naming a kref that no longer exists. The fix is right, and it makes this path reachable.So: a
retireImportsto a remote that fails on a full send queue leaves the peer holding an import for an object this kernel has deleted, permanently, with a log line as the only record.Two directions, neither of them the unbounded retry the comment rightly rejects: re-queue the action for that remote with a bound, or record the orphaned eref so the next incarnation change can sweep it — which would mean
forgetEndpointImportsstops filtering on direction, or a separate record for the ones it does not cover.Found by review of #1021 / #1022 / #1023.