CHK reconciles in a tight loop after its first Completed; with `onKeeperResourceUpdate: reconcile` the dependent CHI never finishes (0.27.4)
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 12/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- go, helm, kubernetes
- Domain
- backend, infrastructure
Research direction
Start with keeperPredicate in cmd/operator/app/thread_keeper.go, which passes every CHK update, then trace reconcileCR and HasReconcileWork in pkg/controller/chk/worker-reconciler-chk.go to see why a CHK with an empty action plan keeps continuing. The CHI side is shouldReconcileOnKeeperUpdate and enqueueDependentCHIs in pkg/controller/chi/controller-chk-watcher.go. Done means the CHK settles after its first Completed and a CHI with no NormalizedCRCompleted gets created and deleted without a reconcile livelock.
Written by the indexing model from the issue text.
Description
Environment
| Item | Value |
|---|---|
| clickhouse-operator | 0.27.4 (Helm chart altinity-clickhouse-operator 0.27.4, image tag = appVersion) |
| Kubernetes | Amazon EKS 1.35, platform version eks.24 |
| ClickHouse server | clickhouse/clickhouse-server:26.8.25.14 |
| ClickHouse Keeper | clickhouse/clickhouse-keeper:26.8.25.14 |
| Operator scope | one namespace (clickhouse), namespaced RBAC |
| Operator config | reconcile.coordination.keeper.readyTimeout: 600, reconcile.coordination.keeper.onKeeperResourceUpdate: reconcile |
Summary
We created a ClickHouseInstallation and a ClickHouseKeeperInstallation with the same name in one apply.
The CHI referenced the CHK with spec.configuration.zookeeper.keeper.name.
Two things went wrong at once.
- The CHK reconciled in a tight loop. It reached
Completedabout 92 times a minute for two hours.
Each cycle had an empty action plan but still loggedCR has reconcile work - continue reconcile.
The loop started 5 ms after the firstCompletedand stopped only when we deleted the CHK. - The CHK watcher turned that loop into a CHI livelock.
Every CHKCompletedenqueued aReconcileCHI cmd=addfor the dependent CHI.
The CHI had noNormalizedCRCompletedyet, soshouldReconcileOnKeeperUpdatealways returned true.
The in-flight CHI reconcile was cancelled every time a new command arrived.
The CHI never got past keeper resolution and never created its StatefulSet.
kubectl delete chiwas blocked the same way:deleteCHI()started 70 times a minute and never finished.
A production CHI and CHK in the same namespace were not affected. They were already fully reconciled.
Steps to reproduce
-
Run operator 0.27.4 with this config:
reconcile: coordination: keeper: readyTimeout: 600 onKeeperResourceUpdate: reconcile -
Apply a CHK and a CHI together, in one
kubectl apply(we used one Helm release):apiVersion: clickhouse-keeper.altinity.com/v1 kind: ClickHouseKeeperInstallation metadata: name: restore namespace: clickhouse spec: defaults: storageManagement: provisioner: Operator reclaimPolicy: Retain templates: podTemplate: keeper dataVolumeClaimTemplate: keeper-data configuration: clusters: - name: keeper layout: replicasCount: 1 templates: podTemplates: - name: keeper spec: containers: - name: clickhouse-keeper image: clickhouse/clickhouse-keeper:26.8.25.14 volumeClaimTemplates: - name: keeper-data spec: storageClassName: gp3 accessModes: [ReadWriteOnce] resources: requests: storage: 5Gi --- apiVersion: clickhouse.altinity.com/v1 kind: ClickHouseInstallation metadata: name: restore namespace: clickhouse spec: configuration: zookeeper: keeper: name: restore clusters: - name: swaap layout: shardsCount: 1 replicasCount: 1 templates: podTemplates: - name: server spec: containers: - name: clickhouse image: clickhouse/clickhouse-server:26.8.25.14 -
Watch the operator log. Within one minute the CHK completes, then completes again every 0.4 to 0.7 s.
The CHI never createschi-restore-swaap-0-0. -
Run
kubectl -n clickhouse delete chi restore. The delete never completes.
Observed behaviour
Rates (operator log, Loki count_over_time per minute)
Phase 1, 09:22:47Z to 10:03Z, CHI and CHK both present:
| Log line | Per minute |
|---|---|
CHK clickhouse/restore reached Completed — looking for dependent CHIs |
92 |
No NormalizedCRCompleted for CHI clickhouse/restore — cannot diff, enqueueing |
92 |
Triggering reconcile for CHI clickhouse/restore due to CHK clickhouse/restore completing |
92 |
ENQUEUE new ReconcileCHI cmd=add for clickhouse/restore |
92 |
client rate limiter Wait returned an error: context canceled (CHI keeper resolver) |
23 to 36 |
deleteCHI():CHI:clickhouse/restore:will delete CR with all resources (after 09:37:54Z) |
70 |
Phase 2, 10:03Z to 11:26Z, CHI recreated with zookeeper.nodes and healthy, CHK still present:
| Log line | Per minute |
|---|---|
CHK clickhouse/restore reached Completed |
92 |
| any line mentioning the CHI | 0 |
Totals for 2026-10-15: CHK restore ran reconcileCR() 11,427 times. The production CHK swaap in the same namespace ran it 24 times. The CHI restore ran reconcileCR() 530 times and never completed in phase 1.
Log excerpts
Creation and the first CHK completion. The CHK restarts its reconcile 5 ms after completing, before the watcher fires:
I1015 09:22:01.650178 1 controller.go:719] ENQUEUE new ReconcileCHI cmd=add for clickhouse/restore
I1015 09:22:01.784951 1 worker-reconciler-chk.go:58] worker-reconciler-chk.go:58:reconcileCR():start:CHK:clickhouse/restore
I1015 09:22:47.156775 1 worker.go:336] finalizeReconcileAndMarkCompleted():CHK:clickhouse/restore:reconcile completed successfully, task id: auto-54df58ca-bede-450b-96b2-5573e1174969
I1015 09:22:47.157603 1 controller-chk-watcher.go:112] CHK clickhouse/restore reached Completed — looking for dependent CHIs
I1015 09:22:47.162484 1 worker-reconciler-chk.go:58] worker-reconciler-chk.go:58:reconcileCR():start:CHK:clickhouse/restore
I1015 09:22:47.503048 1 worker.go:336] finalizeReconcileAndMarkCompleted():CHK:clickhouse/restore:reconcile completed successfully, task id: auto-222ea6fe-04d4-4980-addc-ef9497a93701
I1015 09:22:47.559295 1 controller-chk-watcher.go:221] CHI:clickhouse/restore:No NormalizedCRCompleted for CHI clickhouse/restore — cannot diff, enqueueing
I1015 09:22:47.559317 1 controller-chk-watcher.go:197] Triggering reconcile for CHI clickhouse/restore due to CHK clickhouse/restore completing
I1015 09:22:47.560022 1 controller.go:719] ENQUEUE new ReconcileCHI cmd=add for clickhouse/restore
One CHK cycle in steady state. The action plan line is empty, yet the reconcile continues and rewrites every object:
I1015 10:30:00.437388 1 worker.go:336] finalizeReconcileAndMarkCompleted():CHK:clickhouse/restore:reconcile completed successfully, task id: auto-21535e60-ee0f-4df2-9227-16dea3453183
I1015 10:30:00.444653 1 worker-reconciler-chk.go:58] worker-reconciler-chk.go:58:reconcileCR():start:CHK:clickhouse/restore
I1015 10:30:00.444688 1 worker.go:483] createTemplatedCR():CHK:clickhouse/restore:CR has an ancestor, use it as a base for reconcile. CR: clickhouse/restore
I1015 10:30:00.476445 1 worker-reconciler-chk.go:203] CHK:clickhouse/restore:
I1015 10:30:00.476482 1 worker-reconciler-chk.go:110] reconcileCR():CHK:clickhouse/restore:CR has reconcile work - continue reconcile
I1015 10:30:00.506139 1 worker-config-map.go:70] updateConfigMap():CHK:clickhouse/restore:Update ConfigMap clickhouse/chk-restore-common-configd
I1015 10:30:00.596622 1 worker-pdb.go:36] PDB updated: clickhouse/chk-restore-keeper
I1015 10:30:00.711945 1 worker-service.go:206] updateService():CHK:clickhouse/restore:Update Service success: clickhouse/chk-restore-keeper-0-0
I1015 10:30:00.800383 1 worker.go:190] shouldForceRestartHost():Host:0-0[0/0]:clickhouse/restore:Host force restart is not required. Host: 0-0
I1015 10:30:00.909489 1 worker-service.go:206] updateService():CHK:clickhouse/restore:Update Service success: clickhouse/keeper-restore
I1015 10:30:01.087557 1 controller-chk-watcher.go:112] CHK clickhouse/restore reached Completed — looking for dependent CHIs
The CHI side during phase 1. The reconcile restarts about every 0.4 to 1.4 s and the keeper resolver sees a cancelled context:
I1015 09:30:00.162374 1 worker.go:384] worker.go:347:updateCHI():end:CHI:clickhouse/restore
I1015 09:30:00.162439 1 worker.go:346] worker.go:346:updateCHI():start:CHI:clickhouse/restore
I1015 09:30:00.162476 1 worker.go:354] updateCHI():CHI:clickhouse/restore:finalizer in place, proceed to reconcile cycle. CHI: clickhouse/restore
I1015 09:30:00.361301 1 controller.go:719] ENQUEUE new ReconcileCHI cmd=add for clickhouse/restore
I1015 09:30:01.167511 1 controller-keeper-resolver.go:82] Keeper replica discovery failed or returned 0 nodes, falling back to CR service: failed to resolve keeper ref: host services clickhouse/restore: client rate limiter Wait returned an error: context canceled
kubectl delete chi restore at 09:37:54Z. The delete starts, the watcher enqueues another add, and the delete starts again one second later:
I1015 09:37:54.163551 1 worker-deleter.go:765] deleteCHI():CHI:clickhouse/restore:will delete CR with all resources: clickhouse/restore
I1015 09:37:54.181214 1 worker-deleter.go:291] deleteCRProtocol():CHI:clickhouse/restore:Delete CHI started
I1015 09:37:54.362168 1 controller-chk-watcher.go:221] CHI:clickhouse/restore:No NormalizedCRCompleted for CHI clickhouse/restore — cannot diff, enqueueing
I1015 09:37:54.363173 1 controller.go:719] ENQUEUE new ReconcileCHI cmd=add for clickhouse/restore
I1015 09:37:55.362075 1 worker-deleter.go:765] deleteCHI():CHI:clickhouse/restore:will delete CR with all resources: clickhouse/restore
Recovery at 10:02:56Z. We removed the CHI finalizer by patch, deleted the chi-storage-restore ConfigMap, and recreated the CHI with zookeeper.nodes. The CHI then completed in about 90 s. The CHK kept looping:
I1015 10:02:56.009796 1 controller.go:719] ENQUEUE new ReconcileCHI cmd=add for clickhouse/restore
I1015 10:02:56.056575 1 worker.go:602] createTemplatedCR():CHI:clickhouse/restore:CR has NO ancestor, use empty base for reconcile. CR: clickhouse/restore
I1015 10:03:01.653536 1 worker-service.go:217] createService():CHI:clickhouse/restore:OK Create Service: clickhouse/chi-restore-swaap-0-0
I1015 10:04:27.708233 1 worker-reconciler-chi.go:239] CHI:clickhouse/restore:IPs of the CR clickhouse/restore: len: 1 [10.50.77.65]
Code path (release-0.27.4)
pkg/controller/chi/controller-chk-watcher.goonCHKUpdatefires on every CHK update whosestatus.statusmoves toCompleted.enqueueDependentCHIslists CHIs, keeps those whosezookeeper.keepermatches the CHK, and callsshouldReconcileOnKeeperUpdate.shouldReconcileOnKeeperUpdatereturns true whencr.GetAncestorT()is nil ("No NormalizedCRCompleted ... cannot diff, enqueueing"). A CHI that has never completed always hits this branch.- It then enqueues
cmd_queue.NewReconcileCHI(cmd_queue.ReconcileAdd, nil, chi). The comment saysReconcileAddis used becauseprepareCHIAddreturns true unconditionally.
cmd/operator/app/thread_keeper.go- The CHK controller is built with
For(&api.ClickHouseKeeperInstallation{}, builder.WithPredicates(keeperPredicate()))andOwns(&apps.StatefulSet{}). keeperPredicate().UpdateFuncreturns true for every update. It does not filter on generation. Status writes therefore re-enqueue the CHK.
- The CHK controller is built with
pkg/controller/chk/controller.gocallsw.reconcileCR(ctx, nil, new)withold == nil, soisGenerationTheSame(old, new)cannot short-circuit.pkg/controller/chk/worker-reconciler-chk.goreconcileCRcontinues whennew.HasReconcileWork()is true. In our logs this was true on every cycle, with an empty action plan.- Each cycle calls
markReconcileStart, per-hostupdateCRObjectStatus, andfinalizeReconcileAndMarkCompleted. Each of those is a CHK update event, so the status round trip feeds the next reconcile.
pkg/controller/chi/controller-keeper-resolver.go:82is where the cancelled CHI reconcile surfaces (Keeper replica discovery failed ... context canceled).
We could not tell from logs why HasReconcileWork() stays true for this CHK and not for our production CHK in the same namespace. Both run the same operator and chart. Differences: the looping CHK has one replica, and it was created in the same apply as its CHI.
Expected behaviour
- A CHK with an empty action plan should log
No reconcile work - abort reconcileand stop. It should not reachCompleted92 times a minute. - One CHK completion should trigger at most one CHI reconcile, and that reconcile should be allowed to finish. The watcher should not enqueue a new
addwhile a reconcile for the same CHI is still running, or it should coalesce. kubectl delete chishould complete even while the watcher is active.
Workaround
Do not reference the CHK from the CHI. Point the CHI at the CHK client Service instead:
spec:
configuration:
zookeeper:
nodes:
- host: keeper-restore
port: 2181
With this the CHI is no longer a "dependent CHI", the watcher skips it, and the CHI completes. The CHK loop itself continues until the CHK is deleted.
To unblock a stuck delete: patch the CHI finalizers to [], delete the chi-storage-<name> ConfigMap, then delete the CHI.
Secondary: CRD lookup needs cluster-scoped RBAC
With the chart's namespaced RBAC, every CHI and CHK delete logs this and continues:
E1015 11:26:32.520397 1 worker-deleter.go:764] deleteCHI():CHI:clickhouse/restore:unable to get CRD, got error: customresourcedefinitions.apiextensions.k8s.io "clickhouseinstallations.clickhouse.altinity.com" is forbidden: User "system:serviceaccount:clickhouse:clickhouse-operator" cannot get resource "customresourcedefinitions" in API group "apiextensions.k8s.io" at the cluster scope
E1015 11:26:32.792197 1 worker-deleter.go:277] deleteCHK():CHK:clickhouse/restore:unable to get CRD, got error: customresourcedefinitions.apiextensions.k8s.io "clickhousekeeperinstallations.clickhouse-keeper.altinity.com" is forbidden: User "system:serviceaccount:clickhouse:clickhouse-operator" cannot get resource "customresourcedefinitions" in API group "apiextensions.k8s.io" at the cluster scope
customresourcedefinitions is cluster-scoped, so a namespaced Role can never grant it. We added a ClusterRole with get on customresourcedefinitions. The chart could ship that ClusterRole when rbac.namespaceScoped is set, or the deleter could treat the forbidden error as "CRD present".
Related issues
- #1913 Race condition: deploying CHI after Keeper leaves CHI unconnected to Keeper/replication (closed). Same CHI and CHK ordering, different symptom.
- #2035 ClickHouseKeeperInstallation stuck at status InProgress despite healthy 3-node quorum (open).
No release after 0.27.4 exists as of 2026-10-16. pkg/controller/chi/controller-chk-watcher.go on master is byte-identical to the 0.27.4 tag (blob 5c9100df). No merged PR since 2026-09-24 mentions the keeper watcher or onKeeperResourceUpdate.
- Dominant language
- Go
- Stars
- 2.6k
- Forks
- 579
- Avg merge
- 5d 22h
- Merged PRs (30d)
- 5
Getting set up
- Ships a Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from Altinity/clickhouse-operator
-
Counter metrics are missing the conventional _total suffixPossibly taken A pull request linked to this issue is open or already merged. Open
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Altinity/clickhouse-operator#2093 · 1 comment ·
Maintainers usually reply within 1 day
-
Recover reconciles from recoverAbortedReconcileOnPodReady ignores readiness of service selector labelsPossibly taken @Tyagiquamar claimed this today. Open
Difficulty 4/5 3-5 days Newbie friendliness 35/100
Altinity/clickhouse-operator#2106 ·
Maintainers usually reply within 1 day
-
Difficulty 5/5 Over a week Newbie friendliness 18/100
Altinity/clickhouse-operator#2105 ·
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 35/100
Altinity/clickhouse-operator#2098 ·
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 35/100
Altinity/clickhouse-operator#2096 ·
Maintainers usually reply within 1 day
All issues in Altinity/clickhouse-operator
Similar issues
-
[Bug]: message-level cache_control on chat messages is dropped, so Anthropic prompt caching never triggersPossibly taken @Atharva-Kanherkar claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 80/100
Maintainers usually reply within 1 day
-
documentation good first issue help wanted
Difficulty 2/5 1-3 hours Newbie friendliness 90/100
eugenioenko/ttt#1000 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Billionmail/BillionMail#389 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
Gentleman-Programming/gentle-ai#5603 ·
Maintainers usually reply within 1 day
-
good first issue help wanted v3
Difficulty 2/5 1-3 hours Newbie friendliness 77/100
oras-project/oras-go#1621 ·
Maintainers usually reply within 1 day