Skip to content

Conversation

@bhanvimenghani
Copy link
Contributor

@bhanvimenghani bhanvimenghani commented Dec 5, 2025

Description

This PR introduces the foundational Layer data structure for Kruize's runtime recommendations feature.

New Layer Core Components

  • LayerMetadata.java: Metadata about the layer
  • LayerPresence.java: Defines how layers are detected in workloads
  • LayerDetector.java: Detection logic for layer presence

Removed old layer implementations. These are replaced by the new generic Layer data structure that can represent any layer type through configuration.

Fixes # (issue)

Type of change

  • Bug fix
  • New feature
  • Docs update
  • Breaking change (What changes might users need to make in their application due to this PR?)
  • Requires DB changes

How has this been tested?

Please describe the tests that were run to verify your changes and steps to reproduce. Please specify any test configuration required.

  • New Test X
  • Functional testsuite

Test Configuration

  • Kubernetes clusters tested on:

Checklist 🎯

  • Followed coding guidelines
  • Comments added
  • Dependent changes merged
  • Documentation updated
  • Tests added or updated

Additional information

Include any additional information such as links, test results, screenshots here

Summary by Sourcery

Introduce a new core data model for Kruize layers to support configurable runtime layer definitions and tunables.

New Features:

  • Add a Layer entity representing a generic, configurable Kruize layer with metadata, presence, and tunables.
  • Define LayerTunable to describe individual tunable parameters, including type, bounds, and choices.
  • Introduce LayerPresence and LayerDetector abstractions to capture label- and query-based layer detection strategies.
  • Add LayerMetadata to hold basic identifying information for layers.

@bhanvimenghani bhanvimenghani self-assigned this Dec 5, 2025
@bhanvimenghani bhanvimenghani marked this pull request as draft December 5, 2025 09:08
@bhanvimenghani bhanvimenghani requested review from bharathappali and removed request for khansaad December 5, 2025 09:09
@bhanvimenghani bhanvimenghani marked this pull request as ready for review December 8, 2025 08:02
@bhanvimenghani bhanvimenghani added this to the Kruize 0.9 Release milestone Dec 8, 2025
Copy link
Member

@bharathappali bharathappali left a comment

Choose a reason for hiding this comment

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

Needs some changes related to copyrights comment and some abstractions

@@ -0,0 +1,125 @@
package com.autotune.analyzer.kruizeLayer;
Copy link
Member

Choose a reason for hiding this comment

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

Thanks for the PR @bhanvimenghani

Please kindly add the copyrights comment as this is a new file.

// For query-based detection
private String datasource;
private String query;
private String key;
Copy link
Member

Choose a reason for hiding this comment

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

Is key required?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The key has same value for all queries. But this was mentioned in the reference yaml hence i added it https://github.com/kruize/autotune/blob/master/manifests/autotune/autotune-configs/hotspot-micrometer-config.yaml

private String query;
private String key;

// For label-based detection
Copy link
Member

Choose a reason for hiding this comment

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

I feel this class would be populating either a datasource, query attributes or the name and value attributes. Would it be a good idea to maintain an interface which holds the getType which can either be a query based implementation or a label based implementation, so that we can create a particular class implementation based on the type, rather than deciding at runtime by check if certain attributes are null.

Please let me know your thoughts on this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, we can have an interface, just thinking if i should create a separate pr for it so that this one does not gets too long.

Copy link
Member

Choose a reason for hiding this comment

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

yeah a adding changes in a new PR makes more sense. Will approve this one and we can make changes on top of it

@@ -0,0 +1,82 @@
package com.autotune.analyzer.kruizeLayer;
Copy link
Member

Choose a reason for hiding this comment

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

Please kindly add the copyright comment.

@@ -0,0 +1,21 @@
package com.autotune.analyzer.kruizeLayer;
Copy link
Member

Choose a reason for hiding this comment

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

Please kindly add the copyright comment.



public class LayerMetadata {
private String name;
Copy link
Member

Choose a reason for hiding this comment

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

I see a high level field name in the Layer class, Are both these names the same?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes both metadata > name and layer_name have the same value I am following this yaml to create the fields https://github.com/kruize/autotune/blob/master/manifests/autotune/autotune-configs/openj9-actuator-config.yaml

@@ -0,0 +1,63 @@
package com.autotune.analyzer.kruizeLayer;
Copy link
Member

Choose a reason for hiding this comment

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

Please kindly add the copyright comment.

@@ -0,0 +1,88 @@
package com.autotune.analyzer.kruizeLayer;
Copy link
Member

Choose a reason for hiding this comment

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

Please kindly add the copyright comment.

@bharathappali bharathappali self-requested a review December 10, 2025 07:41
Copy link
Member

@bharathappali bharathappali left a comment

Choose a reason for hiding this comment

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

Agreed to add the structural changes as a different PR, LGTM

@dinogun
Copy link
Contributor

dinogun commented Dec 10, 2025

@sourcery-ai review

@sourcery-ai
Copy link

sourcery-ai bot commented Dec 10, 2025

Reviewer's Guide

Introduces a new generic Kruize Layer core model with metadata, presence detection, and tunable definitions to support runtime-agnostic layer configuration, including detectors for both label- and query-based discovery.

Class diagram for new Kruize Layer core data model

classDiagram
    class Layer {
        +String apiVersion
        +String kind
        +LayerMetadata metadata
        +String name
        +String layerName
        +Integer layerLevel
        +String details
        +LayerPresence layerPresence
        +List~LayerTunable~ tunables
        +Layer()
        +Layer(apiVersion String, kind String, metadata LayerMetadata, layerPresence LayerPresence, tunables List~LayerTunable~)
        +String getApiVersion()
        +void setApiVersion(apiVersion String)
        +String getKind()
        +void setKind(kind String)
        +LayerMetadata getMetadata()
        +void setMetadata(metadata LayerMetadata)
        +LayerPresence getLayerPresence()
        +void setLayerPresence(layerPresence LayerPresence)
        +List~LayerTunable~ getTunables()
        +void setTunables(tunables List~LayerTunable~)
        +String getName()
        +void setName(name String)
        +String getLayerName()
        +void setLayerName(layerName String)
        +Integer getLayerLevel()
        +void setLayerLevel(layerLevel Integer)
        +String getDetails()
        +void setDetails(details String)
        +String toString()
    }

