Repository navigation
Reducing ETCD pressure from ResolutionRequest CRDs at scale #10644
Replies: 6 comments 6 replies
|
I want to pursue a TEP for non-CRD resolution, I believe that makes more sense instead of using another CRD for resolving. It will offload the resolution completely to a different service and won't touch etcd as well. |
|
To expand on my earlier comment, I think a TEP should compare reference architectures before assuming a particular transport or storage backend. Hatchet is interesting because it separates the orchestration engine, durable state, and workers. kube-shard takes a different approach: it preserves the Kubernetes API while routing selected API groups to a secondary API server backed by Kine and SQL. For Tekton, the key boundary may be between state that is part of our public Kubernetes-native contract: kube-shard looks useful as a platform-level mitigation because it moves writes away from the primary etcd, but it retains the create/update/delete churn and introduces a secondary API server, Kine, and database stack. I am therefore unsure whether it should become a dependency of Tekton itself. The TEP should also quantify how much pressure comes specifically from ResolutionRequest objects versus other Tekton and Kubernetes resources before choosing an architecture. @gbenhaim @chmouel , do you have pointers to other reference architectures or constraints we should consider, particularly around HA, retries, consistency, authentication/authorization, observability, and upgrades? |
|
If option 1 turns out to be enough, is it worth it building option 2, which introduces a lot of additional complexity, in the code base and from a service deployment POV? |
This seems like the most tenable .However I think this understates the complexity required. This would change the caching API and break some of the separation of responsibilities since right now caching configuration is resolver-specific and the Pipelines Controller is by design ignorant of the resolvers and their implementation details. I believe in order for this to work, we would need to do one of two things, each with their own tradeoffs: |
|
Related finding here: since ResolutionRequests are not removed until after the PipelineRun is deleted, they can incur a pretty significant etcd footprint. Deleting the ResolutionRequests as soon as they're no longer needed could reduce the etcd footprint of a PipelineRun quite as much as 30% after the pipelineRun starts, which I think is likely the majority of a pipelineRun's lifetime: #10692 |
Uh oh!
There was an error while loading. Please reload this page.
Problem
At scale,
ResolutionRequestCRDs create significant ETCD pressure — independent of whether the resolved content is cached or not.Each time a
PipelineRunorTaskRunneeds to resolve a remoteTaskorPipeline, aResolutionRequestcustom resource is created in ETCD, reconciled by the resolver controller, updated with the result, and eventually garbage collected. That's at minimum 3 ETCD writes per resolution (create → update status → delete), even when the resolver cache returns an instant hit.Based on feedback from customer engagements, we've seen environments running 80,000+ TaskRuns/hour (projected to grow 3×), where ETCD capacity — not compute — is the practical scalability ceiling. At that throughput, ResolutionRequest churn is a meaningful contributor to ETCD pressure.
What the current cache does (and doesn't do)
The resolver cache (introduced in v0.76 / late 2025, with several important fixes through early 2026) successfully reduces remote fetch latency and upstream load — on a cache hit, no network call is made.
However, the cache operates inside the resolver's
Resolve()method, which is called by theResolutionRequestreconciler. By the time the cache is consulted, theResolutionRequestCRD has already been written to ETCD. The cache eliminates redundant fetches but does not eliminate the CRD round-trip.In short: the cache solves latency, not ETCD pressure.
Possible directions
A few approaches could address this (not mutually exclusive):
1. Short-circuit resolution before CRD creation
Move the cache (or a layer of it) into the
PipelineRun/TaskRunreconciler so that on a cache hit, noResolutionRequestis created at all. This is the least disruptive change — the existing CRD-based flow remains for cache misses.2. Non-CRD resolution path ("resolver v2")
Replace the
ResolutionRequestCRD coordination mechanism with a direct HTTP/gRPC call from the pipeline controller to resolver endpoints. Resolution becomes a synchronous service call with built-in caching, eliminating CRD writes entirely. This is a larger architectural change but removes the fundamental coupling between resolution and ETCD.Importantly, this doesn't have to be a big-bang migration. The CRD-based and non-CRD resolution paths can coexist — which path is used would be a configuration switch (e.g. a feature flag in the
feature-flagsConfigMap). Operators can adopt the new path at their own pace, and the existing CRD-based flow remains available as a fallback. From the user's perspective (PipelineRun/TaskRun specs), nothing changes — it's purely an implementation detail of how resolution is dispatched.3. Hybrid
Option 1 as a near-term improvement, option 2 as a longer-term evolution.
Why not bring back ClusterTask?
We've received requests to reintroduce a cluster-scoped Task type that bypasses resolvers entirely. While we understand the motivation (avoiding ResolutionRequest overhead for shared, well-known tasks), ClusterTask was removed deliberately and reintroducing it would:
The right fix is to make resolution itself efficient at scale, regardless of resolver type.
Discussion
All reactions