test: sample for virtual threads - #3554

Closed
csviri wants to merge 21 commits into
operator-framework:nextfrom
csviri:virtual-threads
Closed

test: sample for virtual threads#3554
csviri wants to merge 21 commits into
operator-framework:nextfrom
csviri:virtual-threads

Conversation

@csviri

@csviricsviri commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Adds sample (serves as a smoke test) for virtual threads setup of JOSDK.

TODO:

  • check logging / thread local handling

csviriand others added 19 commits July 22, 2026 15:59
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
…us (operator-framework#3480)
Add integration tests that showcases handling explicit state in status. Both for simple managed/secondary resources and using a dependent resource.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* feat: pool informers so controllers and event sources can share them
Every InformerEventSource used to create its own SharedIndexInformer, so an
operator whose controllers all watch the same secondary type - ConfigMap and
Secret being the usual suspects - opened one watch connection and kept one cache
per controller for the very same resources.
Informers are now handed out by an InformerPool obtained from the
ConfigurationService, keyed by an InformerClassifier. Event sources whose
classifiers are equal are backed by one informer; the pool reference counts its
users and stops the informer once the last one releases it. The classifier is
made up of everything that decides what an informer watches and how: the
KubernetesClient instance (compared by identity, since two clients for the same
API server may still differ in credentials, impersonation or TLS material), the
resource class or the group/version/kind for generic resources, the namespace,
the label, field and shard selectors, and the item store.
Two components are deliberately not part of that identity. The informerListLimit
is excluded, so event sources that disagree only on it still share an informer,
keeping the limit of whichever one created it and logging a warning. Indexers are
excluded because they can be added to a running informer: they are registered
under a name qualified with the controller and event source that added them, so
index names stay private to an event source while callers keep using their own
names, and they are removed again when that event source releases the informer.
Two strategies ship: DefaultInformerPool shares as described and is the default,
NonSharingInformerPool creates a dedicated informer per event source for anyone
wanting to opt out. Either is selected with
ConfigurationServiceOverrider#withInformerPool, and a custom strategy extends
AbstractInformerPool, which already creates the informers from a classifier,
starts them and waits for their caches to sync, leaving the subclass only the
question of whether and when an informer is shared.
Consequently informer creation and startup moved out of InformerWrapper and
InformerManager into the pool, InformerManager acquires and releases informers
instead of owning them, and it removes its own event handler and indexers from an
informer that keeps running for others. An event source registered dynamically
against an already running shared informer needs no special handling: the client
replays the cache contents to a newly added handler.
Also in support of the above: ConfigurationService#informerPool, an
InformerEventSource constructor that no longer needs an EventSourceContext (the
one taking it is deprecated), the resource group/version/kind on
InformerConfiguration, equality and toString on FieldSelector, and equality of
GroupVersionKindPlural made consistent with its hashCode so that an unspecified
plural no longer splits informers.
The pooling itself is production ready; the configuration API around it is marked
experimental and may still change. Covered by unit tests for the pools, the
classifier, the wrapper and the manager, and by integration tests for sharing,
dynamic registration and de-registration that each run against both strategies.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
…-framework#3511)
Bumps [org.apache.maven.plugins:maven-jar-plugin](https://github.com/apache/maven-jar-plugin) from 3.5.0 to 3.5.1.
- [Release notes](https://github.com/apache/maven-jar-plugin/releases)
- [Commits](apache/maven-jar-plugin@maven-jar-plugin-3.5.0...maven-jar-plugin-3.5.1)
---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-jar-plugin
dependency-version: 3.5.1
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…-framework#3510)
Bumps `openrewrite.version` from 8.87.5 to 8.87.7.
Updates `org.openrewrite:rewrite-java` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
Updates `org.openrewrite:rewrite-maven` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
Updates `org.openrewrite:rewrite-test` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
Updates `org.openrewrite:rewrite-java-17` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
---
updated-dependencies:
- dependency-name: org.openrewrite:rewrite-java
dependency-version: 8.87.7
dependency-type: direct:production
update-type: version-update:semver-patch
- dependency-name: org.openrewrite:rewrite-maven
dependency-version: 8.87.7
dependency-type: direct:production
update-type: version-update:semver-patch
- dependency-name: org.openrewrite:rewrite-test
dependency-version: 8.87.7
dependency-type: direct:development
update-type: version-update:semver-patch
- dependency-name: org.openrewrite:rewrite-java-17
dependency-version: 8.87.7
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…8.0 to 3.9.0 (operator-framework#3515)
Signed-off-by: dependabot[bot] <support@github.com>
…erator-framework#3514)
Fabric8's no-context patch() fetches the resource from the server and
computes an RFC 6902 JSON Patch diff, which removes the fields omitted
from a partial resource (including the whole spec). Pass an explicit
JSON_MERGE PatchContext so the non-status merge patch operations really
send application/merge-patch+json.
…ator E2E test (operator-framework#3513)
Adds a Maven-compiled Kotlin test source set to operator-framework-core
and a smoke test verifying that a checked (non-RuntimeException) Exception
thrown from a Kotlin DependentResource is properly caught and reported by
the workflow executor, so that retries are triggered as expected.
Kotlin does not have checked exceptions, so Kotlin code can throw a
checked Exception from an overridden method without declaring it, even
though the Java DependentResource#reconcile signature does not declare
`throws Exception`. Before operator-framework#2965 this exception would not have been
caught by NodeExecutor, since it only handled RuntimeException, silently
swallowing the error and preventing retries.
Closesoperator-framework#2967
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* fix: close class brace in Kotlin test; add Kotlin sample operator E2E
Fixes a missing closing brace left in KotlinCheckedExceptionDependentResourceTest.kt.
Adds a new sample-operators/kotlin-operator module: a minimalist Kotlin
operator (ConfigMapCopyReconciler) that copies a value from a custom
resource's spec into a ConfigMap, with a dual-mode (local/remote) E2E
test mirroring the other samples. This verifies deserialization via the
fabric8 client and the full reconciliation runtime when both the CR and
reconciler are implemented in Kotlin, addressing the maintainer's request
for broader coverage than the existing unit-level smoke test.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This needed to be fixed after rebase.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
)
This is expected for a pool to set. Event if the underlying implementation would set it, this is the correct way to handle it.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
…work#3547)
AbstractWorkflowExecutor called Workflow#getDependentResourcesByName purely to
read its size. That allocates a HashMap and walks every node to collect the
dependent resources, then discards the map. The executor is constructed on
every reconcile and cleanup of a workflow-based reconciler, so use the existing
Workflow#size instead.
…perator-framework#3550)
ResourceOperations#desiredForJsonPatch takes an Options argument that it never
reads - it only clones the actual resource and applies the operator. All six
jsonPatch call sites thread the value in for nothing, which suggests the method
still honours the option. Remove the parameter.
…ator-framework#3544)
No behavior change; each site is replaced by a helper that already exists.
- PrimaryUpdateAndCacheUtils#compareResourceVersions (and its private
validateResourceVersion) duplicated the entire algorithm of
ReconcilerUtilsInternal#validateAndCompareResourceVersions: the length-first
compare, the empty check, the leading-zero check and the same exception
messages. It had no production caller, so the copy could silently drift from
the version all production paths use. Delegate instead.
- addFinalizerWithSSA builds its bare SSA skeleton with
HasMetadata#initNameAndNamespaceFrom, which ResourceOperations already uses
for the same purpose and which is Namespaced-aware.
- AbstractInformerPool formats the informer identifier with
ReconcilerUtilsInternal#getResourceTypeNameWithVersion instead of
concatenating the resource name and version by hand.
- EventFilterWindow uses ExtendedResourceEvent#getResourceVersion, which had
no callers even though it is exactly the expression used here.
- LocallyRunOperatorExtension instantiates reconcilers with Utils#instantiate,
so it also supports non-public no-arg constructors and reports the failing
class instead of wrapping in a bare RuntimeException.
…perator-framework#3543)
The event count is increased at the beginning of the reconciliation, thus
waiting for it released the test into the middle of a reconciliation that
was still about to remove the finalizer. The subsequent update then raced
with that removal and failed with a conflict.
Wait for the finalizer removal to actually land, and retry the (optimistically
locked) update with a fresh read on conflict.
…rk#3536)
Add an opt-in, experimental detectApiVersionChange option on
@KubernetesDependent that records the API version the operator applies
in the javaoperatorsdk.io/last-applied-api-version annotation. The
regular matcher then detects a mismatch when that marker differs from
(or is missing relative to) the currently applied API version, causing
a one-time update without triggering repeated reconciliations once the
resource is up-to-date. Disabled by default, so existing behavior and
matching are unaffected unless explicitly enabled.
Guard against a null or immutable annotations map (e.g. Map.of(...))
on the desired resource when detectApiVersionChange is enabled, since
writing the last-applied-api-version marker (and the pre-existing
previous-annotation bookkeeping that runs alongside it) requires a
mutable map. Also rename a misleadingly-named test helper and replace
a no-op assertion on a primitive boolean with a concrete expectation.
Addresses Copilot review feedback on PR operator-framework#3536.
Add DetectApiVersionChangeIT covering the end-to-end scenario: a
ConfigMap dependent resource configured with detectApiVersionChange
is marked with the current API version on creation (without
triggering an update), and a stale marker annotation left on the
actual resource (simulating an older operator/CRD version) is
detected and corrected with exactly one update, after which no
further reconciliation loop occurs.
Signed-off-by: hej090224 <fc49854985@gmail.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
…ator-framework#3548)
InformerManager is generic over C extends Informable, but getTargetClient
type-tested for InformerEventSourceConfiguration to find out whether a specific
(e.g. remote cluster) client was configured. Informable has two implementors
and only one could answer the question, so a third configuration type wanting
its own client would be ignored silently rather than failing to compile.
Move the default getKubernetesClient() up from InformerEventSourceConfiguration
to Informable and let InformerManager ask the configuration directly. The
default still returns Optional.empty(), so existing implementations are
unaffected. As a side effect the ConfigurationService client is now only
created when no specific client is configured, instead of being created and
then discarded.
…rk#3546)
- GenericKubernetesResourceMatcher allocated the path-prefix lists
(List.of(SPEC), List.of(METADATA), the labels/annotations pair, List.of(STATUS))
once per JSON-diff node while matching, and nodeIsChildOf built a stream per
call. Both run for every node of every match, so hoist the lists to constants
and use an indexed loop.
- SSABasedGenericKubernetesResourceMatcher#sanitizeState nested the StatefulSet
volume-claim-template handling four levels deep inside the type ladder;
extract it into sanitizeVolumeClaimTemplates so the ladder reads as one
dispatch per resource kind.
@openshift-ciopenshift-ciBot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 11, 2026
@coderabbitai

coderabbitaiBot commented Aug 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e6fdac0d-13b6-4df3-af4f-3f9ff690efcf

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@openshift-ciopenshift-ciBot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 11, 2026
@csviri
csviri changed the base branch from main to nextAugust 11, 2026 15:49
Requires Java 21, therefore the module is only part of the build when the
JDK used is at least version 21.
Configures both the framework executors (reconciliation and workflow) and
the Kubernetes client task executor with a virtual thread per task
executor.
@openshift-ciopenshift-ciBot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 11, 2026
@csviricsviri linked an issue Aug 11, 2026 that may be closed by this pull request
@csviricsviri changed the title Sample for virtual threadsimprove: sample for virtual threadsAug 11, 2026
@csviri
csviri marked this pull request as ready for review August 12, 2026 06:49
CopilotAI lite review requested due to automatic review settings August 12, 2026 06:49
@openshift-ciopenshift-ciBot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 12, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new sample operator demonstrating how to run JOSDK reconciliations and Fabric8 client async callbacks on Java virtual threads (Java 21+), along with documentation and CI updates so the sample is built/tested appropriately.

Changes:

  • Introduces a new sample-operators/virtual-threads Maven module (Java 21+) with a reconciler, CRD types, logging config, and an E2E test.
  • Adds a JDK-activated Maven profile in sample-operators/pom.xml to include the sample only on JDK 21+.
  • Updates docs and the E2E GitHub Actions workflow to reference and test the new sample.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
sample-operators/virtual-threads/src/test/resources/log4j2.xmlAdds test logging configuration for the virtual threads sample.
sample-operators/virtual-threads/src/test/java/io/javaoperatorsdk/operator/sample/VirtualThreadsOperatorE2E.javaAdds an E2E test validating parallel reconciliations and virtual-thread execution.
sample-operators/virtual-threads/src/main/resources/log4j2.xmlAdds runtime logging configuration for the sample operator.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsStatus.javaAdds CR status fields used by the sample to report observed value and virtual-thread usage.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsSpec.javaAdds CR spec schema for the sample.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsReconciler.javaImplements a reconciler that simulates blocking work and patches status.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsOperator.javaAdds the sample operator entry point wiring virtual-thread executors and client config.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsCustomResource.javaDefines the sample CustomResource type and group/version metadata.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreads.javaCentralizes virtual-thread configuration for both framework executors and Fabric8 client task executor.
sample-operators/virtual-threads/README.mdDocuments the sample, Java 21+ requirement, and how to run it.
sample-operators/virtual-threads/pom.xmlIntroduces the new Java 21+ sample module build and dependencies.
sample-operators/virtual-threads/k8s/virtual-threads-custom-resource.yamlAdds a sample CR manifest for running the operator manually.
sample-operators/virtual-threads/k8s/operator.yamlAdds a deployment + RBAC manifest used by remote E2E mode.
sample-operators/pom.xmlAdds a JDK>=21-activated profile to include the new sample module.
docs/content/en/docs/getting-started/bootstrap-and-samples.mdAdds the virtual-threads sample to the list of available samples.
docs/content/en/docs/documentation/operations/configuration.mdDocuments how to run JOSDK on virtual threads (framework + client executor).
.github/workflows/e2e-test.ymlAdds the new sample module to the E2E workflow matrix (running on JDK 25).

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment threaddocs/content/en/docs/getting-started/bootstrap-and-samples.md Outdated
Comment on lines +66 to +70
@Override
public void onClose(Executor executor) {
((ExecutorService) executor).shutdownNow();
}
}
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@csviri
csviri marked this pull request as draft August 12, 2026 08:54
@openshift-ciopenshift-ciBot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 12, 2026
Comment on lines +30 to +31
can be replaced with virtual threads, so that a reconciliation waiting on a remote call does not
occupy a platform thread:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
can be replaced with virtual threads, so that a reconciliation waiting on a remote call does not
occupy a platform thread:
can be replaced with virtual threads.

Each virtual thread always runs on platform thread in the background.

@csviricsviri changed the title improve: sample for virtual threadstest: sample for virtual threadsAug 13, 2026
@openshift-ciopenshift-ciBot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 28, 2026
@openshift-ci

Copy link
Copy Markdown

PR needs rebase.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@csviri
csviriforce-pushed the next branch 2 times, most recently from 5ccac4d to 478199eCompareSeptember 3, 2026 11:40
@csviri
csviri deleted the branch operator-framework:nextSeptember 3, 2026 13:15
@csviricsviri closed this Sep 3, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge/work-in-progressIndicates that a PR should not merge because it is a work in progress.needs-rebaseIndicates a PR cannot be merged because it has merge conflicts with HEAD.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for Virtual Threads

4 participants

@csviri@xstefank@exijn
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content

test: sample for virtual threads - #3554

Closed
csviri wants to merge 21 commits into
operator-framework:nextfrom
csviri:virtual-threads
Closed

test: sample for virtual threads#3554
csviri wants to merge 21 commits into
operator-framework:nextfrom
csviri:virtual-threads

Conversation

@csviri

@csviricsviri commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Adds sample (serves as a smoke test) for virtual threads setup of JOSDK.

TODO:

  • check logging / thread local handling

csviriand others added 19 commits July 22, 2026 15:59
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
…us (operator-framework#3480)
Add integration tests that showcases handling explicit state in status. Both for simple managed/secondary resources and using a dependent resource.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* feat: pool informers so controllers and event sources can share them
Every InformerEventSource used to create its own SharedIndexInformer, so an
operator whose controllers all watch the same secondary type - ConfigMap and
Secret being the usual suspects - opened one watch connection and kept one cache
per controller for the very same resources.
Informers are now handed out by an InformerPool obtained from the
ConfigurationService, keyed by an InformerClassifier. Event sources whose
classifiers are equal are backed by one informer; the pool reference counts its
users and stops the informer once the last one releases it. The classifier is
made up of everything that decides what an informer watches and how: the
KubernetesClient instance (compared by identity, since two clients for the same
API server may still differ in credentials, impersonation or TLS material), the
resource class or the group/version/kind for generic resources, the namespace,
the label, field and shard selectors, and the item store.
Two components are deliberately not part of that identity. The informerListLimit
is excluded, so event sources that disagree only on it still share an informer,
keeping the limit of whichever one created it and logging a warning. Indexers are
excluded because they can be added to a running informer: they are registered
under a name qualified with the controller and event source that added them, so
index names stay private to an event source while callers keep using their own
names, and they are removed again when that event source releases the informer.
Two strategies ship: DefaultInformerPool shares as described and is the default,
NonSharingInformerPool creates a dedicated informer per event source for anyone
wanting to opt out. Either is selected with
ConfigurationServiceOverrider#withInformerPool, and a custom strategy extends
AbstractInformerPool, which already creates the informers from a classifier,
starts them and waits for their caches to sync, leaving the subclass only the
question of whether and when an informer is shared.
Consequently informer creation and startup moved out of InformerWrapper and
InformerManager into the pool, InformerManager acquires and releases informers
instead of owning them, and it removes its own event handler and indexers from an
informer that keeps running for others. An event source registered dynamically
against an already running shared informer needs no special handling: the client
replays the cache contents to a newly added handler.
Also in support of the above: ConfigurationService#informerPool, an
InformerEventSource constructor that no longer needs an EventSourceContext (the
one taking it is deprecated), the resource group/version/kind on
InformerConfiguration, equality and toString on FieldSelector, and equality of
GroupVersionKindPlural made consistent with its hashCode so that an unspecified
plural no longer splits informers.
The pooling itself is production ready; the configuration API around it is marked
experimental and may still change. Covered by unit tests for the pools, the
classifier, the wrapper and the manager, and by integration tests for sharing,
dynamic registration and de-registration that each run against both strategies.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
…-framework#3511)
Bumps [org.apache.maven.plugins:maven-jar-plugin](https://github.com/apache/maven-jar-plugin) from 3.5.0 to 3.5.1.
- [Release notes](https://github.com/apache/maven-jar-plugin/releases)
- [Commits](apache/maven-jar-plugin@maven-jar-plugin-3.5.0...maven-jar-plugin-3.5.1)
---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-jar-plugin
dependency-version: 3.5.1
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…-framework#3510)
Bumps `openrewrite.version` from 8.87.5 to 8.87.7.
Updates `org.openrewrite:rewrite-java` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
Updates `org.openrewrite:rewrite-maven` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
Updates `org.openrewrite:rewrite-test` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
Updates `org.openrewrite:rewrite-java-17` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
---
updated-dependencies:
- dependency-name: org.openrewrite:rewrite-java
dependency-version: 8.87.7
dependency-type: direct:production
update-type: version-update:semver-patch
- dependency-name: org.openrewrite:rewrite-maven
dependency-version: 8.87.7
dependency-type: direct:production
update-type: version-update:semver-patch
- dependency-name: org.openrewrite:rewrite-test
dependency-version: 8.87.7
dependency-type: direct:development
update-type: version-update:semver-patch
- dependency-name: org.openrewrite:rewrite-java-17
dependency-version: 8.87.7
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…8.0 to 3.9.0 (operator-framework#3515)
Signed-off-by: dependabot[bot] <support@github.com>
…erator-framework#3514)
Fabric8's no-context patch() fetches the resource from the server and
computes an RFC 6902 JSON Patch diff, which removes the fields omitted
from a partial resource (including the whole spec). Pass an explicit
JSON_MERGE PatchContext so the non-status merge patch operations really
send application/merge-patch+json.
…ator E2E test (operator-framework#3513)
Adds a Maven-compiled Kotlin test source set to operator-framework-core
and a smoke test verifying that a checked (non-RuntimeException) Exception
thrown from a Kotlin DependentResource is properly caught and reported by
the workflow executor, so that retries are triggered as expected.
Kotlin does not have checked exceptions, so Kotlin code can throw a
checked Exception from an overridden method without declaring it, even
though the Java DependentResource#reconcile signature does not declare
`throws Exception`. Before operator-framework#2965 this exception would not have been
caught by NodeExecutor, since it only handled RuntimeException, silently
swallowing the error and preventing retries.
Closesoperator-framework#2967
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* fix: close class brace in Kotlin test; add Kotlin sample operator E2E
Fixes a missing closing brace left in KotlinCheckedExceptionDependentResourceTest.kt.
Adds a new sample-operators/kotlin-operator module: a minimalist Kotlin
operator (ConfigMapCopyReconciler) that copies a value from a custom
resource's spec into a ConfigMap, with a dual-mode (local/remote) E2E
test mirroring the other samples. This verifies deserialization via the
fabric8 client and the full reconciliation runtime when both the CR and
reconciler are implemented in Kotlin, addressing the maintainer's request
for broader coverage than the existing unit-level smoke test.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This needed to be fixed after rebase.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
)
This is expected for a pool to set. Event if the underlying implementation would set it, this is the correct way to handle it.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
…work#3547)
AbstractWorkflowExecutor called Workflow#getDependentResourcesByName purely to
read its size. That allocates a HashMap and walks every node to collect the
dependent resources, then discards the map. The executor is constructed on
every reconcile and cleanup of a workflow-based reconciler, so use the existing
Workflow#size instead.
…perator-framework#3550)
ResourceOperations#desiredForJsonPatch takes an Options argument that it never
reads - it only clones the actual resource and applies the operator. All six
jsonPatch call sites thread the value in for nothing, which suggests the method
still honours the option. Remove the parameter.
…ator-framework#3544)
No behavior change; each site is replaced by a helper that already exists.
- PrimaryUpdateAndCacheUtils#compareResourceVersions (and its private
validateResourceVersion) duplicated the entire algorithm of
ReconcilerUtilsInternal#validateAndCompareResourceVersions: the length-first
compare, the empty check, the leading-zero check and the same exception
messages. It had no production caller, so the copy could silently drift from
the version all production paths use. Delegate instead.
- addFinalizerWithSSA builds its bare SSA skeleton with
HasMetadata#initNameAndNamespaceFrom, which ResourceOperations already uses
for the same purpose and which is Namespaced-aware.
- AbstractInformerPool formats the informer identifier with
ReconcilerUtilsInternal#getResourceTypeNameWithVersion instead of
concatenating the resource name and version by hand.
- EventFilterWindow uses ExtendedResourceEvent#getResourceVersion, which had
no callers even though it is exactly the expression used here.
- LocallyRunOperatorExtension instantiates reconcilers with Utils#instantiate,
so it also supports non-public no-arg constructors and reports the failing
class instead of wrapping in a bare RuntimeException.
…perator-framework#3543)
The event count is increased at the beginning of the reconciliation, thus
waiting for it released the test into the middle of a reconciliation that
was still about to remove the finalizer. The subsequent update then raced
with that removal and failed with a conflict.
Wait for the finalizer removal to actually land, and retry the (optimistically
locked) update with a fresh read on conflict.
…rk#3536)
Add an opt-in, experimental detectApiVersionChange option on
@KubernetesDependent that records the API version the operator applies
in the javaoperatorsdk.io/last-applied-api-version annotation. The
regular matcher then detects a mismatch when that marker differs from
(or is missing relative to) the currently applied API version, causing
a one-time update without triggering repeated reconciliations once the
resource is up-to-date. Disabled by default, so existing behavior and
matching are unaffected unless explicitly enabled.
Guard against a null or immutable annotations map (e.g. Map.of(...))
on the desired resource when detectApiVersionChange is enabled, since
writing the last-applied-api-version marker (and the pre-existing
previous-annotation bookkeeping that runs alongside it) requires a
mutable map. Also rename a misleadingly-named test helper and replace
a no-op assertion on a primitive boolean with a concrete expectation.
Addresses Copilot review feedback on PR operator-framework#3536.
Add DetectApiVersionChangeIT covering the end-to-end scenario: a
ConfigMap dependent resource configured with detectApiVersionChange
is marked with the current API version on creation (without
triggering an update), and a stale marker annotation left on the
actual resource (simulating an older operator/CRD version) is
detected and corrected with exactly one update, after which no
further reconciliation loop occurs.
Signed-off-by: hej090224 <fc49854985@gmail.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
…ator-framework#3548)
InformerManager is generic over C extends Informable, but getTargetClient
type-tested for InformerEventSourceConfiguration to find out whether a specific
(e.g. remote cluster) client was configured. Informable has two implementors
and only one could answer the question, so a third configuration type wanting
its own client would be ignored silently rather than failing to compile.
Move the default getKubernetesClient() up from InformerEventSourceConfiguration
to Informable and let InformerManager ask the configuration directly. The
default still returns Optional.empty(), so existing implementations are
unaffected. As a side effect the ConfigurationService client is now only
created when no specific client is configured, instead of being created and
then discarded.
…rk#3546)
- GenericKubernetesResourceMatcher allocated the path-prefix lists
(List.of(SPEC), List.of(METADATA), the labels/annotations pair, List.of(STATUS))
once per JSON-diff node while matching, and nodeIsChildOf built a stream per
call. Both run for every node of every match, so hoist the lists to constants
and use an indexed loop.
- SSABasedGenericKubernetesResourceMatcher#sanitizeState nested the StatefulSet
volume-claim-template handling four levels deep inside the type ladder;
extract it into sanitizeVolumeClaimTemplates so the ladder reads as one
dispatch per resource kind.
@openshift-ciopenshift-ciBot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 11, 2026
@coderabbitai

coderabbitaiBot commented Aug 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e6fdac0d-13b6-4df3-af4f-3f9ff690efcf

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@openshift-ciopenshift-ciBot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 11, 2026
@csviri
csviri changed the base branch from main to nextAugust 11, 2026 15:49
Requires Java 21, therefore the module is only part of the build when the
JDK used is at least version 21.
Configures both the framework executors (reconciliation and workflow) and
the Kubernetes client task executor with a virtual thread per task
executor.
@openshift-ciopenshift-ciBot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 11, 2026
@csviricsviri linked an issue Aug 11, 2026 that may be closed by this pull request
@csviricsviri changed the title Sample for virtual threadsimprove: sample for virtual threadsAug 11, 2026
@csviri
csviri marked this pull request as ready for review August 12, 2026 06:49
CopilotAI lite review requested due to automatic review settings August 12, 2026 06:49
@openshift-ciopenshift-ciBot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 12, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new sample operator demonstrating how to run JOSDK reconciliations and Fabric8 client async callbacks on Java virtual threads (Java 21+), along with documentation and CI updates so the sample is built/tested appropriately.

Changes:

  • Introduces a new sample-operators/virtual-threads Maven module (Java 21+) with a reconciler, CRD types, logging config, and an E2E test.
  • Adds a JDK-activated Maven profile in sample-operators/pom.xml to include the sample only on JDK 21+.
  • Updates docs and the E2E GitHub Actions workflow to reference and test the new sample.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
sample-operators/virtual-threads/src/test/resources/log4j2.xmlAdds test logging configuration for the virtual threads sample.
sample-operators/virtual-threads/src/test/java/io/javaoperatorsdk/operator/sample/VirtualThreadsOperatorE2E.javaAdds an E2E test validating parallel reconciliations and virtual-thread execution.
sample-operators/virtual-threads/src/main/resources/log4j2.xmlAdds runtime logging configuration for the sample operator.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsStatus.javaAdds CR status fields used by the sample to report observed value and virtual-thread usage.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsSpec.javaAdds CR spec schema for the sample.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsReconciler.javaImplements a reconciler that simulates blocking work and patches status.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsOperator.javaAdds the sample operator entry point wiring virtual-thread executors and client config.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsCustomResource.javaDefines the sample CustomResource type and group/version metadata.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreads.javaCentralizes virtual-thread configuration for both framework executors and Fabric8 client task executor.
sample-operators/virtual-threads/README.mdDocuments the sample, Java 21+ requirement, and how to run it.
sample-operators/virtual-threads/pom.xmlIntroduces the new Java 21+ sample module build and dependencies.
sample-operators/virtual-threads/k8s/virtual-threads-custom-resource.yamlAdds a sample CR manifest for running the operator manually.
sample-operators/virtual-threads/k8s/operator.yamlAdds a deployment + RBAC manifest used by remote E2E mode.
sample-operators/pom.xmlAdds a JDK>=21-activated profile to include the new sample module.
docs/content/en/docs/getting-started/bootstrap-and-samples.mdAdds the virtual-threads sample to the list of available samples.
docs/content/en/docs/documentation/operations/configuration.mdDocuments how to run JOSDK on virtual threads (framework + client executor).
.github/workflows/e2e-test.ymlAdds the new sample module to the E2E workflow matrix (running on JDK 25).

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment threaddocs/content/en/docs/getting-started/bootstrap-and-samples.md Outdated
Comment on lines +66 to +70
@Override
public void onClose(Executor executor) {
((ExecutorService) executor).shutdownNow();
}
}
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@csviri
csviri marked this pull request as draft August 12, 2026 08:54
@openshift-ciopenshift-ciBot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 12, 2026
Comment on lines +30 to +31
can be replaced with virtual threads, so that a reconciliation waiting on a remote call does not
occupy a platform thread:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
can be replaced with virtual threads, so that a reconciliation waiting on a remote call does not
occupy a platform thread:
can be replaced with virtual threads.

Each virtual thread always runs on platform thread in the background.

@csviricsviri changed the title improve: sample for virtual threadstest: sample for virtual threadsAug 13, 2026
@openshift-ciopenshift-ciBot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 28, 2026
@openshift-ci

Copy link
Copy Markdown

PR needs rebase.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@csviri
csviriforce-pushed the next branch 2 times, most recently from 5ccac4d to 478199eCompareSeptember 3, 2026 11:40
@csviri
csviri deleted the branch operator-framework:nextSeptember 3, 2026 13:15
@csviricsviri closed this Sep 3, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge/work-in-progressIndicates that a PR should not merge because it is a work in progress.needs-rebaseIndicates a PR cannot be merged because it has merge conflicts with HEAD.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for Virtual Threads

4 participants

@csviri@xstefank@exijn
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

test: sample for virtual threads - #3554

Closed
csviri wants to merge 21 commits into
operator-framework:nextfrom
csviri:virtual-threads
Closed

test: sample for virtual threads#3554
csviri wants to merge 21 commits into
operator-framework:nextfrom
csviri:virtual-threads

Conversation

@csviri

@csviricsviri commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Adds sample (serves as a smoke test) for virtual threads setup of JOSDK.

TODO:

  • check logging / thread local handling

csviriand others added 19 commits July 22, 2026 15:59
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
…us (operator-framework#3480)
Add integration tests that showcases handling explicit state in status. Both for simple managed/secondary resources and using a dependent resource.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* feat: pool informers so controllers and event sources can share them
Every InformerEventSource used to create its own SharedIndexInformer, so an
operator whose controllers all watch the same secondary type - ConfigMap and
Secret being the usual suspects - opened one watch connection and kept one cache
per controller for the very same resources.
Informers are now handed out by an InformerPool obtained from the
ConfigurationService, keyed by an InformerClassifier. Event sources whose
classifiers are equal are backed by one informer; the pool reference counts its
users and stops the informer once the last one releases it. The classifier is
made up of everything that decides what an informer watches and how: the
KubernetesClient instance (compared by identity, since two clients for the same
API server may still differ in credentials, impersonation or TLS material), the
resource class or the group/version/kind for generic resources, the namespace,
the label, field and shard selectors, and the item store.
Two components are deliberately not part of that identity. The informerListLimit
is excluded, so event sources that disagree only on it still share an informer,
keeping the limit of whichever one created it and logging a warning. Indexers are
excluded because they can be added to a running informer: they are registered
under a name qualified with the controller and event source that added them, so
index names stay private to an event source while callers keep using their own
names, and they are removed again when that event source releases the informer.
Two strategies ship: DefaultInformerPool shares as described and is the default,
NonSharingInformerPool creates a dedicated informer per event source for anyone
wanting to opt out. Either is selected with
ConfigurationServiceOverrider#withInformerPool, and a custom strategy extends
AbstractInformerPool, which already creates the informers from a classifier,
starts them and waits for their caches to sync, leaving the subclass only the
question of whether and when an informer is shared.
Consequently informer creation and startup moved out of InformerWrapper and
InformerManager into the pool, InformerManager acquires and releases informers
instead of owning them, and it removes its own event handler and indexers from an
informer that keeps running for others. An event source registered dynamically
against an already running shared informer needs no special handling: the client
replays the cache contents to a newly added handler.
Also in support of the above: ConfigurationService#informerPool, an
InformerEventSource constructor that no longer needs an EventSourceContext (the
one taking it is deprecated), the resource group/version/kind on
InformerConfiguration, equality and toString on FieldSelector, and equality of
GroupVersionKindPlural made consistent with its hashCode so that an unspecified
plural no longer splits informers.
The pooling itself is production ready; the configuration API around it is marked
experimental and may still change. Covered by unit tests for the pools, the
classifier, the wrapper and the manager, and by integration tests for sharing,
dynamic registration and de-registration that each run against both strategies.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
…-framework#3511)
Bumps [org.apache.maven.plugins:maven-jar-plugin](https://github.com/apache/maven-jar-plugin) from 3.5.0 to 3.5.1.
- [Release notes](https://github.com/apache/maven-jar-plugin/releases)
- [Commits](apache/maven-jar-plugin@maven-jar-plugin-3.5.0...maven-jar-plugin-3.5.1)
---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-jar-plugin
dependency-version: 3.5.1
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…-framework#3510)
Bumps `openrewrite.version` from 8.87.5 to 8.87.7.
Updates `org.openrewrite:rewrite-java` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
Updates `org.openrewrite:rewrite-maven` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
Updates `org.openrewrite:rewrite-test` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
Updates `org.openrewrite:rewrite-java-17` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
---
updated-dependencies:
- dependency-name: org.openrewrite:rewrite-java
dependency-version: 8.87.7
dependency-type: direct:production
update-type: version-update:semver-patch
- dependency-name: org.openrewrite:rewrite-maven
dependency-version: 8.87.7
dependency-type: direct:production
update-type: version-update:semver-patch
- dependency-name: org.openrewrite:rewrite-test
dependency-version: 8.87.7
dependency-type: direct:development
update-type: version-update:semver-patch
- dependency-name: org.openrewrite:rewrite-java-17
dependency-version: 8.87.7
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…8.0 to 3.9.0 (operator-framework#3515)
Signed-off-by: dependabot[bot] <support@github.com>
…erator-framework#3514)
Fabric8's no-context patch() fetches the resource from the server and
computes an RFC 6902 JSON Patch diff, which removes the fields omitted
from a partial resource (including the whole spec). Pass an explicit
JSON_MERGE PatchContext so the non-status merge patch operations really
send application/merge-patch+json.
…ator E2E test (operator-framework#3513)
Adds a Maven-compiled Kotlin test source set to operator-framework-core
and a smoke test verifying that a checked (non-RuntimeException) Exception
thrown from a Kotlin DependentResource is properly caught and reported by
the workflow executor, so that retries are triggered as expected.
Kotlin does not have checked exceptions, so Kotlin code can throw a
checked Exception from an overridden method without declaring it, even
though the Java DependentResource#reconcile signature does not declare
`throws Exception`. Before operator-framework#2965 this exception would not have been
caught by NodeExecutor, since it only handled RuntimeException, silently
swallowing the error and preventing retries.
Closesoperator-framework#2967
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* fix: close class brace in Kotlin test; add Kotlin sample operator E2E
Fixes a missing closing brace left in KotlinCheckedExceptionDependentResourceTest.kt.
Adds a new sample-operators/kotlin-operator module: a minimalist Kotlin
operator (ConfigMapCopyReconciler) that copies a value from a custom
resource's spec into a ConfigMap, with a dual-mode (local/remote) E2E
test mirroring the other samples. This verifies deserialization via the
fabric8 client and the full reconciliation runtime when both the CR and
reconciler are implemented in Kotlin, addressing the maintainer's request
for broader coverage than the existing unit-level smoke test.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This needed to be fixed after rebase.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
)
This is expected for a pool to set. Event if the underlying implementation would set it, this is the correct way to handle it.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
…work#3547)
AbstractWorkflowExecutor called Workflow#getDependentResourcesByName purely to
read its size. That allocates a HashMap and walks every node to collect the
dependent resources, then discards the map. The executor is constructed on
every reconcile and cleanup of a workflow-based reconciler, so use the existing
Workflow#size instead.
…perator-framework#3550)
ResourceOperations#desiredForJsonPatch takes an Options argument that it never
reads - it only clones the actual resource and applies the operator. All six
jsonPatch call sites thread the value in for nothing, which suggests the method
still honours the option. Remove the parameter.
…ator-framework#3544)
No behavior change; each site is replaced by a helper that already exists.
- PrimaryUpdateAndCacheUtils#compareResourceVersions (and its private
validateResourceVersion) duplicated the entire algorithm of
ReconcilerUtilsInternal#validateAndCompareResourceVersions: the length-first
compare, the empty check, the leading-zero check and the same exception
messages. It had no production caller, so the copy could silently drift from
the version all production paths use. Delegate instead.
- addFinalizerWithSSA builds its bare SSA skeleton with
HasMetadata#initNameAndNamespaceFrom, which ResourceOperations already uses
for the same purpose and which is Namespaced-aware.
- AbstractInformerPool formats the informer identifier with
ReconcilerUtilsInternal#getResourceTypeNameWithVersion instead of
concatenating the resource name and version by hand.
- EventFilterWindow uses ExtendedResourceEvent#getResourceVersion, which had
no callers even though it is exactly the expression used here.
- LocallyRunOperatorExtension instantiates reconcilers with Utils#instantiate,
so it also supports non-public no-arg constructors and reports the failing
class instead of wrapping in a bare RuntimeException.
…perator-framework#3543)
The event count is increased at the beginning of the reconciliation, thus
waiting for it released the test into the middle of a reconciliation that
was still about to remove the finalizer. The subsequent update then raced
with that removal and failed with a conflict.
Wait for the finalizer removal to actually land, and retry the (optimistically
locked) update with a fresh read on conflict.
…rk#3536)
Add an opt-in, experimental detectApiVersionChange option on
@KubernetesDependent that records the API version the operator applies
in the javaoperatorsdk.io/last-applied-api-version annotation. The
regular matcher then detects a mismatch when that marker differs from
(or is missing relative to) the currently applied API version, causing
a one-time update without triggering repeated reconciliations once the
resource is up-to-date. Disabled by default, so existing behavior and
matching are unaffected unless explicitly enabled.
Guard against a null or immutable annotations map (e.g. Map.of(...))
on the desired resource when detectApiVersionChange is enabled, since
writing the last-applied-api-version marker (and the pre-existing
previous-annotation bookkeeping that runs alongside it) requires a
mutable map. Also rename a misleadingly-named test helper and replace
a no-op assertion on a primitive boolean with a concrete expectation.
Addresses Copilot review feedback on PR operator-framework#3536.
Add DetectApiVersionChangeIT covering the end-to-end scenario: a
ConfigMap dependent resource configured with detectApiVersionChange
is marked with the current API version on creation (without
triggering an update), and a stale marker annotation left on the
actual resource (simulating an older operator/CRD version) is
detected and corrected with exactly one update, after which no
further reconciliation loop occurs.
Signed-off-by: hej090224 <fc49854985@gmail.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
…ator-framework#3548)
InformerManager is generic over C extends Informable, but getTargetClient
type-tested for InformerEventSourceConfiguration to find out whether a specific
(e.g. remote cluster) client was configured. Informable has two implementors
and only one could answer the question, so a third configuration type wanting
its own client would be ignored silently rather than failing to compile.
Move the default getKubernetesClient() up from InformerEventSourceConfiguration
to Informable and let InformerManager ask the configuration directly. The
default still returns Optional.empty(), so existing implementations are
unaffected. As a side effect the ConfigurationService client is now only
created when no specific client is configured, instead of being created and
then discarded.
…rk#3546)
- GenericKubernetesResourceMatcher allocated the path-prefix lists
(List.of(SPEC), List.of(METADATA), the labels/annotations pair, List.of(STATUS))
once per JSON-diff node while matching, and nodeIsChildOf built a stream per
call. Both run for every node of every match, so hoist the lists to constants
and use an indexed loop.
- SSABasedGenericKubernetesResourceMatcher#sanitizeState nested the StatefulSet
volume-claim-template handling four levels deep inside the type ladder;
extract it into sanitizeVolumeClaimTemplates so the ladder reads as one
dispatch per resource kind.
@openshift-ciopenshift-ciBot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 11, 2026
@coderabbitai

coderabbitaiBot commented Aug 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e6fdac0d-13b6-4df3-af4f-3f9ff690efcf

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@openshift-ciopenshift-ciBot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 11, 2026
@csviri
csviri changed the base branch from main to nextAugust 11, 2026 15:49
Requires Java 21, therefore the module is only part of the build when the
JDK used is at least version 21.
Configures both the framework executors (reconciliation and workflow) and
the Kubernetes client task executor with a virtual thread per task
executor.
@openshift-ciopenshift-ciBot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 11, 2026
@csviricsviri linked an issue Aug 11, 2026 that may be closed by this pull request
@csviricsviri changed the title Sample for virtual threadsimprove: sample for virtual threadsAug 11, 2026
@csviri
csviri marked this pull request as ready for review August 12, 2026 06:49
CopilotAI lite review requested due to automatic review settings August 12, 2026 06:49
@openshift-ciopenshift-ciBot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 12, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new sample operator demonstrating how to run JOSDK reconciliations and Fabric8 client async callbacks on Java virtual threads (Java 21+), along with documentation and CI updates so the sample is built/tested appropriately.

Changes:

  • Introduces a new sample-operators/virtual-threads Maven module (Java 21+) with a reconciler, CRD types, logging config, and an E2E test.
  • Adds a JDK-activated Maven profile in sample-operators/pom.xml to include the sample only on JDK 21+.
  • Updates docs and the E2E GitHub Actions workflow to reference and test the new sample.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
sample-operators/virtual-threads/src/test/resources/log4j2.xmlAdds test logging configuration for the virtual threads sample.
sample-operators/virtual-threads/src/test/java/io/javaoperatorsdk/operator/sample/VirtualThreadsOperatorE2E.javaAdds an E2E test validating parallel reconciliations and virtual-thread execution.
sample-operators/virtual-threads/src/main/resources/log4j2.xmlAdds runtime logging configuration for the sample operator.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsStatus.javaAdds CR status fields used by the sample to report observed value and virtual-thread usage.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsSpec.javaAdds CR spec schema for the sample.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsReconciler.javaImplements a reconciler that simulates blocking work and patches status.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsOperator.javaAdds the sample operator entry point wiring virtual-thread executors and client config.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsCustomResource.javaDefines the sample CustomResource type and group/version metadata.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreads.javaCentralizes virtual-thread configuration for both framework executors and Fabric8 client task executor.
sample-operators/virtual-threads/README.mdDocuments the sample, Java 21+ requirement, and how to run it.
sample-operators/virtual-threads/pom.xmlIntroduces the new Java 21+ sample module build and dependencies.
sample-operators/virtual-threads/k8s/virtual-threads-custom-resource.yamlAdds a sample CR manifest for running the operator manually.
sample-operators/virtual-threads/k8s/operator.yamlAdds a deployment + RBAC manifest used by remote E2E mode.
sample-operators/pom.xmlAdds a JDK>=21-activated profile to include the new sample module.
docs/content/en/docs/getting-started/bootstrap-and-samples.mdAdds the virtual-threads sample to the list of available samples.
docs/content/en/docs/documentation/operations/configuration.mdDocuments how to run JOSDK on virtual threads (framework + client executor).
.github/workflows/e2e-test.ymlAdds the new sample module to the E2E workflow matrix (running on JDK 25).

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment threaddocs/content/en/docs/getting-started/bootstrap-and-samples.md Outdated
Comment on lines +66 to +70
@Override
public void onClose(Executor executor) {
((ExecutorService) executor).shutdownNow();
}
}
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@csviri
csviri marked this pull request as draft August 12, 2026 08:54
@openshift-ciopenshift-ciBot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 12, 2026
Comment on lines +30 to +31
can be replaced with virtual threads, so that a reconciliation waiting on a remote call does not
occupy a platform thread:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
can be replaced with virtual threads, so that a reconciliation waiting on a remote call does not
occupy a platform thread:
can be replaced with virtual threads.

Each virtual thread always runs on platform thread in the background.

@csviricsviri changed the title improve: sample for virtual threadstest: sample for virtual threadsAug 13, 2026
@openshift-ciopenshift-ciBot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 28, 2026
@openshift-ci

Copy link
Copy Markdown

PR needs rebase.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@csviri
csviriforce-pushed the next branch 2 times, most recently from 5ccac4d to 478199eCompareSeptember 3, 2026 11:40
@csviri
csviri deleted the branch operator-framework:nextSeptember 3, 2026 13:15
@csviricsviri closed this Sep 3, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge/work-in-progressIndicates that a PR should not merge because it is a work in progress.needs-rebaseIndicates a PR cannot be merged because it has merge conflicts with HEAD.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for Virtual Threads

4 participants

@csviri@xstefank@exijn
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

test: sample for virtual threads - #3554

Closed
csviri wants to merge 21 commits into
operator-framework:nextfrom
csviri:virtual-threads
Closed

test: sample for virtual threads#3554
csviri wants to merge 21 commits into
operator-framework:nextfrom
csviri:virtual-threads

Conversation

@csviri

@csviricsviri commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Adds sample (serves as a smoke test) for virtual threads setup of JOSDK.

TODO:

  • check logging / thread local handling

csviriand others added 19 commits July 22, 2026 15:59
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
…us (operator-framework#3480)
Add integration tests that showcases handling explicit state in status. Both for simple managed/secondary resources and using a dependent resource.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* feat: pool informers so controllers and event sources can share them
Every InformerEventSource used to create its own SharedIndexInformer, so an
operator whose controllers all watch the same secondary type - ConfigMap and
Secret being the usual suspects - opened one watch connection and kept one cache
per controller for the very same resources.
Informers are now handed out by an InformerPool obtained from the
ConfigurationService, keyed by an InformerClassifier. Event sources whose
classifiers are equal are backed by one informer; the pool reference counts its
users and stops the informer once the last one releases it. The classifier is
made up of everything that decides what an informer watches and how: the
KubernetesClient instance (compared by identity, since two clients for the same
API server may still differ in credentials, impersonation or TLS material), the
resource class or the group/version/kind for generic resources, the namespace,
the label, field and shard selectors, and the item store.
Two components are deliberately not part of that identity. The informerListLimit
is excluded, so event sources that disagree only on it still share an informer,
keeping the limit of whichever one created it and logging a warning. Indexers are
excluded because they can be added to a running informer: they are registered
under a name qualified with the controller and event source that added them, so
index names stay private to an event source while callers keep using their own
names, and they are removed again when that event source releases the informer.
Two strategies ship: DefaultInformerPool shares as described and is the default,
NonSharingInformerPool creates a dedicated informer per event source for anyone
wanting to opt out. Either is selected with
ConfigurationServiceOverrider#withInformerPool, and a custom strategy extends
AbstractInformerPool, which already creates the informers from a classifier,
starts them and waits for their caches to sync, leaving the subclass only the
question of whether and when an informer is shared.
Consequently informer creation and startup moved out of InformerWrapper and
InformerManager into the pool, InformerManager acquires and releases informers
instead of owning them, and it removes its own event handler and indexers from an
informer that keeps running for others. An event source registered dynamically
against an already running shared informer needs no special handling: the client
replays the cache contents to a newly added handler.
Also in support of the above: ConfigurationService#informerPool, an
InformerEventSource constructor that no longer needs an EventSourceContext (the
one taking it is deprecated), the resource group/version/kind on
InformerConfiguration, equality and toString on FieldSelector, and equality of
GroupVersionKindPlural made consistent with its hashCode so that an unspecified
plural no longer splits informers.
The pooling itself is production ready; the configuration API around it is marked
experimental and may still change. Covered by unit tests for the pools, the
classifier, the wrapper and the manager, and by integration tests for sharing,
dynamic registration and de-registration that each run against both strategies.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
…-framework#3511)
Bumps [org.apache.maven.plugins:maven-jar-plugin](https://github.com/apache/maven-jar-plugin) from 3.5.0 to 3.5.1.
- [Release notes](https://github.com/apache/maven-jar-plugin/releases)
- [Commits](apache/maven-jar-plugin@maven-jar-plugin-3.5.0...maven-jar-plugin-3.5.1)
---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-jar-plugin
dependency-version: 3.5.1
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…-framework#3510)
Bumps `openrewrite.version` from 8.87.5 to 8.87.7.
Updates `org.openrewrite:rewrite-java` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
Updates `org.openrewrite:rewrite-maven` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
Updates `org.openrewrite:rewrite-test` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
Updates `org.openrewrite:rewrite-java-17` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
---
updated-dependencies:
- dependency-name: org.openrewrite:rewrite-java
dependency-version: 8.87.7
dependency-type: direct:production
update-type: version-update:semver-patch
- dependency-name: org.openrewrite:rewrite-maven
dependency-version: 8.87.7
dependency-type: direct:production
update-type: version-update:semver-patch
- dependency-name: org.openrewrite:rewrite-test
dependency-version: 8.87.7
dependency-type: direct:development
update-type: version-update:semver-patch
- dependency-name: org.openrewrite:rewrite-java-17
dependency-version: 8.87.7
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…8.0 to 3.9.0 (operator-framework#3515)
Signed-off-by: dependabot[bot] <support@github.com>
…erator-framework#3514)
Fabric8's no-context patch() fetches the resource from the server and
computes an RFC 6902 JSON Patch diff, which removes the fields omitted
from a partial resource (including the whole spec). Pass an explicit
JSON_MERGE PatchContext so the non-status merge patch operations really
send application/merge-patch+json.
…ator E2E test (operator-framework#3513)
Adds a Maven-compiled Kotlin test source set to operator-framework-core
and a smoke test verifying that a checked (non-RuntimeException) Exception
thrown from a Kotlin DependentResource is properly caught and reported by
the workflow executor, so that retries are triggered as expected.
Kotlin does not have checked exceptions, so Kotlin code can throw a
checked Exception from an overridden method without declaring it, even
though the Java DependentResource#reconcile signature does not declare
`throws Exception`. Before operator-framework#2965 this exception would not have been
caught by NodeExecutor, since it only handled RuntimeException, silently
swallowing the error and preventing retries.
Closesoperator-framework#2967
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* fix: close class brace in Kotlin test; add Kotlin sample operator E2E
Fixes a missing closing brace left in KotlinCheckedExceptionDependentResourceTest.kt.
Adds a new sample-operators/kotlin-operator module: a minimalist Kotlin
operator (ConfigMapCopyReconciler) that copies a value from a custom
resource's spec into a ConfigMap, with a dual-mode (local/remote) E2E
test mirroring the other samples. This verifies deserialization via the
fabric8 client and the full reconciliation runtime when both the CR and
reconciler are implemented in Kotlin, addressing the maintainer's request
for broader coverage than the existing unit-level smoke test.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This needed to be fixed after rebase.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
)
This is expected for a pool to set. Event if the underlying implementation would set it, this is the correct way to handle it.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
…work#3547)
AbstractWorkflowExecutor called Workflow#getDependentResourcesByName purely to
read its size. That allocates a HashMap and walks every node to collect the
dependent resources, then discards the map. The executor is constructed on
every reconcile and cleanup of a workflow-based reconciler, so use the existing
Workflow#size instead.
…perator-framework#3550)
ResourceOperations#desiredForJsonPatch takes an Options argument that it never
reads - it only clones the actual resource and applies the operator. All six
jsonPatch call sites thread the value in for nothing, which suggests the method
still honours the option. Remove the parameter.
…ator-framework#3544)
No behavior change; each site is replaced by a helper that already exists.
- PrimaryUpdateAndCacheUtils#compareResourceVersions (and its private
validateResourceVersion) duplicated the entire algorithm of
ReconcilerUtilsInternal#validateAndCompareResourceVersions: the length-first
compare, the empty check, the leading-zero check and the same exception
messages. It had no production caller, so the copy could silently drift from
the version all production paths use. Delegate instead.
- addFinalizerWithSSA builds its bare SSA skeleton with
HasMetadata#initNameAndNamespaceFrom, which ResourceOperations already uses
for the same purpose and which is Namespaced-aware.
- AbstractInformerPool formats the informer identifier with
ReconcilerUtilsInternal#getResourceTypeNameWithVersion instead of
concatenating the resource name and version by hand.
- EventFilterWindow uses ExtendedResourceEvent#getResourceVersion, which had
no callers even though it is exactly the expression used here.
- LocallyRunOperatorExtension instantiates reconcilers with Utils#instantiate,
so it also supports non-public no-arg constructors and reports the failing
class instead of wrapping in a bare RuntimeException.
…perator-framework#3543)
The event count is increased at the beginning of the reconciliation, thus
waiting for it released the test into the middle of a reconciliation that
was still about to remove the finalizer. The subsequent update then raced
with that removal and failed with a conflict.
Wait for the finalizer removal to actually land, and retry the (optimistically
locked) update with a fresh read on conflict.
…rk#3536)
Add an opt-in, experimental detectApiVersionChange option on
@KubernetesDependent that records the API version the operator applies
in the javaoperatorsdk.io/last-applied-api-version annotation. The
regular matcher then detects a mismatch when that marker differs from
(or is missing relative to) the currently applied API version, causing
a one-time update without triggering repeated reconciliations once the
resource is up-to-date. Disabled by default, so existing behavior and
matching are unaffected unless explicitly enabled.
Guard against a null or immutable annotations map (e.g. Map.of(...))
on the desired resource when detectApiVersionChange is enabled, since
writing the last-applied-api-version marker (and the pre-existing
previous-annotation bookkeeping that runs alongside it) requires a
mutable map. Also rename a misleadingly-named test helper and replace
a no-op assertion on a primitive boolean with a concrete expectation.
Addresses Copilot review feedback on PR operator-framework#3536.
Add DetectApiVersionChangeIT covering the end-to-end scenario: a
ConfigMap dependent resource configured with detectApiVersionChange
is marked with the current API version on creation (without
triggering an update), and a stale marker annotation left on the
actual resource (simulating an older operator/CRD version) is
detected and corrected with exactly one update, after which no
further reconciliation loop occurs.
Signed-off-by: hej090224 <fc49854985@gmail.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
…ator-framework#3548)
InformerManager is generic over C extends Informable, but getTargetClient
type-tested for InformerEventSourceConfiguration to find out whether a specific
(e.g. remote cluster) client was configured. Informable has two implementors
and only one could answer the question, so a third configuration type wanting
its own client would be ignored silently rather than failing to compile.
Move the default getKubernetesClient() up from InformerEventSourceConfiguration
to Informable and let InformerManager ask the configuration directly. The
default still returns Optional.empty(), so existing implementations are
unaffected. As a side effect the ConfigurationService client is now only
created when no specific client is configured, instead of being created and
then discarded.
…rk#3546)
- GenericKubernetesResourceMatcher allocated the path-prefix lists
(List.of(SPEC), List.of(METADATA), the labels/annotations pair, List.of(STATUS))
once per JSON-diff node while matching, and nodeIsChildOf built a stream per
call. Both run for every node of every match, so hoist the lists to constants
and use an indexed loop.
- SSABasedGenericKubernetesResourceMatcher#sanitizeState nested the StatefulSet
volume-claim-template handling four levels deep inside the type ladder;
extract it into sanitizeVolumeClaimTemplates so the ladder reads as one
dispatch per resource kind.
@openshift-ciopenshift-ciBot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 11, 2026
@coderabbitai

coderabbitaiBot commented Aug 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e6fdac0d-13b6-4df3-af4f-3f9ff690efcf

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@openshift-ciopenshift-ciBot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 11, 2026
@csviri
csviri changed the base branch from main to nextAugust 11, 2026 15:49
Requires Java 21, therefore the module is only part of the build when the
JDK used is at least version 21.
Configures both the framework executors (reconciliation and workflow) and
the Kubernetes client task executor with a virtual thread per task
executor.
@openshift-ciopenshift-ciBot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 11, 2026
@csviricsviri linked an issue Aug 11, 2026 that may be closed by this pull request
@csviricsviri changed the title Sample for virtual threadsimprove: sample for virtual threadsAug 11, 2026
@csviri
csviri marked this pull request as ready for review August 12, 2026 06:49
CopilotAI lite review requested due to automatic review settings August 12, 2026 06:49
@openshift-ciopenshift-ciBot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 12, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new sample operator demonstrating how to run JOSDK reconciliations and Fabric8 client async callbacks on Java virtual threads (Java 21+), along with documentation and CI updates so the sample is built/tested appropriately.

Changes:

  • Introduces a new sample-operators/virtual-threads Maven module (Java 21+) with a reconciler, CRD types, logging config, and an E2E test.
  • Adds a JDK-activated Maven profile in sample-operators/pom.xml to include the sample only on JDK 21+.
  • Updates docs and the E2E GitHub Actions workflow to reference and test the new sample.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
sample-operators/virtual-threads/src/test/resources/log4j2.xmlAdds test logging configuration for the virtual threads sample.
sample-operators/virtual-threads/src/test/java/io/javaoperatorsdk/operator/sample/VirtualThreadsOperatorE2E.javaAdds an E2E test validating parallel reconciliations and virtual-thread execution.
sample-operators/virtual-threads/src/main/resources/log4j2.xmlAdds runtime logging configuration for the sample operator.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsStatus.javaAdds CR status fields used by the sample to report observed value and virtual-thread usage.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsSpec.javaAdds CR spec schema for the sample.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsReconciler.javaImplements a reconciler that simulates blocking work and patches status.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsOperator.javaAdds the sample operator entry point wiring virtual-thread executors and client config.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsCustomResource.javaDefines the sample CustomResource type and group/version metadata.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreads.javaCentralizes virtual-thread configuration for both framework executors and Fabric8 client task executor.
sample-operators/virtual-threads/README.mdDocuments the sample, Java 21+ requirement, and how to run it.
sample-operators/virtual-threads/pom.xmlIntroduces the new Java 21+ sample module build and dependencies.
sample-operators/virtual-threads/k8s/virtual-threads-custom-resource.yamlAdds a sample CR manifest for running the operator manually.
sample-operators/virtual-threads/k8s/operator.yamlAdds a deployment + RBAC manifest used by remote E2E mode.
sample-operators/pom.xmlAdds a JDK>=21-activated profile to include the new sample module.
docs/content/en/docs/getting-started/bootstrap-and-samples.mdAdds the virtual-threads sample to the list of available samples.
docs/content/en/docs/documentation/operations/configuration.mdDocuments how to run JOSDK on virtual threads (framework + client executor).
.github/workflows/e2e-test.ymlAdds the new sample module to the E2E workflow matrix (running on JDK 25).

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment threaddocs/content/en/docs/getting-started/bootstrap-and-samples.md Outdated
Comment on lines +66 to +70
@Override
public void onClose(Executor executor) {
((ExecutorService) executor).shutdownNow();
}
}
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@csviri
csviri marked this pull request as draft August 12, 2026 08:54
@openshift-ciopenshift-ciBot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 12, 2026
Comment on lines +30 to +31
can be replaced with virtual threads, so that a reconciliation waiting on a remote call does not
occupy a platform thread:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
can be replaced with virtual threads, so that a reconciliation waiting on a remote call does not
occupy a platform thread:
can be replaced with virtual threads.

Each virtual thread always runs on platform thread in the background.

@csviricsviri changed the title improve: sample for virtual threadstest: sample for virtual threadsAug 13, 2026
@openshift-ciopenshift-ciBot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 28, 2026
@openshift-ci

Copy link
Copy Markdown

PR needs rebase.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@csviri
csviriforce-pushed the next branch 2 times, most recently from 5ccac4d to 478199eCompareSeptember 3, 2026 11:40
@csviri
csviri deleted the branch operator-framework:nextSeptember 3, 2026 13:15
@csviricsviri closed this Sep 3, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge/work-in-progressIndicates that a PR should not merge because it is a work in progress.needs-rebaseIndicates a PR cannot be merged because it has merge conflicts with HEAD.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for Virtual Threads

4 participants

@csviri@xstefank@exijn
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

test: sample for virtual threads - #3554

Closed
csviri wants to merge 21 commits into
operator-framework:nextfrom
csviri:virtual-threads
Closed

test: sample for virtual threads#3554
csviri wants to merge 21 commits into
operator-framework:nextfrom
csviri:virtual-threads

Conversation

@csviri

@csviricsviri commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Adds sample (serves as a smoke test) for virtual threads setup of JOSDK.

TODO:

  • check logging / thread local handling

csviriand others added 19 commits July 22, 2026 15:59
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
…us (operator-framework#3480)
Add integration tests that showcases handling explicit state in status. Both for simple managed/secondary resources and using a dependent resource.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* feat: pool informers so controllers and event sources can share them
Every InformerEventSource used to create its own SharedIndexInformer, so an
operator whose controllers all watch the same secondary type - ConfigMap and
Secret being the usual suspects - opened one watch connection and kept one cache
per controller for the very same resources.
Informers are now handed out by an InformerPool obtained from the
ConfigurationService, keyed by an InformerClassifier. Event sources whose
classifiers are equal are backed by one informer; the pool reference counts its
users and stops the informer once the last one releases it. The classifier is
made up of everything that decides what an informer watches and how: the
KubernetesClient instance (compared by identity, since two clients for the same
API server may still differ in credentials, impersonation or TLS material), the
resource class or the group/version/kind for generic resources, the namespace,
the label, field and shard selectors, and the item store.
Two components are deliberately not part of that identity. The informerListLimit
is excluded, so event sources that disagree only on it still share an informer,
keeping the limit of whichever one created it and logging a warning. Indexers are
excluded because they can be added to a running informer: they are registered
under a name qualified with the controller and event source that added them, so
index names stay private to an event source while callers keep using their own
names, and they are removed again when that event source releases the informer.
Two strategies ship: DefaultInformerPool shares as described and is the default,
NonSharingInformerPool creates a dedicated informer per event source for anyone
wanting to opt out. Either is selected with
ConfigurationServiceOverrider#withInformerPool, and a custom strategy extends
AbstractInformerPool, which already creates the informers from a classifier,
starts them and waits for their caches to sync, leaving the subclass only the
question of whether and when an informer is shared.
Consequently informer creation and startup moved out of InformerWrapper and
InformerManager into the pool, InformerManager acquires and releases informers
instead of owning them, and it removes its own event handler and indexers from an
informer that keeps running for others. An event source registered dynamically
against an already running shared informer needs no special handling: the client
replays the cache contents to a newly added handler.
Also in support of the above: ConfigurationService#informerPool, an
InformerEventSource constructor that no longer needs an EventSourceContext (the
one taking it is deprecated), the resource group/version/kind on
InformerConfiguration, equality and toString on FieldSelector, and equality of
GroupVersionKindPlural made consistent with its hashCode so that an unspecified
plural no longer splits informers.
The pooling itself is production ready; the configuration API around it is marked
experimental and may still change. Covered by unit tests for the pools, the
classifier, the wrapper and the manager, and by integration tests for sharing,
dynamic registration and de-registration that each run against both strategies.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
…-framework#3511)
Bumps [org.apache.maven.plugins:maven-jar-plugin](https://github.com/apache/maven-jar-plugin) from 3.5.0 to 3.5.1.
- [Release notes](https://github.com/apache/maven-jar-plugin/releases)
- [Commits](apache/maven-jar-plugin@maven-jar-plugin-3.5.0...maven-jar-plugin-3.5.1)
---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-jar-plugin
dependency-version: 3.5.1
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…-framework#3510)
Bumps `openrewrite.version` from 8.87.5 to 8.87.7.
Updates `org.openrewrite:rewrite-java` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
Updates `org.openrewrite:rewrite-maven` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
Updates `org.openrewrite:rewrite-test` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
Updates `org.openrewrite:rewrite-java-17` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
---
updated-dependencies:
- dependency-name: org.openrewrite:rewrite-java
dependency-version: 8.87.7
dependency-type: direct:production
update-type: version-update:semver-patch
- dependency-name: org.openrewrite:rewrite-maven
dependency-version: 8.87.7
dependency-type: direct:production
update-type: version-update:semver-patch
- dependency-name: org.openrewrite:rewrite-test
dependency-version: 8.87.7
dependency-type: direct:development
update-type: version-update:semver-patch
- dependency-name: org.openrewrite:rewrite-java-17
dependency-version: 8.87.7
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…8.0 to 3.9.0 (operator-framework#3515)
Signed-off-by: dependabot[bot] <support@github.com>
…erator-framework#3514)
Fabric8's no-context patch() fetches the resource from the server and
computes an RFC 6902 JSON Patch diff, which removes the fields omitted
from a partial resource (including the whole spec). Pass an explicit
JSON_MERGE PatchContext so the non-status merge patch operations really
send application/merge-patch+json.
…ator E2E test (operator-framework#3513)
Adds a Maven-compiled Kotlin test source set to operator-framework-core
and a smoke test verifying that a checked (non-RuntimeException) Exception
thrown from a Kotlin DependentResource is properly caught and reported by
the workflow executor, so that retries are triggered as expected.
Kotlin does not have checked exceptions, so Kotlin code can throw a
checked Exception from an overridden method without declaring it, even
though the Java DependentResource#reconcile signature does not declare
`throws Exception`. Before operator-framework#2965 this exception would not have been
caught by NodeExecutor, since it only handled RuntimeException, silently
swallowing the error and preventing retries.
Closesoperator-framework#2967
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* fix: close class brace in Kotlin test; add Kotlin sample operator E2E
Fixes a missing closing brace left in KotlinCheckedExceptionDependentResourceTest.kt.
Adds a new sample-operators/kotlin-operator module: a minimalist Kotlin
operator (ConfigMapCopyReconciler) that copies a value from a custom
resource's spec into a ConfigMap, with a dual-mode (local/remote) E2E
test mirroring the other samples. This verifies deserialization via the
fabric8 client and the full reconciliation runtime when both the CR and
reconciler are implemented in Kotlin, addressing the maintainer's request
for broader coverage than the existing unit-level smoke test.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This needed to be fixed after rebase.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
)
This is expected for a pool to set. Event if the underlying implementation would set it, this is the correct way to handle it.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
…work#3547)
AbstractWorkflowExecutor called Workflow#getDependentResourcesByName purely to
read its size. That allocates a HashMap and walks every node to collect the
dependent resources, then discards the map. The executor is constructed on
every reconcile and cleanup of a workflow-based reconciler, so use the existing
Workflow#size instead.
…perator-framework#3550)
ResourceOperations#desiredForJsonPatch takes an Options argument that it never
reads - it only clones the actual resource and applies the operator. All six
jsonPatch call sites thread the value in for nothing, which suggests the method
still honours the option. Remove the parameter.
…ator-framework#3544)
No behavior change; each site is replaced by a helper that already exists.
- PrimaryUpdateAndCacheUtils#compareResourceVersions (and its private
validateResourceVersion) duplicated the entire algorithm of
ReconcilerUtilsInternal#validateAndCompareResourceVersions: the length-first
compare, the empty check, the leading-zero check and the same exception
messages. It had no production caller, so the copy could silently drift from
the version all production paths use. Delegate instead.
- addFinalizerWithSSA builds its bare SSA skeleton with
HasMetadata#initNameAndNamespaceFrom, which ResourceOperations already uses
for the same purpose and which is Namespaced-aware.
- AbstractInformerPool formats the informer identifier with
ReconcilerUtilsInternal#getResourceTypeNameWithVersion instead of
concatenating the resource name and version by hand.
- EventFilterWindow uses ExtendedResourceEvent#getResourceVersion, which had
no callers even though it is exactly the expression used here.
- LocallyRunOperatorExtension instantiates reconcilers with Utils#instantiate,
so it also supports non-public no-arg constructors and reports the failing
class instead of wrapping in a bare RuntimeException.
…perator-framework#3543)
The event count is increased at the beginning of the reconciliation, thus
waiting for it released the test into the middle of a reconciliation that
was still about to remove the finalizer. The subsequent update then raced
with that removal and failed with a conflict.
Wait for the finalizer removal to actually land, and retry the (optimistically
locked) update with a fresh read on conflict.
…rk#3536)
Add an opt-in, experimental detectApiVersionChange option on
@KubernetesDependent that records the API version the operator applies
in the javaoperatorsdk.io/last-applied-api-version annotation. The
regular matcher then detects a mismatch when that marker differs from
(or is missing relative to) the currently applied API version, causing
a one-time update without triggering repeated reconciliations once the
resource is up-to-date. Disabled by default, so existing behavior and
matching are unaffected unless explicitly enabled.
Guard against a null or immutable annotations map (e.g. Map.of(...))
on the desired resource when detectApiVersionChange is enabled, since
writing the last-applied-api-version marker (and the pre-existing
previous-annotation bookkeeping that runs alongside it) requires a
mutable map. Also rename a misleadingly-named test helper and replace
a no-op assertion on a primitive boolean with a concrete expectation.
Addresses Copilot review feedback on PR operator-framework#3536.
Add DetectApiVersionChangeIT covering the end-to-end scenario: a
ConfigMap dependent resource configured with detectApiVersionChange
is marked with the current API version on creation (without
triggering an update), and a stale marker annotation left on the
actual resource (simulating an older operator/CRD version) is
detected and corrected with exactly one update, after which no
further reconciliation loop occurs.
Signed-off-by: hej090224 <fc49854985@gmail.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
…ator-framework#3548)
InformerManager is generic over C extends Informable, but getTargetClient
type-tested for InformerEventSourceConfiguration to find out whether a specific
(e.g. remote cluster) client was configured. Informable has two implementors
and only one could answer the question, so a third configuration type wanting
its own client would be ignored silently rather than failing to compile.
Move the default getKubernetesClient() up from InformerEventSourceConfiguration
to Informable and let InformerManager ask the configuration directly. The
default still returns Optional.empty(), so existing implementations are
unaffected. As a side effect the ConfigurationService client is now only
created when no specific client is configured, instead of being created and
then discarded.
…rk#3546)
- GenericKubernetesResourceMatcher allocated the path-prefix lists
(List.of(SPEC), List.of(METADATA), the labels/annotations pair, List.of(STATUS))
once per JSON-diff node while matching, and nodeIsChildOf built a stream per
call. Both run for every node of every match, so hoist the lists to constants
and use an indexed loop.
- SSABasedGenericKubernetesResourceMatcher#sanitizeState nested the StatefulSet
volume-claim-template handling four levels deep inside the type ladder;
extract it into sanitizeVolumeClaimTemplates so the ladder reads as one
dispatch per resource kind.
@openshift-ciopenshift-ciBot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 11, 2026
@coderabbitai

coderabbitaiBot commented Aug 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e6fdac0d-13b6-4df3-af4f-3f9ff690efcf

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@openshift-ciopenshift-ciBot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 11, 2026
@csviri
csviri changed the base branch from main to nextAugust 11, 2026 15:49
Requires Java 21, therefore the module is only part of the build when the
JDK used is at least version 21.
Configures both the framework executors (reconciliation and workflow) and
the Kubernetes client task executor with a virtual thread per task
executor.
@openshift-ciopenshift-ciBot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 11, 2026
@csviricsviri linked an issue Aug 11, 2026 that may be closed by this pull request
@csviricsviri changed the title Sample for virtual threadsimprove: sample for virtual threadsAug 11, 2026
@csviri
csviri marked this pull request as ready for review August 12, 2026 06:49
CopilotAI lite review requested due to automatic review settings August 12, 2026 06:49
@openshift-ciopenshift-ciBot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 12, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new sample operator demonstrating how to run JOSDK reconciliations and Fabric8 client async callbacks on Java virtual threads (Java 21+), along with documentation and CI updates so the sample is built/tested appropriately.

Changes:

  • Introduces a new sample-operators/virtual-threads Maven module (Java 21+) with a reconciler, CRD types, logging config, and an E2E test.
  • Adds a JDK-activated Maven profile in sample-operators/pom.xml to include the sample only on JDK 21+.
  • Updates docs and the E2E GitHub Actions workflow to reference and test the new sample.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
sample-operators/virtual-threads/src/test/resources/log4j2.xmlAdds test logging configuration for the virtual threads sample.
sample-operators/virtual-threads/src/test/java/io/javaoperatorsdk/operator/sample/VirtualThreadsOperatorE2E.javaAdds an E2E test validating parallel reconciliations and virtual-thread execution.
sample-operators/virtual-threads/src/main/resources/log4j2.xmlAdds runtime logging configuration for the sample operator.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsStatus.javaAdds CR status fields used by the sample to report observed value and virtual-thread usage.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsSpec.javaAdds CR spec schema for the sample.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsReconciler.javaImplements a reconciler that simulates blocking work and patches status.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsOperator.javaAdds the sample operator entry point wiring virtual-thread executors and client config.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsCustomResource.javaDefines the sample CustomResource type and group/version metadata.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreads.javaCentralizes virtual-thread configuration for both framework executors and Fabric8 client task executor.
sample-operators/virtual-threads/README.mdDocuments the sample, Java 21+ requirement, and how to run it.
sample-operators/virtual-threads/pom.xmlIntroduces the new Java 21+ sample module build and dependencies.
sample-operators/virtual-threads/k8s/virtual-threads-custom-resource.yamlAdds a sample CR manifest for running the operator manually.
sample-operators/virtual-threads/k8s/operator.yamlAdds a deployment + RBAC manifest used by remote E2E mode.
sample-operators/pom.xmlAdds a JDK>=21-activated profile to include the new sample module.
docs/content/en/docs/getting-started/bootstrap-and-samples.mdAdds the virtual-threads sample to the list of available samples.
docs/content/en/docs/documentation/operations/configuration.mdDocuments how to run JOSDK on virtual threads (framework + client executor).
.github/workflows/e2e-test.ymlAdds the new sample module to the E2E workflow matrix (running on JDK 25).

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment threaddocs/content/en/docs/getting-started/bootstrap-and-samples.md Outdated
Comment on lines +66 to +70
@Override
public void onClose(Executor executor) {
((ExecutorService) executor).shutdownNow();
}
}
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@csviri
csviri marked this pull request as draft August 12, 2026 08:54
@openshift-ciopenshift-ciBot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 12, 2026
Comment on lines +30 to +31
can be replaced with virtual threads, so that a reconciliation waiting on a remote call does not
occupy a platform thread:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
can be replaced with virtual threads, so that a reconciliation waiting on a remote call does not
occupy a platform thread:
can be replaced with virtual threads.

Each virtual thread always runs on platform thread in the background.

@csviricsviri changed the title improve: sample for virtual threadstest: sample for virtual threadsAug 13, 2026
@openshift-ciopenshift-ciBot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 28, 2026
@openshift-ci

Copy link
Copy Markdown

PR needs rebase.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@csviri
csviriforce-pushed the next branch 2 times, most recently from 5ccac4d to 478199eCompareSeptember 3, 2026 11:40
@csviri
csviri deleted the branch operator-framework:nextSeptember 3, 2026 13:15
@csviricsviri closed this Sep 3, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge/work-in-progressIndicates that a PR should not merge because it is a work in progress.needs-rebaseIndicates a PR cannot be merged because it has merge conflicts with HEAD.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for Virtual Threads

4 participants

@csviri@xstefank@exijn
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

test: sample for virtual threads - #3554

Closed
csviri wants to merge 21 commits into
operator-framework:nextfrom
csviri:virtual-threads
Closed

test: sample for virtual threads#3554
csviri wants to merge 21 commits into
operator-framework:nextfrom
csviri:virtual-threads

Conversation

@csviri

@csviricsviri commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Adds sample (serves as a smoke test) for virtual threads setup of JOSDK.

TODO:

  • check logging / thread local handling

csviriand others added 19 commits July 22, 2026 15:59
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
…us (operator-framework#3480)
Add integration tests that showcases handling explicit state in status. Both for simple managed/secondary resources and using a dependent resource.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* feat: pool informers so controllers and event sources can share them
Every InformerEventSource used to create its own SharedIndexInformer, so an
operator whose controllers all watch the same secondary type - ConfigMap and
Secret being the usual suspects - opened one watch connection and kept one cache
per controller for the very same resources.
Informers are now handed out by an InformerPool obtained from the
ConfigurationService, keyed by an InformerClassifier. Event sources whose
classifiers are equal are backed by one informer; the pool reference counts its
users and stops the informer once the last one releases it. The classifier is
made up of everything that decides what an informer watches and how: the
KubernetesClient instance (compared by identity, since two clients for the same
API server may still differ in credentials, impersonation or TLS material), the
resource class or the group/version/kind for generic resources, the namespace,
the label, field and shard selectors, and the item store.
Two components are deliberately not part of that identity. The informerListLimit
is excluded, so event sources that disagree only on it still share an informer,
keeping the limit of whichever one created it and logging a warning. Indexers are
excluded because they can be added to a running informer: they are registered
under a name qualified with the controller and event source that added them, so
index names stay private to an event source while callers keep using their own
names, and they are removed again when that event source releases the informer.
Two strategies ship: DefaultInformerPool shares as described and is the default,
NonSharingInformerPool creates a dedicated informer per event source for anyone
wanting to opt out. Either is selected with
ConfigurationServiceOverrider#withInformerPool, and a custom strategy extends
AbstractInformerPool, which already creates the informers from a classifier,
starts them and waits for their caches to sync, leaving the subclass only the
question of whether and when an informer is shared.
Consequently informer creation and startup moved out of InformerWrapper and
InformerManager into the pool, InformerManager acquires and releases informers
instead of owning them, and it removes its own event handler and indexers from an
informer that keeps running for others. An event source registered dynamically
against an already running shared informer needs no special handling: the client
replays the cache contents to a newly added handler.
Also in support of the above: ConfigurationService#informerPool, an
InformerEventSource constructor that no longer needs an EventSourceContext (the
one taking it is deprecated), the resource group/version/kind on
InformerConfiguration, equality and toString on FieldSelector, and equality of
GroupVersionKindPlural made consistent with its hashCode so that an unspecified
plural no longer splits informers.
The pooling itself is production ready; the configuration API around it is marked
experimental and may still change. Covered by unit tests for the pools, the
classifier, the wrapper and the manager, and by integration tests for sharing,
dynamic registration and de-registration that each run against both strategies.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
…-framework#3511)
Bumps [org.apache.maven.plugins:maven-jar-plugin](https://github.com/apache/maven-jar-plugin) from 3.5.0 to 3.5.1.
- [Release notes](https://github.com/apache/maven-jar-plugin/releases)
- [Commits](apache/maven-jar-plugin@maven-jar-plugin-3.5.0...maven-jar-plugin-3.5.1)
---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-jar-plugin
dependency-version: 3.5.1
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…-framework#3510)
Bumps `openrewrite.version` from 8.87.5 to 8.87.7.
Updates `org.openrewrite:rewrite-java` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
Updates `org.openrewrite:rewrite-maven` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
Updates `org.openrewrite:rewrite-test` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
Updates `org.openrewrite:rewrite-java-17` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
---
updated-dependencies:
- dependency-name: org.openrewrite:rewrite-java
dependency-version: 8.87.7
dependency-type: direct:production
update-type: version-update:semver-patch
- dependency-name: org.openrewrite:rewrite-maven
dependency-version: 8.87.7
dependency-type: direct:production
update-type: version-update:semver-patch
- dependency-name: org.openrewrite:rewrite-test
dependency-version: 8.87.7
dependency-type: direct:development
update-type: version-update:semver-patch
- dependency-name: org.openrewrite:rewrite-java-17
dependency-version: 8.87.7
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…8.0 to 3.9.0 (operator-framework#3515)
Signed-off-by: dependabot[bot] <support@github.com>
…erator-framework#3514)
Fabric8's no-context patch() fetches the resource from the server and
computes an RFC 6902 JSON Patch diff, which removes the fields omitted
from a partial resource (including the whole spec). Pass an explicit
JSON_MERGE PatchContext so the non-status merge patch operations really
send application/merge-patch+json.
…ator E2E test (operator-framework#3513)
Adds a Maven-compiled Kotlin test source set to operator-framework-core
and a smoke test verifying that a checked (non-RuntimeException) Exception
thrown from a Kotlin DependentResource is properly caught and reported by
the workflow executor, so that retries are triggered as expected.
Kotlin does not have checked exceptions, so Kotlin code can throw a
checked Exception from an overridden method without declaring it, even
though the Java DependentResource#reconcile signature does not declare
`throws Exception`. Before operator-framework#2965 this exception would not have been
caught by NodeExecutor, since it only handled RuntimeException, silently
swallowing the error and preventing retries.
Closesoperator-framework#2967
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* fix: close class brace in Kotlin test; add Kotlin sample operator E2E
Fixes a missing closing brace left in KotlinCheckedExceptionDependentResourceTest.kt.
Adds a new sample-operators/kotlin-operator module: a minimalist Kotlin
operator (ConfigMapCopyReconciler) that copies a value from a custom
resource's spec into a ConfigMap, with a dual-mode (local/remote) E2E
test mirroring the other samples. This verifies deserialization via the
fabric8 client and the full reconciliation runtime when both the CR and
reconciler are implemented in Kotlin, addressing the maintainer's request
for broader coverage than the existing unit-level smoke test.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This needed to be fixed after rebase.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
)
This is expected for a pool to set. Event if the underlying implementation would set it, this is the correct way to handle it.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
…work#3547)
AbstractWorkflowExecutor called Workflow#getDependentResourcesByName purely to
read its size. That allocates a HashMap and walks every node to collect the
dependent resources, then discards the map. The executor is constructed on
every reconcile and cleanup of a workflow-based reconciler, so use the existing
Workflow#size instead.
…perator-framework#3550)
ResourceOperations#desiredForJsonPatch takes an Options argument that it never
reads - it only clones the actual resource and applies the operator. All six
jsonPatch call sites thread the value in for nothing, which suggests the method
still honours the option. Remove the parameter.
…ator-framework#3544)
No behavior change; each site is replaced by a helper that already exists.
- PrimaryUpdateAndCacheUtils#compareResourceVersions (and its private
validateResourceVersion) duplicated the entire algorithm of
ReconcilerUtilsInternal#validateAndCompareResourceVersions: the length-first
compare, the empty check, the leading-zero check and the same exception
messages. It had no production caller, so the copy could silently drift from
the version all production paths use. Delegate instead.
- addFinalizerWithSSA builds its bare SSA skeleton with
HasMetadata#initNameAndNamespaceFrom, which ResourceOperations already uses
for the same purpose and which is Namespaced-aware.
- AbstractInformerPool formats the informer identifier with
ReconcilerUtilsInternal#getResourceTypeNameWithVersion instead of
concatenating the resource name and version by hand.
- EventFilterWindow uses ExtendedResourceEvent#getResourceVersion, which had
no callers even though it is exactly the expression used here.
- LocallyRunOperatorExtension instantiates reconcilers with Utils#instantiate,
so it also supports non-public no-arg constructors and reports the failing
class instead of wrapping in a bare RuntimeException.
…perator-framework#3543)
The event count is increased at the beginning of the reconciliation, thus
waiting for it released the test into the middle of a reconciliation that
was still about to remove the finalizer. The subsequent update then raced
with that removal and failed with a conflict.
Wait for the finalizer removal to actually land, and retry the (optimistically
locked) update with a fresh read on conflict.
…rk#3536)
Add an opt-in, experimental detectApiVersionChange option on
@KubernetesDependent that records the API version the operator applies
in the javaoperatorsdk.io/last-applied-api-version annotation. The
regular matcher then detects a mismatch when that marker differs from
(or is missing relative to) the currently applied API version, causing
a one-time update without triggering repeated reconciliations once the
resource is up-to-date. Disabled by default, so existing behavior and
matching are unaffected unless explicitly enabled.
Guard against a null or immutable annotations map (e.g. Map.of(...))
on the desired resource when detectApiVersionChange is enabled, since
writing the last-applied-api-version marker (and the pre-existing
previous-annotation bookkeeping that runs alongside it) requires a
mutable map. Also rename a misleadingly-named test helper and replace
a no-op assertion on a primitive boolean with a concrete expectation.
Addresses Copilot review feedback on PR operator-framework#3536.
Add DetectApiVersionChangeIT covering the end-to-end scenario: a
ConfigMap dependent resource configured with detectApiVersionChange
is marked with the current API version on creation (without
triggering an update), and a stale marker annotation left on the
actual resource (simulating an older operator/CRD version) is
detected and corrected with exactly one update, after which no
further reconciliation loop occurs.
Signed-off-by: hej090224 <fc49854985@gmail.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
…ator-framework#3548)
InformerManager is generic over C extends Informable, but getTargetClient
type-tested for InformerEventSourceConfiguration to find out whether a specific
(e.g. remote cluster) client was configured. Informable has two implementors
and only one could answer the question, so a third configuration type wanting
its own client would be ignored silently rather than failing to compile.
Move the default getKubernetesClient() up from InformerEventSourceConfiguration
to Informable and let InformerManager ask the configuration directly. The
default still returns Optional.empty(), so existing implementations are
unaffected. As a side effect the ConfigurationService client is now only
created when no specific client is configured, instead of being created and
then discarded.
…rk#3546)
- GenericKubernetesResourceMatcher allocated the path-prefix lists
(List.of(SPEC), List.of(METADATA), the labels/annotations pair, List.of(STATUS))
once per JSON-diff node while matching, and nodeIsChildOf built a stream per
call. Both run for every node of every match, so hoist the lists to constants
and use an indexed loop.
- SSABasedGenericKubernetesResourceMatcher#sanitizeState nested the StatefulSet
volume-claim-template handling four levels deep inside the type ladder;
extract it into sanitizeVolumeClaimTemplates so the ladder reads as one
dispatch per resource kind.
@openshift-ciopenshift-ciBot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 11, 2026
@coderabbitai

coderabbitaiBot commented Aug 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e6fdac0d-13b6-4df3-af4f-3f9ff690efcf

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@openshift-ciopenshift-ciBot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 11, 2026
@csviri
csviri changed the base branch from main to nextAugust 11, 2026 15:49
Requires Java 21, therefore the module is only part of the build when the
JDK used is at least version 21.
Configures both the framework executors (reconciliation and workflow) and
the Kubernetes client task executor with a virtual thread per task
executor.
@openshift-ciopenshift-ciBot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 11, 2026
@csviricsviri linked an issue Aug 11, 2026 that may be closed by this pull request
@csviricsviri changed the title Sample for virtual threadsimprove: sample for virtual threadsAug 11, 2026
@csviri
csviri marked this pull request as ready for review August 12, 2026 06:49
CopilotAI lite review requested due to automatic review settings August 12, 2026 06:49
@openshift-ciopenshift-ciBot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 12, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new sample operator demonstrating how to run JOSDK reconciliations and Fabric8 client async callbacks on Java virtual threads (Java 21+), along with documentation and CI updates so the sample is built/tested appropriately.

Changes:

  • Introduces a new sample-operators/virtual-threads Maven module (Java 21+) with a reconciler, CRD types, logging config, and an E2E test.
  • Adds a JDK-activated Maven profile in sample-operators/pom.xml to include the sample only on JDK 21+.
  • Updates docs and the E2E GitHub Actions workflow to reference and test the new sample.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
sample-operators/virtual-threads/src/test/resources/log4j2.xmlAdds test logging configuration for the virtual threads sample.
sample-operators/virtual-threads/src/test/java/io/javaoperatorsdk/operator/sample/VirtualThreadsOperatorE2E.javaAdds an E2E test validating parallel reconciliations and virtual-thread execution.
sample-operators/virtual-threads/src/main/resources/log4j2.xmlAdds runtime logging configuration for the sample operator.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsStatus.javaAdds CR status fields used by the sample to report observed value and virtual-thread usage.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsSpec.javaAdds CR spec schema for the sample.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsReconciler.javaImplements a reconciler that simulates blocking work and patches status.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsOperator.javaAdds the sample operator entry point wiring virtual-thread executors and client config.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsCustomResource.javaDefines the sample CustomResource type and group/version metadata.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreads.javaCentralizes virtual-thread configuration for both framework executors and Fabric8 client task executor.
sample-operators/virtual-threads/README.mdDocuments the sample, Java 21+ requirement, and how to run it.
sample-operators/virtual-threads/pom.xmlIntroduces the new Java 21+ sample module build and dependencies.
sample-operators/virtual-threads/k8s/virtual-threads-custom-resource.yamlAdds a sample CR manifest for running the operator manually.
sample-operators/virtual-threads/k8s/operator.yamlAdds a deployment + RBAC manifest used by remote E2E mode.
sample-operators/pom.xmlAdds a JDK>=21-activated profile to include the new sample module.
docs/content/en/docs/getting-started/bootstrap-and-samples.mdAdds the virtual-threads sample to the list of available samples.
docs/content/en/docs/documentation/operations/configuration.mdDocuments how to run JOSDK on virtual threads (framework + client executor).
.github/workflows/e2e-test.ymlAdds the new sample module to the E2E workflow matrix (running on JDK 25).

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment threaddocs/content/en/docs/getting-started/bootstrap-and-samples.md Outdated
Comment on lines +66 to +70
@Override
public void onClose(Executor executor) {
((ExecutorService) executor).shutdownNow();
}
}
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@csviri
csviri marked this pull request as draft August 12, 2026 08:54
@openshift-ciopenshift-ciBot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 12, 2026
Comment on lines +30 to +31
can be replaced with virtual threads, so that a reconciliation waiting on a remote call does not
occupy a platform thread:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
can be replaced with virtual threads, so that a reconciliation waiting on a remote call does not
occupy a platform thread:
can be replaced with virtual threads.

Each virtual thread always runs on platform thread in the background.

@csviricsviri changed the title improve: sample for virtual threadstest: sample for virtual threadsAug 13, 2026
@openshift-ciopenshift-ciBot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 28, 2026
@openshift-ci

Copy link
Copy Markdown

PR needs rebase.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@csviri
csviriforce-pushed the next branch 2 times, most recently from 5ccac4d to 478199eCompareSeptember 3, 2026 11:40
@csviri
csviri deleted the branch operator-framework:nextSeptember 3, 2026 13:15
@csviricsviri closed this Sep 3, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge/work-in-progressIndicates that a PR should not merge because it is a work in progress.needs-rebaseIndicates a PR cannot be merged because it has merge conflicts with HEAD.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for Virtual Threads

4 participants

@csviri@xstefank@exijn
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

test: sample for virtual threads - #3554

Closed
csviri wants to merge 21 commits into
operator-framework:nextfrom
csviri:virtual-threads
Closed

test: sample for virtual threads#3554
csviri wants to merge 21 commits into
operator-framework:nextfrom
csviri:virtual-threads

Conversation

@csviri

@csviricsviri commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Adds sample (serves as a smoke test) for virtual threads setup of JOSDK.

TODO:

  • check logging / thread local handling

csviriand others added 19 commits July 22, 2026 15:59
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
…us (operator-framework#3480)
Add integration tests that showcases handling explicit state in status. Both for simple managed/secondary resources and using a dependent resource.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* feat: pool informers so controllers and event sources can share them
Every InformerEventSource used to create its own SharedIndexInformer, so an
operator whose controllers all watch the same secondary type - ConfigMap and
Secret being the usual suspects - opened one watch connection and kept one cache
per controller for the very same resources.
Informers are now handed out by an InformerPool obtained from the
ConfigurationService, keyed by an InformerClassifier. Event sources whose
classifiers are equal are backed by one informer; the pool reference counts its
users and stops the informer once the last one releases it. The classifier is
made up of everything that decides what an informer watches and how: the
KubernetesClient instance (compared by identity, since two clients for the same
API server may still differ in credentials, impersonation or TLS material), the
resource class or the group/version/kind for generic resources, the namespace,
the label, field and shard selectors, and the item store.
Two components are deliberately not part of that identity. The informerListLimit
is excluded, so event sources that disagree only on it still share an informer,
keeping the limit of whichever one created it and logging a warning. Indexers are
excluded because they can be added to a running informer: they are registered
under a name qualified with the controller and event source that added them, so
index names stay private to an event source while callers keep using their own
names, and they are removed again when that event source releases the informer.
Two strategies ship: DefaultInformerPool shares as described and is the default,
NonSharingInformerPool creates a dedicated informer per event source for anyone
wanting to opt out. Either is selected with
ConfigurationServiceOverrider#withInformerPool, and a custom strategy extends
AbstractInformerPool, which already creates the informers from a classifier,
starts them and waits for their caches to sync, leaving the subclass only the
question of whether and when an informer is shared.
Consequently informer creation and startup moved out of InformerWrapper and
InformerManager into the pool, InformerManager acquires and releases informers
instead of owning them, and it removes its own event handler and indexers from an
informer that keeps running for others. An event source registered dynamically
against an already running shared informer needs no special handling: the client
replays the cache contents to a newly added handler.
Also in support of the above: ConfigurationService#informerPool, an
InformerEventSource constructor that no longer needs an EventSourceContext (the
one taking it is deprecated), the resource group/version/kind on
InformerConfiguration, equality and toString on FieldSelector, and equality of
GroupVersionKindPlural made consistent with its hashCode so that an unspecified
plural no longer splits informers.
The pooling itself is production ready; the configuration API around it is marked
experimental and may still change. Covered by unit tests for the pools, the
classifier, the wrapper and the manager, and by integration tests for sharing,
dynamic registration and de-registration that each run against both strategies.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
…-framework#3511)
Bumps [org.apache.maven.plugins:maven-jar-plugin](https://github.com/apache/maven-jar-plugin) from 3.5.0 to 3.5.1.
- [Release notes](https://github.com/apache/maven-jar-plugin/releases)
- [Commits](apache/maven-jar-plugin@maven-jar-plugin-3.5.0...maven-jar-plugin-3.5.1)
---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-jar-plugin
dependency-version: 3.5.1
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…-framework#3510)
Bumps `openrewrite.version` from 8.87.5 to 8.87.7.
Updates `org.openrewrite:rewrite-java` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
Updates `org.openrewrite:rewrite-maven` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
Updates `org.openrewrite:rewrite-test` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
Updates `org.openrewrite:rewrite-java-17` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
---
updated-dependencies:
- dependency-name: org.openrewrite:rewrite-java
dependency-version: 8.87.7
dependency-type: direct:production
update-type: version-update:semver-patch
- dependency-name: org.openrewrite:rewrite-maven
dependency-version: 8.87.7
dependency-type: direct:production
update-type: version-update:semver-patch
- dependency-name: org.openrewrite:rewrite-test
dependency-version: 8.87.7
dependency-type: direct:development
update-type: version-update:semver-patch
- dependency-name: org.openrewrite:rewrite-java-17
dependency-version: 8.87.7
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…8.0 to 3.9.0 (operator-framework#3515)
Signed-off-by: dependabot[bot] <support@github.com>
…erator-framework#3514)
Fabric8's no-context patch() fetches the resource from the server and
computes an RFC 6902 JSON Patch diff, which removes the fields omitted
from a partial resource (including the whole spec). Pass an explicit
JSON_MERGE PatchContext so the non-status merge patch operations really
send application/merge-patch+json.
…ator E2E test (operator-framework#3513)
Adds a Maven-compiled Kotlin test source set to operator-framework-core
and a smoke test verifying that a checked (non-RuntimeException) Exception
thrown from a Kotlin DependentResource is properly caught and reported by
the workflow executor, so that retries are triggered as expected.
Kotlin does not have checked exceptions, so Kotlin code can throw a
checked Exception from an overridden method without declaring it, even
though the Java DependentResource#reconcile signature does not declare
`throws Exception`. Before operator-framework#2965 this exception would not have been
caught by NodeExecutor, since it only handled RuntimeException, silently
swallowing the error and preventing retries.
Closesoperator-framework#2967
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* fix: close class brace in Kotlin test; add Kotlin sample operator E2E
Fixes a missing closing brace left in KotlinCheckedExceptionDependentResourceTest.kt.
Adds a new sample-operators/kotlin-operator module: a minimalist Kotlin
operator (ConfigMapCopyReconciler) that copies a value from a custom
resource's spec into a ConfigMap, with a dual-mode (local/remote) E2E
test mirroring the other samples. This verifies deserialization via the
fabric8 client and the full reconciliation runtime when both the CR and
reconciler are implemented in Kotlin, addressing the maintainer's request
for broader coverage than the existing unit-level smoke test.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This needed to be fixed after rebase.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
)
This is expected for a pool to set. Event if the underlying implementation would set it, this is the correct way to handle it.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
…work#3547)
AbstractWorkflowExecutor called Workflow#getDependentResourcesByName purely to
read its size. That allocates a HashMap and walks every node to collect the
dependent resources, then discards the map. The executor is constructed on
every reconcile and cleanup of a workflow-based reconciler, so use the existing
Workflow#size instead.
…perator-framework#3550)
ResourceOperations#desiredForJsonPatch takes an Options argument that it never
reads - it only clones the actual resource and applies the operator. All six
jsonPatch call sites thread the value in for nothing, which suggests the method
still honours the option. Remove the parameter.
…ator-framework#3544)
No behavior change; each site is replaced by a helper that already exists.
- PrimaryUpdateAndCacheUtils#compareResourceVersions (and its private
validateResourceVersion) duplicated the entire algorithm of
ReconcilerUtilsInternal#validateAndCompareResourceVersions: the length-first
compare, the empty check, the leading-zero check and the same exception
messages. It had no production caller, so the copy could silently drift from
the version all production paths use. Delegate instead.
- addFinalizerWithSSA builds its bare SSA skeleton with
HasMetadata#initNameAndNamespaceFrom, which ResourceOperations already uses
for the same purpose and which is Namespaced-aware.
- AbstractInformerPool formats the informer identifier with
ReconcilerUtilsInternal#getResourceTypeNameWithVersion instead of
concatenating the resource name and version by hand.
- EventFilterWindow uses ExtendedResourceEvent#getResourceVersion, which had
no callers even though it is exactly the expression used here.
- LocallyRunOperatorExtension instantiates reconcilers with Utils#instantiate,
so it also supports non-public no-arg constructors and reports the failing
class instead of wrapping in a bare RuntimeException.
…perator-framework#3543)
The event count is increased at the beginning of the reconciliation, thus
waiting for it released the test into the middle of a reconciliation that
was still about to remove the finalizer. The subsequent update then raced
with that removal and failed with a conflict.
Wait for the finalizer removal to actually land, and retry the (optimistically
locked) update with a fresh read on conflict.
…rk#3536)
Add an opt-in, experimental detectApiVersionChange option on
@KubernetesDependent that records the API version the operator applies
in the javaoperatorsdk.io/last-applied-api-version annotation. The
regular matcher then detects a mismatch when that marker differs from
(or is missing relative to) the currently applied API version, causing
a one-time update without triggering repeated reconciliations once the
resource is up-to-date. Disabled by default, so existing behavior and
matching are unaffected unless explicitly enabled.
Guard against a null or immutable annotations map (e.g. Map.of(...))
on the desired resource when detectApiVersionChange is enabled, since
writing the last-applied-api-version marker (and the pre-existing
previous-annotation bookkeeping that runs alongside it) requires a
mutable map. Also rename a misleadingly-named test helper and replace
a no-op assertion on a primitive boolean with a concrete expectation.
Addresses Copilot review feedback on PR operator-framework#3536.
Add DetectApiVersionChangeIT covering the end-to-end scenario: a
ConfigMap dependent resource configured with detectApiVersionChange
is marked with the current API version on creation (without
triggering an update), and a stale marker annotation left on the
actual resource (simulating an older operator/CRD version) is
detected and corrected with exactly one update, after which no
further reconciliation loop occurs.
Signed-off-by: hej090224 <fc49854985@gmail.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
…ator-framework#3548)
InformerManager is generic over C extends Informable, but getTargetClient
type-tested for InformerEventSourceConfiguration to find out whether a specific
(e.g. remote cluster) client was configured. Informable has two implementors
and only one could answer the question, so a third configuration type wanting
its own client would be ignored silently rather than failing to compile.
Move the default getKubernetesClient() up from InformerEventSourceConfiguration
to Informable and let InformerManager ask the configuration directly. The
default still returns Optional.empty(), so existing implementations are
unaffected. As a side effect the ConfigurationService client is now only
created when no specific client is configured, instead of being created and
then discarded.
…rk#3546)
- GenericKubernetesResourceMatcher allocated the path-prefix lists
(List.of(SPEC), List.of(METADATA), the labels/annotations pair, List.of(STATUS))
once per JSON-diff node while matching, and nodeIsChildOf built a stream per
call. Both run for every node of every match, so hoist the lists to constants
and use an indexed loop.
- SSABasedGenericKubernetesResourceMatcher#sanitizeState nested the StatefulSet
volume-claim-template handling four levels deep inside the type ladder;
extract it into sanitizeVolumeClaimTemplates so the ladder reads as one
dispatch per resource kind.
@openshift-ciopenshift-ciBot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 11, 2026
@coderabbitai

coderabbitaiBot commented Aug 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e6fdac0d-13b6-4df3-af4f-3f9ff690efcf

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@openshift-ciopenshift-ciBot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 11, 2026
@csviri
csviri changed the base branch from main to nextAugust 11, 2026 15:49
Requires Java 21, therefore the module is only part of the build when the
JDK used is at least version 21.
Configures both the framework executors (reconciliation and workflow) and
the Kubernetes client task executor with a virtual thread per task
executor.
@openshift-ciopenshift-ciBot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 11, 2026
@csviricsviri linked an issue Aug 11, 2026 that may be closed by this pull request
@csviricsviri changed the title Sample for virtual threadsimprove: sample for virtual threadsAug 11, 2026
@csviri
csviri marked this pull request as ready for review August 12, 2026 06:49
CopilotAI lite review requested due to automatic review settings August 12, 2026 06:49
@openshift-ciopenshift-ciBot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 12, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new sample operator demonstrating how to run JOSDK reconciliations and Fabric8 client async callbacks on Java virtual threads (Java 21+), along with documentation and CI updates so the sample is built/tested appropriately.

Changes:

  • Introduces a new sample-operators/virtual-threads Maven module (Java 21+) with a reconciler, CRD types, logging config, and an E2E test.
  • Adds a JDK-activated Maven profile in sample-operators/pom.xml to include the sample only on JDK 21+.
  • Updates docs and the E2E GitHub Actions workflow to reference and test the new sample.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
sample-operators/virtual-threads/src/test/resources/log4j2.xmlAdds test logging configuration for the virtual threads sample.
sample-operators/virtual-threads/src/test/java/io/javaoperatorsdk/operator/sample/VirtualThreadsOperatorE2E.javaAdds an E2E test validating parallel reconciliations and virtual-thread execution.
sample-operators/virtual-threads/src/main/resources/log4j2.xmlAdds runtime logging configuration for the sample operator.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsStatus.javaAdds CR status fields used by the sample to report observed value and virtual-thread usage.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsSpec.javaAdds CR spec schema for the sample.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsReconciler.javaImplements a reconciler that simulates blocking work and patches status.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsOperator.javaAdds the sample operator entry point wiring virtual-thread executors and client config.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsCustomResource.javaDefines the sample CustomResource type and group/version metadata.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreads.javaCentralizes virtual-thread configuration for both framework executors and Fabric8 client task executor.
sample-operators/virtual-threads/README.mdDocuments the sample, Java 21+ requirement, and how to run it.
sample-operators/virtual-threads/pom.xmlIntroduces the new Java 21+ sample module build and dependencies.
sample-operators/virtual-threads/k8s/virtual-threads-custom-resource.yamlAdds a sample CR manifest for running the operator manually.
sample-operators/virtual-threads/k8s/operator.yamlAdds a deployment + RBAC manifest used by remote E2E mode.
sample-operators/pom.xmlAdds a JDK>=21-activated profile to include the new sample module.
docs/content/en/docs/getting-started/bootstrap-and-samples.mdAdds the virtual-threads sample to the list of available samples.
docs/content/en/docs/documentation/operations/configuration.mdDocuments how to run JOSDK on virtual threads (framework + client executor).
.github/workflows/e2e-test.ymlAdds the new sample module to the E2E workflow matrix (running on JDK 25).

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment threaddocs/content/en/docs/getting-started/bootstrap-and-samples.md Outdated
Comment on lines +66 to +70
@Override
public void onClose(Executor executor) {
((ExecutorService) executor).shutdownNow();
}
}
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@csviri
csviri marked this pull request as draft August 12, 2026 08:54
@openshift-ciopenshift-ciBot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 12, 2026
Comment on lines +30 to +31
can be replaced with virtual threads, so that a reconciliation waiting on a remote call does not
occupy a platform thread:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
can be replaced with virtual threads, so that a reconciliation waiting on a remote call does not
occupy a platform thread:
can be replaced with virtual threads.

Each virtual thread always runs on platform thread in the background.

@csviricsviri changed the title improve: sample for virtual threadstest: sample for virtual threadsAug 13, 2026
@openshift-ciopenshift-ciBot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 28, 2026
@openshift-ci

Copy link
Copy Markdown

PR needs rebase.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@csviri
csviriforce-pushed the next branch 2 times, most recently from 5ccac4d to 478199eCompareSeptember 3, 2026 11:40
@csviri
csviri deleted the branch operator-framework:nextSeptember 3, 2026 13:15
@csviricsviri closed this Sep 3, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge/work-in-progressIndicates that a PR should not merge because it is a work in progress.needs-rebaseIndicates a PR cannot be merged because it has merge conflicts with HEAD.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for Virtual Threads

4 participants

@csviri@xstefank@exijn
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

test: sample for virtual threads - #3554

Closed
csviri wants to merge 21 commits into
operator-framework:nextfrom
csviri:virtual-threads
Closed

test: sample for virtual threads#3554
csviri wants to merge 21 commits into
operator-framework:nextfrom
csviri:virtual-threads

Conversation

@csviri

@csviricsviri commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Adds sample (serves as a smoke test) for virtual threads setup of JOSDK.

TODO:

  • check logging / thread local handling

csviriand others added 19 commits July 22, 2026 15:59
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
…us (operator-framework#3480)
Add integration tests that showcases handling explicit state in status. Both for simple managed/secondary resources and using a dependent resource.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* feat: pool informers so controllers and event sources can share them
Every InformerEventSource used to create its own SharedIndexInformer, so an
operator whose controllers all watch the same secondary type - ConfigMap and
Secret being the usual suspects - opened one watch connection and kept one cache
per controller for the very same resources.
Informers are now handed out by an InformerPool obtained from the
ConfigurationService, keyed by an InformerClassifier. Event sources whose
classifiers are equal are backed by one informer; the pool reference counts its
users and stops the informer once the last one releases it. The classifier is
made up of everything that decides what an informer watches and how: the
KubernetesClient instance (compared by identity, since two clients for the same
API server may still differ in credentials, impersonation or TLS material), the
resource class or the group/version/kind for generic resources, the namespace,
the label, field and shard selectors, and the item store.
Two components are deliberately not part of that identity. The informerListLimit
is excluded, so event sources that disagree only on it still share an informer,
keeping the limit of whichever one created it and logging a warning. Indexers are
excluded because they can be added to a running informer: they are registered
under a name qualified with the controller and event source that added them, so
index names stay private to an event source while callers keep using their own
names, and they are removed again when that event source releases the informer.
Two strategies ship: DefaultInformerPool shares as described and is the default,
NonSharingInformerPool creates a dedicated informer per event source for anyone
wanting to opt out. Either is selected with
ConfigurationServiceOverrider#withInformerPool, and a custom strategy extends
AbstractInformerPool, which already creates the informers from a classifier,
starts them and waits for their caches to sync, leaving the subclass only the
question of whether and when an informer is shared.
Consequently informer creation and startup moved out of InformerWrapper and
InformerManager into the pool, InformerManager acquires and releases informers
instead of owning them, and it removes its own event handler and indexers from an
informer that keeps running for others. An event source registered dynamically
against an already running shared informer needs no special handling: the client
replays the cache contents to a newly added handler.
Also in support of the above: ConfigurationService#informerPool, an
InformerEventSource constructor that no longer needs an EventSourceContext (the
one taking it is deprecated), the resource group/version/kind on
InformerConfiguration, equality and toString on FieldSelector, and equality of
GroupVersionKindPlural made consistent with its hashCode so that an unspecified
plural no longer splits informers.
The pooling itself is production ready; the configuration API around it is marked
experimental and may still change. Covered by unit tests for the pools, the
classifier, the wrapper and the manager, and by integration tests for sharing,
dynamic registration and de-registration that each run against both strategies.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
…-framework#3511)
Bumps [org.apache.maven.plugins:maven-jar-plugin](https://github.com/apache/maven-jar-plugin) from 3.5.0 to 3.5.1.
- [Release notes](https://github.com/apache/maven-jar-plugin/releases)
- [Commits](apache/maven-jar-plugin@maven-jar-plugin-3.5.0...maven-jar-plugin-3.5.1)
---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-jar-plugin
dependency-version: 3.5.1
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…-framework#3510)
Bumps `openrewrite.version` from 8.87.5 to 8.87.7.
Updates `org.openrewrite:rewrite-java` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
Updates `org.openrewrite:rewrite-maven` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
Updates `org.openrewrite:rewrite-test` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
Updates `org.openrewrite:rewrite-java-17` from 8.87.5 to 8.87.7
- [Release notes](https://github.com/openrewrite/rewrite/releases)
- [Commits](openrewrite/rewrite@v8.87.5...v8.87.7)
---
updated-dependencies:
- dependency-name: org.openrewrite:rewrite-java
dependency-version: 8.87.7
dependency-type: direct:production
update-type: version-update:semver-patch
- dependency-name: org.openrewrite:rewrite-maven
dependency-version: 8.87.7
dependency-type: direct:production
update-type: version-update:semver-patch
- dependency-name: org.openrewrite:rewrite-test
dependency-version: 8.87.7
dependency-type: direct:development
update-type: version-update:semver-patch
- dependency-name: org.openrewrite:rewrite-java-17
dependency-version: 8.87.7
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…8.0 to 3.9.0 (operator-framework#3515)
Signed-off-by: dependabot[bot] <support@github.com>
…erator-framework#3514)
Fabric8's no-context patch() fetches the resource from the server and
computes an RFC 6902 JSON Patch diff, which removes the fields omitted
from a partial resource (including the whole spec). Pass an explicit
JSON_MERGE PatchContext so the non-status merge patch operations really
send application/merge-patch+json.
…ator E2E test (operator-framework#3513)
Adds a Maven-compiled Kotlin test source set to operator-framework-core
and a smoke test verifying that a checked (non-RuntimeException) Exception
thrown from a Kotlin DependentResource is properly caught and reported by
the workflow executor, so that retries are triggered as expected.
Kotlin does not have checked exceptions, so Kotlin code can throw a
checked Exception from an overridden method without declaring it, even
though the Java DependentResource#reconcile signature does not declare
`throws Exception`. Before operator-framework#2965 this exception would not have been
caught by NodeExecutor, since it only handled RuntimeException, silently
swallowing the error and preventing retries.
Closesoperator-framework#2967
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* fix: close class brace in Kotlin test; add Kotlin sample operator E2E
Fixes a missing closing brace left in KotlinCheckedExceptionDependentResourceTest.kt.
Adds a new sample-operators/kotlin-operator module: a minimalist Kotlin
operator (ConfigMapCopyReconciler) that copies a value from a custom
resource's spec into a ConfigMap, with a dual-mode (local/remote) E2E
test mirroring the other samples. This verifies deserialization via the
fabric8 client and the full reconciliation runtime when both the CR and
reconciler are implemented in Kotlin, addressing the maintainer's request
for broader coverage than the existing unit-level smoke test.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This needed to be fixed after rebase.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
)
This is expected for a pool to set. Event if the underlying implementation would set it, this is the correct way to handle it.
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
…work#3547)
AbstractWorkflowExecutor called Workflow#getDependentResourcesByName purely to
read its size. That allocates a HashMap and walks every node to collect the
dependent resources, then discards the map. The executor is constructed on
every reconcile and cleanup of a workflow-based reconciler, so use the existing
Workflow#size instead.
…perator-framework#3550)
ResourceOperations#desiredForJsonPatch takes an Options argument that it never
reads - it only clones the actual resource and applies the operator. All six
jsonPatch call sites thread the value in for nothing, which suggests the method
still honours the option. Remove the parameter.
…ator-framework#3544)
No behavior change; each site is replaced by a helper that already exists.
- PrimaryUpdateAndCacheUtils#compareResourceVersions (and its private
validateResourceVersion) duplicated the entire algorithm of
ReconcilerUtilsInternal#validateAndCompareResourceVersions: the length-first
compare, the empty check, the leading-zero check and the same exception
messages. It had no production caller, so the copy could silently drift from
the version all production paths use. Delegate instead.
- addFinalizerWithSSA builds its bare SSA skeleton with
HasMetadata#initNameAndNamespaceFrom, which ResourceOperations already uses
for the same purpose and which is Namespaced-aware.
- AbstractInformerPool formats the informer identifier with
ReconcilerUtilsInternal#getResourceTypeNameWithVersion instead of
concatenating the resource name and version by hand.
- EventFilterWindow uses ExtendedResourceEvent#getResourceVersion, which had
no callers even though it is exactly the expression used here.
- LocallyRunOperatorExtension instantiates reconcilers with Utils#instantiate,
so it also supports non-public no-arg constructors and reports the failing
class instead of wrapping in a bare RuntimeException.
…perator-framework#3543)
The event count is increased at the beginning of the reconciliation, thus
waiting for it released the test into the middle of a reconciliation that
was still about to remove the finalizer. The subsequent update then raced
with that removal and failed with a conflict.
Wait for the finalizer removal to actually land, and retry the (optimistically
locked) update with a fresh read on conflict.
…rk#3536)
Add an opt-in, experimental detectApiVersionChange option on
@KubernetesDependent that records the API version the operator applies
in the javaoperatorsdk.io/last-applied-api-version annotation. The
regular matcher then detects a mismatch when that marker differs from
(or is missing relative to) the currently applied API version, causing
a one-time update without triggering repeated reconciliations once the
resource is up-to-date. Disabled by default, so existing behavior and
matching are unaffected unless explicitly enabled.
Guard against a null or immutable annotations map (e.g. Map.of(...))
on the desired resource when detectApiVersionChange is enabled, since
writing the last-applied-api-version marker (and the pre-existing
previous-annotation bookkeeping that runs alongside it) requires a
mutable map. Also rename a misleadingly-named test helper and replace
a no-op assertion on a primitive boolean with a concrete expectation.
Addresses Copilot review feedback on PR operator-framework#3536.
Add DetectApiVersionChangeIT covering the end-to-end scenario: a
ConfigMap dependent resource configured with detectApiVersionChange
is marked with the current API version on creation (without
triggering an update), and a stale marker annotation left on the
actual resource (simulating an older operator/CRD version) is
detected and corrected with exactly one update, after which no
further reconciliation loop occurs.
Signed-off-by: hej090224 <fc49854985@gmail.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
…ator-framework#3548)
InformerManager is generic over C extends Informable, but getTargetClient
type-tested for InformerEventSourceConfiguration to find out whether a specific
(e.g. remote cluster) client was configured. Informable has two implementors
and only one could answer the question, so a third configuration type wanting
its own client would be ignored silently rather than failing to compile.
Move the default getKubernetesClient() up from InformerEventSourceConfiguration
to Informable and let InformerManager ask the configuration directly. The
default still returns Optional.empty(), so existing implementations are
unaffected. As a side effect the ConfigurationService client is now only
created when no specific client is configured, instead of being created and
then discarded.
…rk#3546)
- GenericKubernetesResourceMatcher allocated the path-prefix lists
(List.of(SPEC), List.of(METADATA), the labels/annotations pair, List.of(STATUS))
once per JSON-diff node while matching, and nodeIsChildOf built a stream per
call. Both run for every node of every match, so hoist the lists to constants
and use an indexed loop.
- SSABasedGenericKubernetesResourceMatcher#sanitizeState nested the StatefulSet
volume-claim-template handling four levels deep inside the type ladder;
extract it into sanitizeVolumeClaimTemplates so the ladder reads as one
dispatch per resource kind.
@openshift-ciopenshift-ciBot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 11, 2026
@coderabbitai

coderabbitaiBot commented Aug 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e6fdac0d-13b6-4df3-af4f-3f9ff690efcf

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@openshift-ciopenshift-ciBot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 11, 2026
@csviri
csviri changed the base branch from main to nextAugust 11, 2026 15:49
Requires Java 21, therefore the module is only part of the build when the
JDK used is at least version 21.
Configures both the framework executors (reconciliation and workflow) and
the Kubernetes client task executor with a virtual thread per task
executor.
@openshift-ciopenshift-ciBot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 11, 2026
@csviricsviri linked an issue Aug 11, 2026 that may be closed by this pull request
@csviricsviri changed the title Sample for virtual threadsimprove: sample for virtual threadsAug 11, 2026
@csviri
csviri marked this pull request as ready for review August 12, 2026 06:49
CopilotAI lite review requested due to automatic review settings August 12, 2026 06:49
@openshift-ciopenshift-ciBot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 12, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new sample operator demonstrating how to run JOSDK reconciliations and Fabric8 client async callbacks on Java virtual threads (Java 21+), along with documentation and CI updates so the sample is built/tested appropriately.

Changes:

  • Introduces a new sample-operators/virtual-threads Maven module (Java 21+) with a reconciler, CRD types, logging config, and an E2E test.
  • Adds a JDK-activated Maven profile in sample-operators/pom.xml to include the sample only on JDK 21+.
  • Updates docs and the E2E GitHub Actions workflow to reference and test the new sample.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
sample-operators/virtual-threads/src/test/resources/log4j2.xmlAdds test logging configuration for the virtual threads sample.
sample-operators/virtual-threads/src/test/java/io/javaoperatorsdk/operator/sample/VirtualThreadsOperatorE2E.javaAdds an E2E test validating parallel reconciliations and virtual-thread execution.
sample-operators/virtual-threads/src/main/resources/log4j2.xmlAdds runtime logging configuration for the sample operator.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsStatus.javaAdds CR status fields used by the sample to report observed value and virtual-thread usage.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsSpec.javaAdds CR spec schema for the sample.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsReconciler.javaImplements a reconciler that simulates blocking work and patches status.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsOperator.javaAdds the sample operator entry point wiring virtual-thread executors and client config.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreadsCustomResource.javaDefines the sample CustomResource type and group/version metadata.
sample-operators/virtual-threads/src/main/java/io/javaoperatorsdk/operator/sample/VirtualThreads.javaCentralizes virtual-thread configuration for both framework executors and Fabric8 client task executor.
sample-operators/virtual-threads/README.mdDocuments the sample, Java 21+ requirement, and how to run it.
sample-operators/virtual-threads/pom.xmlIntroduces the new Java 21+ sample module build and dependencies.
sample-operators/virtual-threads/k8s/virtual-threads-custom-resource.yamlAdds a sample CR manifest for running the operator manually.
sample-operators/virtual-threads/k8s/operator.yamlAdds a deployment + RBAC manifest used by remote E2E mode.
sample-operators/pom.xmlAdds a JDK>=21-activated profile to include the new sample module.
docs/content/en/docs/getting-started/bootstrap-and-samples.mdAdds the virtual-threads sample to the list of available samples.
docs/content/en/docs/documentation/operations/configuration.mdDocuments how to run JOSDK on virtual threads (framework + client executor).
.github/workflows/e2e-test.ymlAdds the new sample module to the E2E workflow matrix (running on JDK 25).

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment threaddocs/content/en/docs/getting-started/bootstrap-and-samples.md Outdated
Comment on lines +66 to +70
@Override
public void onClose(Executor executor) {
((ExecutorService) executor).shutdownNow();
}
}
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@csviri
csviri marked this pull request as draft August 12, 2026 08:54
@openshift-ciopenshift-ciBot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 12, 2026
Comment on lines +30 to +31
can be replaced with virtual threads, so that a reconciliation waiting on a remote call does not
occupy a platform thread:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
can be replaced with virtual threads, so that a reconciliation waiting on a remote call does not
occupy a platform thread:
can be replaced with virtual threads.

Each virtual thread always runs on platform thread in the background.

@csviricsviri changed the title improve: sample for virtual threadstest: sample for virtual threadsAug 13, 2026
@openshift-ciopenshift-ciBot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 28, 2026
@openshift-ci

Copy link
Copy Markdown

PR needs rebase.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@csviri
csviriforce-pushed the next branch 2 times, most recently from 5ccac4d to 478199eCompareSeptember 3, 2026 11:40
@csviri
csviri deleted the branch operator-framework:nextSeptember 3, 2026 13:15
@csviricsviri closed this Sep 3, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge/work-in-progressIndicates that a PR should not merge because it is a work in progress.needs-rebaseIndicates a PR cannot be merged because it has merge conflicts with HEAD.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for Virtual Threads

4 participants

@csviri@xstefank@exijn