convoy docs
Search

Sync policy

When convoy applies a render, how a /sync request works, and when convoy reconciles a Release again.

The sync policy tells convoy when to apply a render. In automatic mode, convoy applies the render at each reconcile. In manual mode, convoy renders and reports, but it applies only after a /sync request. The sync policy also sets how often convoy reconciles the Release, and how it retries after a failure.

The fields are in spec.syncPolicy. See Release.

Automatic and manual mode#

spec.syncPolicy.automatic selects the mode. The field has no default value. If you do not set it, the value is false, and the Release uses manual mode.

Automatic (automatic: true)Manual (automatic: false)
Fetch, render and mutationsAt each reconcileAt each reconcile
ApplyAt each reconcileOnly when a /sync request is pending
Drift correctionYes. The observer requests a /sync when it finds drift.No. The observer only reports drift.

In automatic mode, convoy applies the full render at each reconcile, also when nothing changed. This also corrects drift at each periodic reconcile.

In manual mode without a pending /sync request, convoy compares the current render with status.lastSuccessful.renderRef:

  • Equal. The Applied condition is True with reason Applied. The message says that the Release is in sync and that the apply waits for /sync.
  • Different, or no render applied yet. The Applied condition is False with reason WaitingForSync.

In both cases convoy applies nothing and does not run the pre-apply dry run.

The /sync request#

POST releases/<name>/sync writes the current time to the annotation convoy.kumodo.io/sync-requested-at on the Release. A change to this annotation starts a reconcile.

In manual mode, a request is pending when the annotation time is later than status.lastHandledSyncAt. convoy compares the times to the second.

  • convoy sets status.lastHandledSyncAt only after a successful apply.
  • If the apply fails or waits, the request stays pending. The next reconcile tries the apply again.
  • In automatic mode, convoy does not use status.lastHandledSyncAt. A /sync request only starts a reconcile.

These writes also set convoy.kumodo.io/sync-requested-at. In manual mode, each one counts as a /sync request:

  • a write to the /pin, /mutations or /lifecycle subresource
  • a /sync request from the observer health loop. The observer sends one when a sync wave completes, when health assessment reaches a new render, or when a batch of lifecycle hooks completes.
sh
kubectl convoy release sync podinfo

Forced sync#

POST releases/<name>/sync?force=true also writes convoy.kumodo.io/sync-force-requested-at. The next reconcile then:

  • skips the pre-apply dry run. See Apply and prune.
  • starts a new Progressing rollout, if the Release has a failurePolicy. This clears a Failed or RolledBack rollout. See Progressive rollout.

A force request has effect for one reconcile only. convoy writes status.lastHandledForceSyncAt after that reconcile, also when the reconcile fails or waits.

sh
kubectl convoy release sync podinfo --force

What starts a reconcile#

The release controller reconciles a Release when one of these events occurs:

  • The Release is created.
  • metadata.generation changes. Only a change to spec changes the generation.
  • The annotation convoy.kumodo.io/sync-requested-at changes.
  • The informer resync occurs. The default period is 2 minutes (flag --resync).
  • The time of a scheduled requeue arrives. The requeue rules are below.
  • The observer writes new health data to the ReleaseInventory.
  • The status of the Applied or Healthy condition changes on a Release of a lower cluster wave on the same Cluster.

A status write on a Release does not start a reconcile of that Release. The write does not change the generation or the annotation. So the status writes of the controller and the observer do not cause a loop.

A change to mutations or lifecycle through the main resource does not change the generation. convoy uses the change at the next periodic reconcile. A write through the /mutations or /lifecycle subresource starts a reconcile at once.

Requeue rules#

After each reconcile, convoy decides when to reconcile the Release again.

Result of the reconcileNext reconcile
Transient failure, retryBackoff.initial is setAfter the retryBackoff delay
Transient failure, retryBackoff.initial is not setAfter the delay of the default rate limiter
Success, or a hold that is not an errorAfter spec.syncPolicy.interval
Rollout in ProgressingAfter interval, or after 10 seconds if that is shorter

Transient failures include SourceNotReady, ValuesNotReady, ClusterNotReady, DeployFailed and PruneFailed. Unsupported, Unappliable, Pinned, HookFailed and the waiting reasons are not transient. convoy checks them again at the next periodic reconcile.

interval#

spec.syncPolicy.interval is the period of the periodic reconcile. The observer uses the same interval for its drift checks. See Drift.

The field has no default value. With interval not set or 0, convoy schedules no periodic reconcile. Only the informer resync and the events above start a reconcile.

retryBackoff#

With retryBackoff.initial set, convoy calculates the delay before retry number n as initial × factor^(n-1):

  • If factor is less than 1, convoy uses 2.
  • The delay is not more than max. If max is not set, the limit is 1000 seconds.
  • convoy counts the failures in memory. A success or a hold resets the count. A restart of the controller also resets it.

Without retryBackoff.initial, the work queue retries with its default rate limiter. That limiter starts at 5 milliseconds and doubles the delay up to 1000 seconds.

In the code

convoy v0.9.0