Skip to content

fix(scheduler): partial Azure sweep counts as success and evicts un-queried subscriptions' rows #1652

Description

@cristim

Summary

tolerateIncompleteSweep (internal/scheduler/scheduler.go:906-925) converts an Azure *azure.PartialSubscriptionFailureError into scheduler-level success. Today that is a no-op at all five call sites (every Azure path pins a subscription, and GCP/AWS never return that error type), so this is latent — it becomes live the moment the org-wide fan-out from #1520 is actually wired up.

The hazard

Once the fan-out is reachable, an account whose sweep missed N subscriptions is recorded as succeeded. Two consequences follow from treating a partial sweep as a complete one:

  1. Row eviction. The collection path replaces the account's stored recommendations with what the sweep returned. A sweep that never queried subscriptions X and Y returns no rows for them, so previously-collected rows for X and Y are deleted — the savings for those subscriptions silently vanish from the UI and from any downstream aggregate.
  2. last_collection_error is cleared, so nothing on the account records that the sweep was incomplete.

This is exactly the COR-03 hazard that mergeServiceResults' own comment warns about (providers/azure/recommendations.go:276-280): a partial result persisted as if it were complete reads as a shrinking savings opportunity rather than a failed collection.

tolerateIncompleteSweep's NOTE mentions the last_collection_error consequence but not the eviction one, which is the more damaging of the two.

Suggested direction

Before (or as part of) the change that makes the fan-out reachable, decide explicitly how a partial sweep interacts with persistence. Options, roughly in increasing order of effort:

  • Record the partial-failure detail on the account (a dedicated column, or keep last_collection_error populated) instead of clearing it, so the incompleteness survives into the data model.
  • Make the persistence step merge rather than replace when the sweep is known-partial, so un-queried subscriptions keep their previous rows.
  • Scope the replace to the subscriptions that were actually queried (PartialSubscriptionFailureError already carries Failed / FailedSubscriptionIDs(), so the queried set is derivable).

Whichever is chosen, extend tolerateIncompleteSweep's NOTE to state the eviction consequence, not only the last_collection_error one.

Provenance

Found during the adversarial review of #1520. Deliberately not fixed in that PR: the code path is unreachable today, and the fix is a persistence-semantics decision rather than a defect in the fan-out client.

No activity

Activity on this issue will appear here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions