Skip to content

Reduce cache memory footprint by optimizing what we store - #2886

Open
kvalliyurnatt wants to merge 1 commit into
NVIDIA:mainfrom
kvalliyurnatt:reduce-operator-cache-memory
Open

kvalliyurnatt wants to merge 1 commit into
NVIDIA:mainfrom
kvalliyurnatt:reduce-operator-cache-memory

Conversation

@kvalliyurnatt

@kvalliyurnatt kvalliyurnatt commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Description

We can optimize what we store in cache today to optimize operator's memory usage.

  • Strip metadata.managedFields from cached objects and the unused status.images inventory from Nodes.
  • Restrict the CRD cache to the ServiceMonitor CRD.
  • Restrict the ImageStream cache to openshift/driver-toolkit.
  • Default other namespaced caches to the operator namespace, preserving existing Pod and ResourceClaim namespace coverage for DRA teardown checks.

Checklist

  • No secrets, sensitive information, or unrelated changes
  • Lint checks passing (make lint)
  • Generated assets in-sync (make validate-generated-assets)
  • Go mod artifacts in-sync (make validate-modules)
  • Test cases are added for new code paths

Testing

I have added some unit tests, but I plan to test with and without the changes to get a baseline of how much memory we can save here.

Cache memory validation results

Compared candidate 6f1405d against its exact parent 9f85a2a, using the same Go 1.27.1 compiler, dependencies, runtime base, Helm chart, operand versions, and operator resource limits.

Test setup

  • Two Kubernetes nodes with three NVIDIA L4 GPUs and 40 CRDs.
  • Fresh installations, at least 10 minutes of warm-up, and 15-minute idle
    measurement windows with 30-second sampling.
  • Profiling disabled during performance measurements.
  • One interrupted candidate capture was excluded; a complete replacement
    window was collected.

Steady-state results

Metric Parent Candidate Change
Process RSS 71.13 MiB 58.51 MiB 17.7% lower
Go live heap 16.73 MiB 8.43 MiB 49.6% lower
Container working set 36.65 MiB 23.24 MiB 36.6% lower
Process CPU 1.92 millicores 1.90 millicores Essentially unchanged
Operator API requests 0.582/sec 0.570/sec No observed increase
Goroutines 231 222 9 fewer

Both windows completed with zero operator restarts or reconciliation errors.
ClusterPolicy remained ready, expected operands were healthy, all three GPUs
remained allocatable, and CUDA vector-add validation passed on both nodes.

pprof observations

Separate warmed-up profiling runs captured:

Measurement Parent Candidate
Sampled retained heap after GC 14.88 MiB 8.70 MiB
Retained allocations originating in watch decoding, cumulative 6.49 MiB 0.50 MiB

The parent profile includes retained CRD-schema and decoding allocations,
consistent with the broader cache. These are sampled allocation-site estimates,
not exact cache-ownership measurements. Short idle CPU/allocation profiles were
insufficient for reliable hotspot comparisons.

Scope

These results demonstrate lower idle memory without an observed API-load increase
in this two-node test. DRA, and OpenShift validation remain outstanding.

Signed-off-by: Karthikeyan Valliyurnatt <kvalliyurnat@nvidia.com>
@kvalliyurnatt
kvalliyurnatt force-pushed the reduce-operator-cache-memory branch from 8793ed4 to 6f1405d Compare September 11, 2026 16:17
@kvalliyurnatt kvalliyurnatt changed the title reduce cache memory footprint by optimizing what we store Reduce cache memory footprint by optimizing what we store Sep 11, 2026
@kvalliyurnatt
kvalliyurnatt marked this pull request as ready for review September 11, 2026 18:46
@coderabbitai

coderabbitai Bot commented Sep 11, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

📝 Walkthrough

Walkthrough

The change adds centralized GPU Operator cache construction and configuration. The cache handles optional ImageStream and ResourceClaim APIs, propagates other discovery errors, scopes selected resources to workload namespaces, strips managed fields, and filters cached objects. Manager setup now uses the shared options and custom cache constructor. Tests cover discovery behavior, API watches, synchronization, selectors, object transformations, and repeated cached reads.