    class LayerMetadata {
        +String name
        +String getName()
        +void setName(name String)
        +String toString()
    }

    class LayerPresence {
        +String presence
        +List~LayerDetector~ labels
        +List~LayerDetector~ queries
        +LayerPresence()
        +String getPresence()
        +void setPresence(presence String)
        +List~LayerDetector~ getLabels()
        +void setLabels(labels List~LayerDetector~)
        +List~LayerDetector~ getQueries()
        +void setQueries(queries List~LayerDetector~)
        +List~LayerDetector~ getActiveDetectors()
        +String toString()
    }

    class LayerDetector {
        +String datasource
        +String query
        +String key
        +String name
        +String value
        +LayerDetector()
        +LayerDetector(datasource String, query String, key String)
        +LayerDetector(name String, value String)
        +String getDatasource()
        +void setDatasource(datasource String)
        +String getQuery()
        +void setQuery(query String)
        +String getKey()
        +void setKey(key String)
        +String getName()
        +void setName(name String)
        +String getValue()
        +void setValue(value String)
        +String toString()
    }

    class LayerTunable {
        +String name
        +String description
        +String valueType
        +List~String~ choices
        +String upperBound
        +String lowerBound
        +Double step
        +String getName()
        +void setName(name String)
        +String getDescription()
        +void setDescription(description String)
        +String getValueType()
        +void setValueType(valueType String)
        +List~String~ getChoices()
        +void setChoices(choices List~String~)
        +String getUpperBound()
        +void setUpperBound(upperBound String)
        +String getLowerBound()
        +void setLowerBound(lowerBound String)
        +Double getStep()
        +void setStep(step Double)
        +String toString()
    }

    Layer "1" --> "1" LayerMetadata : has
    Layer "1" --> "1" LayerPresence : has
    Layer "1" --> "*" LayerTunable : defines
    LayerPresence "1" --> "*" LayerDetector : labels
    LayerPresence "1" --> "*" LayerDetector : queries
Loading

File-Level Changes

Change Details Files
Add core Layer aggregate model as the main serialized representation of a Kruize layer
  • Define Layer fields for apiVersion, kind, metadata, name, layer_name, layer_level, details, layer_presence, and tunables
  • Use Gson @SerializedName to support both legacy and new field names for JSON/YAML compatibility
  • Provide constructors, getters/setters, and a toString implementation for logging and debugging
src/main/java/com/autotune/analyzer/kruizeLayer/Layer.java
Introduce tunable parameter model used within layers
  • Define LayerTunable fields for name, description, value_type, choices, upper_bound, lower_bound, and step
  • Annotate snake_case JSON fields with @SerializedName for serialization compatibility
  • Add standard getters/setters and toString for diagnostics
src/main/java/com/autotune/analyzer/kruizeLayer/LayerTunable.java
Add detector configuration model for identifying layer presence via metrics queries or labels
  • Model query-based detection with datasource, query, and key fields
  • Model label-based detection with name and value fields and provide overloaded constructors for each mode
  • Expose getters/setters and toString for use in future detection logic
src/main/java/com/autotune/analyzer/kruizeLayer/LayerDetector.java
Add LayerPresence wrapper that ties together presence mode and configured detectors
  • Define presence string plus label- and query-based detector collections
  • Add getActiveDetectors() helper to pick the first non-empty detector list and avoid nulls
  • Provide getters/setters and toString for downstream consumers
src/main/java/com/autotune/analyzer/kruizeLayer/LayerPresence.java
Add minimal metadata model for layer resources
  • Create LayerMetadata with a name field and standard accessors
  • Provide toString implementation for logging
src/main/java/com/autotune/analyzer/kruizeLayer/LayerMetadata.java

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes - here's some feedback:

