GitHub
11/08/2026, 5:48 PMDistributedQuery.Expected still counted the cancelled targets, so the query never reached its expected total.
2. Stuck active forever. A query completes in UpdateQueryStatus, when a node reports and no target is left pending. If cancellation removed the last pending targets, nothing ever ran that check again, so the query stayed active indefinitely.
What changes
• New Queries.CancelNodeQueries(ids []uint) in pkg/queries. In one transaction it:
• deletes the node-queries;
• lowers each affected query's Expected by the number cancelled, never below 0;
• marks a standard query completed and inactive when nothing is left pending;
• marks a carve completed only when nothing at all is left to arrive (Expected reaches 0). Carves that delivered to some nodes still complete when their files land, as before.
• sends the usual metadata change event, so the UI refreshes.
• Both cancellation paths, cancelOutOfScope (tags and sensitive tags) and cancelSensitiveCarves (sensitive paths), use it instead of deleting rows directly. The query cache is still invalidated for the affected nodes.
Security and operational impact
• No access change: the same node-queries are cancelled as before. Only the bookkeeping of their queries is fixed.
• Queries already stuck from earlier cancellations are not backfilled. Expiry or a manual "complete" clears them.
• No schema, API or UI changes.
Validation
• New TestCancelNodeQueries (pkg/queries):
• a query whose last two pending targets are cancelled after one node answered completes, with Expected at 1;
• a carve with a target still pending only drops by one and stays active;
• the carve completes once its last target is cancelled;
• an empty cancel is a no-op.
It failed before the change.
• TestUntagCancelsTagScopedPending now also checks that the cancelled queries' Expected drops to 0.
• The existing cancellation tests for tag delete, sensitive tags and sensitive paths pass unchanged.
• go test ./... passes on an uncached run.
jmpsec/osctrlGitHub
11/08/2026, 6:02 PM