Process events instantly and consistently, stop skipping the events due to "batching"#844
Merged
Process events instantly and consistently, stop skipping the events due to "batching"#844
Conversation
|
This pull request introduces 3 alerts when merging e856f7d into c1745b7 - view on LGTM.com new alerts:
|
e856f7d to
051496e
Compare
|
This pull request introduces 3 alerts when merging 051496e into c1745b7 - view on LGTM.com new alerts:
|
|
Hi @nolar, could you please give a short update on this PR? Is it still on track? Cheers, |
051496e to
1e66e5f
Compare
|
This pull request introduces 1 alert when merging 1e66e5f into 7b45690 - view on LGTM.com new alerts:
|
1e66e5f to
62d3930
Compare
62d3930 to
0a36b8d
Compare
7bf795f to
f27a1e4
Compare
Previously, we used only the status stanza for the reconstruction of the patched body. This was entirely incorrect — besides the patched stanzas, also the metadata changes, such as the `resourceVersion` and other fields. This was not a problem as the reconstructed body was not used for anything useful except for some inconsistency checking (whether the patch was fully applied or not). This becomes a problem when we start tracking the `resourceVersion` — we need the latest patched version applied, which means the status patch. The new `resourceVersion` should not be ignored. If we use the `resourceVersion` from the body patch, we track the consistency until that patch arrives and start calling the handlers, despite their status arrives a little bit later from the status patch. Signed-off-by: Sergey Vasilyev <nolar@nolar.info>
f27a1e4 to
11555c9
Compare
Signed-off-by: Sergey Vasilyev <nolar@nolar.info>
Rationale: Previously (before the change), we picked ALL the events from the backlog within the batch window, so we knew that the queue is empty, and that we are processing the very last event (the very concept of batching). As such, we were safe to relieve the stream pressure (clear the flag unconditionally). Now (after the change), we process the events one by one to ensure the low-level handlers never skip, so for every next event, there could be a few more waiting in the backlog. We must get back to them asap, without any sleeps. The only way to ensure that the processor does not sleep, is to not relieve the stream pressure (not clear the flag), possibly even trigger the pressure (raise the flag). This will automatically wake up the sleeps with the full time "unslept" (i.e., actually sleep for zero time), and will mark the deemed/implied consistency as not achieved, thus skipping the higher-level state-tracking handlers and getting back to the backlog consumption. This is functionally equivalent to the new events truly arriving to the backlog immediately after the stream pressure is relieved (the flag cleared) — except that they are already in the backlog, and we know this. Only the very last event in the backlog will relieve the stream pressure, thus letting the processor sleep until the deemed/implied consistency (the only sleep it needs). Signed-off-by: Sergey Vasilyev <nolar@nolar.info>
Previously, both the low-level and high-level handlers were skipped if there was a newer event in the backlog. This was fine with the concept of batching, where only the latest state was processed. But that broke the promise of seeing ALL the events in the low-level handlers (some intermediate events were skipped). Now, we nevertheless process the pending event — even if we know there is another newer event waiting in the backlog. This ensures the low-level handlers and daemons are started as if there was no indexing pause. The newer event will be processed as if arrived a bit later. This change has no effect if indexing is absent — there is simply no such situation when we have the old event & a newer event. Signed-off-by: Sergey Vasilyev <nolar@nolar.info>
Signed-off-by: Sergey Vasilyev <nolar@nolar.info>
There is no mo batching. Signed-off-by: Sergey Vasilyev <nolar@nolar.info>
11555c9 to
9f2c3fa
Compare
Owner
Author
|
So far, the PR is ready, all tests are added and green, all critical aspects are explored manually (with artifically injected delays) and work as expected. I am ready to click "merge" & release it — but am a bit afraid. It would be good to test it somewhere in a realistic environment (from git or maybe as an release candidate version). |
This was referenced Jan 29, 2026
mcp-ci-bot
pushed a commit
to Mirantis/rockoon
that referenced
this pull request
Feb 2, 2026
latest kopf 1.42.0 is potentially able to resolve a slew of bugs related to controller missing events, see nolar/kopf#844 Related-Issue: PRODX-56742 PRODX-57473 Change-Id: Ia535929623d710ab20975b8030d940eb5179780b
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TL;DR: Process the events/changes asap, with no artificial delays (even 0.1s), stop skipping some of the events under high load, prevent double-execution if 3rd parties also patch the resources (including non-Kopf-initiated patches from the handlers).
Background
A long time ago, at the dawn of Kopf (5a0b2da, #42, #43), the arrived events for a resource objects were packed into "batches". Only the last event that arrived within the 0.1-second window was processed, all preceding events were ignored.
Originally, it was made to address an issue in the official
kubernetesclient (which was later replaced bypykube-ng, which in turn was later replaced by raw HTTP queries via aiohttp).Besides, though not documented, this short time window ensured consistency in some rare cases when a resource was patched by 3rd parties while being processed by Kopf: once Kopf performed its patch, it instantly got both events from the 3rd party and from itself, and only processed the latest state with its own annotations.
Problems
The time-based approach to consistency led to several negative effects noticeable on slow networks between the operator and apiservers (e.g. when operators are executed not in the cluster) or under high load (e.g. with too many resources or too many modifications of several resources).
@on.eventhandlers were not executed for valuable but intermediate events — because they were packed into batches and discarded in favour of the latest event only.These effects were reported and investigated in #729, also directly or indirectly mentioned in #718, #732, #784 (comment). (This PR supersedes and closes #829.)
Besides, code-wise:
Double-processing
In the mentioned cases (slow networks and/or high load), the version patched by Kopf could arrive later than 0.1 seconds after the version patched by the 3rd party. As a result, the batch was not formed and these events were processed separately. In turn, since the intermediate state of the 3rd-party version did not contain Kopf's annotations about the successful processing of the resource, the handlers were re-executed (double-executed). And only after Kopf's patched version arrived, the operator went idle as expected.
Here is how it happened on the timeline, visually, with an artificial delay of 3 seconds:
Solution
The proposed solution introduces a wait for consistency of the resource after it is patched: since the
PATCHoperation returns the patched resource, we can get its resource version and expect this version in the watch-stream. All states arrived before the expected version are considered inconsistent and thus are not processed at least for the high-level state-dependent handlers.This is how it looks on the timeline with the same artificial delay of 3 seconds:
In case the expected version does not arrive within some reasonable time window (5-10 seconds), assume that it will not arrive at all and reset the consistency waiting as if the consistency is reached (even if not). This is a rare case, mostly impossible, and is needed only as a safe-guard: it is better to double-process the resource and cause side-effects than to cease its processing forever.
Time-based batching is removed completely as outdated and not adding any benefit.
User effects
As a result of this fix, all mentioned problems are addressed:
@on.event(), indexed and passed to daemons.TODOs