  • In Layer, there are multiple fields that represent a name (name, layerName, and metadata.name); consider consolidating or clearly distinguishing their responsibilities to avoid ambiguity and inconsistent usage later.
  • The LayerDetector class mixes query-based and label-based detection fields in a single type; introducing an explicit discriminator (e.g., an enum detector type or separate subclasses) would make it harder to create invalid combinations and simplify downstream logic.
  • In LayerPresence#getActiveDetectors, consider returning Collections.emptyList() instead of allocating a new ArrayList on the empty case to reduce unnecessary allocations.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `Layer`, there are multiple fields that represent a name (`name`, `layerName`, and `metadata.name`); consider consolidating or clearly distinguishing their responsibilities to avoid ambiguity and inconsistent usage later.
- The `LayerDetector` class mixes query-based and label-based detection fields in a single type; introducing an explicit discriminator (e.g., an enum detector type or separate subclasses) would make it harder to create invalid combinations and simplify downstream logic.
- In `LayerPresence#getActiveDetectors`, consider returning `Collections.emptyList()` instead of allocating a new `ArrayList` on the empty case to reduce unnecessary allocations.

## Individual Comments

### Comment 1
<location> `src/main/java/com/autotune/analyzer/kruizeLayer/Layer.java:24` </location>
<code_context>
+    private String details;
+    @SerializedName("layer_presence")
+    private LayerPresence layerPresence;
+    private List<LayerTunable> tunables;
+
+
</code_context>

<issue_to_address>
**suggestion:** Consider default-initializing `tunables` to an empty list to reduce null-handling overhead.

As it stands, `tunables` may be `null` when using the no-arg constructor or when deserialization omits the field, forcing callers to add null checks before iterating or adding elements. Initializing it to `Collections.emptyList()` or a mutable empty list (if mutation is expected) makes the type safer to consume and avoids this extra defensive code.

Suggested implementation:

```java
import com.google.gson.annotations.SerializedName;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

```

```java
    @SerializedName("layer_level")
    private Integer layerLevel;
    private String details;
    @SerializedName("layer_presence")
    private LayerPresence layerPresence;
    private List<LayerTunable> tunables = new ArrayList<>();

```
</issue_to_address>

### Comment 2
<location> `src/main/java/com/autotune/analyzer/kruizeLayer/LayerPresence.java:61-68` </location>
<code_context>
+     * Returns the active list of detectors regardless of whether
+     * they came from 'label' or 'queries'.
+     */
+    public List<LayerDetector> getActiveDetectors() {
+        if (labels != null && !labels.isEmpty()) {
+            return labels;
+        }
+        if (queries != null && !queries.isEmpty()) {
+            return queries;
+        }
+        return new java.util.ArrayList<>(); // Return empty list to prevent NullPointerExceptions
+    }
+
</code_context>

<issue_to_address>
**suggestion:** Align `getActiveDetectors` behavior and consider returning an unmodifiable or shared empty list.

Currently, when `labels`/`queries` are non-empty you return the backing list, but when both are empty you return a new mutable list unrelated to the object’s state. That semantic mismatch can be confusing. Consider returning `Collections.emptyList()` (or another unmodifiable view) to avoid extra allocations and make it clear the result should be treated as read-only and not used for in-place updates.

Suggested implementation:

```java
import com.google.gson.annotations.SerializedName;

import java.util.Collections;
import java.util.List;

```

```java
    /**
     * Returns the active list of detectors regardless of whether
     * they came from 'label' or 'queries'.
     * <p>
     * When no detectors are present, this returns a shared, unmodifiable empty list.
     */
    public List<LayerDetector> getActiveDetectors() {
        if (labels != null && !labels.isEmpty()) {
            return labels;
        }
        if (queries != null && !queries.isEmpty()) {
            return queries;
        }
        // Return a shared unmodifiable empty list to avoid allocations and clarify read-only semantics
        return Collections.emptyList();
    }


```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +61 to +68
public List<LayerDetector> getActiveDetectors() {
if (labels != null && !labels.isEmpty()) {
return labels;
}
if (queries != null && !queries.isEmpty()) {
return queries;
}
return new java.util.ArrayList<>(); // Return empty list to prevent NullPointerExceptions
Copy link

Choose a reason for hiding this comment

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

suggestion: Align getActiveDetectors behavior and consider returning an unmodifiable or shared empty list.

Currently, when labels/queries are non-empty you return the backing list, but when both are empty you return a new mutable list unrelated to the object’s state. That semantic mismatch can be confusing. Consider returning Collections.emptyList() (or another unmodifiable view) to avoid extra allocations and make it clear the result should be treated as read-only and not used for in-place updates.

Suggested implementation:

import com.google.gson.annotations.SerializedName;

import java.util.Collections;
import java.util.List;
    /**
     * Returns the active list of detectors regardless of whether
     * they came from 'label' or 'queries'.
     * <p>
     * When no detectors are present, this returns a shared, unmodifiable empty list.
     */
    public List<LayerDetector> getActiveDetectors() {
        if (labels != null && !labels.isEmpty()) {
            return labels;
        }
        if (queries != null && !queries.isEmpty()) {
            return queries;
        }
        // Return a shared unmodifiable empty list to avoid allocations and clarify read-only semantics
        return Collections.emptyList();
    }

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

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

3 participants