Priority: ➖ Normal

Merge Risk: 🟡 Moderate · up to 6f140

DRA workloads outside the operator namespaces can be left stuck terminating during a driver transition. Fix the cache scope and provide the required Go validation results before merge.


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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

🟡 Other comments (1)
cmd/gpu-operator/cache.go-38-38 (1)

38-38: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Can you provide the make fmt and make unit-test results before merge?

Repository guidance requires both checks for Go changes, and the current PR evidence does not include their results.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: QUIET

Plan: Enterprise

Run ID: 0124bc9d-82bc-471d-b46e-41b6d6682f6d

📥 Commits

Reviewing files that changed from the base of the PR and between 9f85a2a and 6f1405d.

📒 Files selected for processing (3)
  • cmd/gpu-operator/cache.go
  • cmd/gpu-operator/cache_test.go
  • cmd/gpu-operator/main.go

Included review availability: Your plan provides up to 12 included reviews per hour; 11 remain after this review.

Comment thread cmd/gpu-operator/cache.go
Comment on lines +60 to +96
func operatorCacheOptions(operatorNamespace string) cache.Options {
stripManagedFields := cache.TransformStripManagedFields()
// DRA teardown checks inspect Pods and their ResourceClaims across the existing
// cache namespaces. Preserve that scope independently of operand-only resources.
workloadNamespaces := map[string]cache.Config{
operatorNamespace: {},
consts.OpenshiftNamespace: {},
}
return cache.Options{
DefaultNamespaces: map[string]cache.Config{operatorNamespace: {}},
DefaultTransform: stripManagedFields,
ByObject: map[client.Object]cache.ByObject{
&corev1.Node{}: {
// Keep all nodes for GPU discovery and label cleanup, but omit the
// image inventory, which no operator consumer uses.
Transform: func(in any) (any, error) {
if node, ok := in.(*corev1.Node); ok {
node.Status.Images = nil
}
return stripManagedFields(in)
},
},
&corev1.Pod{}: {Namespaces: workloadNamespaces},
&resourcev1.ResourceClaim{}: {Namespaces: workloadNamespaces},
&apiextensionsv1.CustomResourceDefinition{}: {
// crdExists only checks ServiceMonitor availability. Apply the
// selector to LIST/WATCH so other CRD schemas never enter the cache.
Field: fields.OneTermEqualSelector("metadata.name", controllers.ServiceMonitorCRDName),
},
&apiimagev1.ImageStream{}: {
// Only the Driver Toolkit ImageStream is read from openshift.
Namespaces: map[string]cache.Config{consts.OpenshiftNamespace: {}},
Field: fields.OneTermEqualSelector("metadata.name", "driver-toolkit"),
},
},
}
}

@coderabbitai coderabbitai Bot Sep 11, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Cache Pods and ResourceClaims cluster-wide for DRA teardown

operatorCacheOptions caches both types only in the operator and OpenShift namespaces. During the DRA-to-device-plugin transition, nodeHasDRAClaimPods therefore misses claim-bearing Pods in other namespaces, removes gpu.deploy.dra-driver, and can leave those Pods stuck in Terminating when their claims cannot be unprepared. Configure both cache entries for cluster-wide coverage.

@kvalliyurnatt kvalliyurnatt Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I noticed this as well and this seems to be a pre existing bug even before these changes, I am happy to fix it in this PR or carve out a separate PR for that. This will increase our memory footprint though, since we will end up caching all Pods and ResourceClaims in all namespaces. I can look to see there are ways to better than that

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@kvalliyurnatt, please include the cluster-wide Pod and ResourceClaim cache coverage in this PR.

A separate PR is appropriate only for unrelated cleanup or broader pre-existing DRA behavior. This change must prevent nodeHasDRAClaimPods from missing claim-bearing Pods outside the operator and OpenShift namespaces.

You are interacting with an AI system.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Devin Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant