summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/custom_strategy.py2
-rw-r--r--robusta_krr/core/abstract/formatters.py3
-rw-r--r--robusta_krr/core/abstract/strategies.py3
-rw-r--r--robusta_krr/strategies/simple.py33
-rw-r--r--robusta_krr/utils/display_name.py20
5 files changed, 47 insertions, 14 deletions
diff --git a/examples/custom_strategy.py b/examples/custom_strategy.py
index f5f2e6c..5917dc4 100644
--- a/examples/custom_strategy.py
+++ b/examples/custom_strategy.py
@@ -16,8 +16,6 @@ class CustomStrategySettings(StrategySettings):
class CustomStrategy(BaseStrategy[CustomStrategySettings]):
- __display_name__ = "my_strategy"
-
def run(self, history_data: HistoryData, object_data: K8sObjectData) -> RunResult:
return {
ResourceType.CPU: ResourceRecommendation(request=self.settings.param_1, limit=None),
diff --git a/robusta_krr/core/abstract/formatters.py b/robusta_krr/core/abstract/formatters.py
index 359edab..a4d1e7b 100644
--- a/robusta_krr/core/abstract/formatters.py
+++ b/robusta_krr/core/abstract/formatters.py
@@ -4,6 +4,8 @@ import abc
import os
from typing import TYPE_CHECKING, Any, TypeVar
+from robusta_krr.utils.display_name import add_display_name
+
if TYPE_CHECKING:
from robusta_krr.core.models.result import Result
@@ -14,6 +16,7 @@ DEFAULT_FORMATTERS_PATH = os.path.join(os.path.dirname(__file__), "formatters")
Self = TypeVar("Self", bound="BaseFormatter")
+@add_display_name(postfix="Formatter")
class BaseFormatter(abc.ABC):
"""Base class for result formatters."""
diff --git a/robusta_krr/core/abstract/strategies.py b/robusta_krr/core/abstract/strategies.py
index 476779a..061f94a 100644
--- a/robusta_krr/core/abstract/strategies.py
+++ b/robusta_krr/core/abstract/strategies.py
@@ -8,6 +8,7 @@ from typing import Generic, Optional, TypeVar, get_args
import pydantic as pd
from robusta_krr.core.models.result import K8sObjectData, ResourceType
+from robusta_krr.utils.display_name import add_display_name
class ResourceRecommendation(pd.BaseModel):
@@ -38,8 +39,10 @@ RunResult = dict[ResourceType, ResourceRecommendation]
Self = TypeVar("Self", bound="BaseStrategy")
+@add_display_name(postfix="Strategy")
class BaseStrategy(abc.ABC, Generic[_StrategySettings]):
__display_name__: str
+
settings: _StrategySettings
def __init__(self, settings: _StrategySettings):
diff --git a/robusta_krr/strategies/simple.py b/robusta_krr/strategies/simple.py
index bb20082..5dba2db 100644
--- a/robusta_krr/strategies/simple.py
+++ b/robusta_krr/strategies/simple.py
@@ -14,27 +14,36 @@ from robusta_krr.core.abstract.strategies import (
class SimpleStrategySettings(StrategySettings):
- cpu_percentile: Decimal = pd.Field(99, gt=0, description="The percentile to use for the request recommendation.")
- memory_percentile: Decimal = pd.Field(
- 105, gt=0, description="The percentile to use for the request recommendation."
+ cpu_percentile: Decimal = pd.Field(
+ 99, gt=0, le=100, description="The percentile to use for the CPU recommendation."
)
+ memory_buffer_percentage: Decimal = pd.Field(
+ 5, gt=0, description="The percentage of added buffer to the peak memory usage for memory recommendation."
+ )
+
+ def calculate_memory_proposal(self, data: dict[str, list[Decimal]]) -> Decimal:
+ data_ = [value for values in data.values() for value in values]
+ if len(data_) == 0:
+ return Decimal("NaN")
+
+ return max(data_) * Decimal(1 + self.memory_buffer_percentage / 100)
+
+ def calculate_cpu_proposal(self, data: dict[str, list[Decimal]]) -> Decimal:
+ data_ = [value for values in data.values() for value in values]
+ if len(data_) == 0:
+ return Decimal("NaN")
+
+ return data_[int((len(data_) - 1) * self.cpu_percentile / 100)]
class SimpleStrategy(BaseStrategy[SimpleStrategySettings]):
__display_name__ = "simple"
def run(self, history_data: HistoryData, object_data: K8sObjectData) -> RunResult:
- cpu_usage = self._calculate_percentile(history_data[ResourceType.CPU], self.settings.cpu_percentile)
- memory_usage = self._calculate_percentile(history_data[ResourceType.Memory], self.settings.memory_percentile)
+ cpu_usage = self.settings.calculate_cpu_proposal(history_data[ResourceType.CPU])
+ memory_usage = self.settings.calculate_memory_proposal(history_data[ResourceType.Memory])
return {
ResourceType.CPU: ResourceRecommendation(request=cpu_usage, limit=None),
ResourceType.Memory: ResourceRecommendation(request=memory_usage, limit=memory_usage),
}
-
- def _calculate_percentile(self, data: dict[str, list[Decimal]], percentile: Decimal) -> Decimal:
- data_ = [value for values in data.values() for value in values]
- if len(data_) == 0:
- return Decimal("NaN")
-
- return max(data_) * percentile / 100
diff --git a/robusta_krr/utils/display_name.py b/robusta_krr/utils/display_name.py
new file mode 100644
index 0000000..3cc89ce
--- /dev/null
+++ b/robusta_krr/utils/display_name.py
@@ -0,0 +1,20 @@
+from typing import Callable, TypeVar
+
+_T = TypeVar("_T")
+
+
+def add_display_name(*, postfix: str) -> Callable[[type[_T]], type[_T]]:
+ """Add a decorator factory to add __display_name__ property to the class."""
+
+ def decorator(cls: type[_T]) -> type[_T]:
+ class DisplayNameProperty:
+ def __get__(self, instance, owner):
+ if owner.__name__.lower().endswith(postfix.lower()):
+ return owner.__name__[: -len(postfix)]
+
+ return owner.__name__
+
+ cls.__display_name__ = DisplayNameProperty()
+ return cls
+
+ return decorator