Shard-scoped caches
How --shard-scoped-cache makes each replica cache only the objects of its own shards.
Sharding divides the reconcile work between replicas. It does not
divide the memory. Without a filter, each replica caches all objects and skips the objects of
other shards at reconcile time. With --shard-scoped-cache, a replica caches only the objects of
the shards that it owns. Then the cache memory of a replica decreases when you add replicas.
Filtered kinds#
| Informer | With --shard-scoped-cache | Reason |
|---|---|---|
Release | Filtered | One object for each Release. The largest part of the memory. |
ReleaseInventory | Filtered | One object for each Release. Same shard as its Release. |
Cluster | Filtered | Same shard as the Releases that target it. |
GitRepository, HelmRegistry, OCIRegistry | All objects | A Release can reference a source in any shard. |
GlobalHealthRuleSet, HealthRuleSet | All objects | A Release can select any rule set. Only the observer caches them. |
A replica that owns a shard has the Releases of that shard, their inventories and their target
Clusters. A Release in namespace a can target a Cluster in namespace b. Both have the shard of
b/<cluster>, so they are in the same cache.
Label selector#
The filtered informers send a label selector to the apiserver in each LIST and WATCH:
convoy.kumodo.io/shard in (0,3,6)The apiserver filters the objects, so the replica does not receive the objects of other shards.
When the replica owns no shard, the selector is convoy.kumodo.io/shard in (none). This selector
matches no object. Without --shard-scoped-cache, the informers use no selector.
Rebuild on ownership change#
scopedinformers.Set holds one informer factory for the three filtered kinds. The controllers
receive stable informer and lister facades from the Set. When the owned shards change, the
OnChange callback of the Coordinator calls Rebuild:
Rebuildcomputes the new selector. If the selector did not change, it returns.- It creates a new factory with the new selector.
- It adds the recorded indexers and event handlers to the new informers.
- It starts the new factory and waits until the three caches are synced.
- It replaces the informers behind the facades.
- It stops the previous factory.
The previous cache serves reads until step 5, so reads never see an empty cache. For a short
time, the replica holds two caches. The managers also call Rebuild once after Prime, before
the controllers start. This call covers a replica that owns no shard, because then Prime does
not call OnChange.
Rebuild runs on the tick goroutine of the Coordinator. It blocks the next lease renewal until
the new caches are synced. If Rebuild fails, the replica logs the error and keeps the previous
caches.
Label requirement#
A filtered informer receives only objects with a matching shard label. An object without the label is in no cache, so no replica reconciles it.
- The apiserver writes the label on each create and update of a
Releaseand aCluster. An object written by convoy 0.9.0 has the label. - The controller-manager copies the label of the Release to the
ReleaseInventoryat each inventory write. If the Release has no label, theReleaseInventoryhas no label. - An object from a version without the shard label has no label until its next write.
Before you set --shard-scoped-cache, check that all Release and Cluster objects have
the label. An update of each object makes the apiserver write it.
Memory#
With a full cache, each replica holds all Release, ReleaseInventory and Cluster objects. The
memory of a replica does not decrease when you add replicas. With shard-scoped caches, a replica
holds about 1/N of these objects for N replicas, up to N = S. Source and rule set objects
are on each replica in both modes.
After a replica stops, the other replicas take its shards. Each replica then holds about
1/(N-1) of the objects. Set the memory limit for this case. See
Scaling and high availability.
Default and deployment#
--shard-scoped-cache is a flag of convoy-controller-manager and convoy-observer. In the
binary, the default is off. The development manifests in dev/deploy/base set the flag on both
components. The overlays single-replica and multi-replica use this base. The
multi-replica overlay runs two observer replicas with the flag.
Observer subresources#
The apiserver sends /preview, /diff, /health and /history to one observer address. The
observer reads the Release from its own cache. With --shard-scoped-cache and more than one
observer replica, the replica that receives the request does not always have the Release.
In the code
convoy v0.9.0
internal/scopedinformers.Newinternal/scopedinformers/scopedinformers.go:237internal/scopedinformers.(*Set).Rebuildinternal/scopedinformers/scopedinformers.go:287internal/scopedinformers.(*Set).selectorinternal/scopedinformers/scopedinformers.go:262internal/inventory.(*apiStore).shardLabelsinternal/inventory/apistore.go:70internal/controllermanager.(*Options).Runinternal/controllermanager/manager.go:142internal/observermanager.(*Options).Runinternal/observermanager/manager.go:135