Skip to content

Commit

Permalink
Add generic platform filtering
Browse files Browse the repository at this point in the history
This allows us to have models select what kind of hardware they accept
based on platform, e.g. rejecting arm instances or special purpose
shapes for e.g. aurora.
  • Loading branch information
jolynch committed Jun 7, 2023
1 parent 9005887 commit 041dd5c
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 41 deletions.
47 changes: 37 additions & 10 deletions service_capacity_modeling/capacity_planner.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from typing import List
from typing import Optional
from typing import Sequence
from typing import Set
from typing import Tuple

import numpy as np
Expand All @@ -21,11 +22,14 @@
from service_capacity_modeling.interface import CapacityRequirement
from service_capacity_modeling.interface import certain_float
from service_capacity_modeling.interface import DataShape
from service_capacity_modeling.interface import Drive
from service_capacity_modeling.interface import Instance
from service_capacity_modeling.interface import Interval
from service_capacity_modeling.interface import interval
from service_capacity_modeling.interface import interval_percentile
from service_capacity_modeling.interface import Lifecycle
from service_capacity_modeling.interface import PlanExplanation
from service_capacity_modeling.interface import Platform
from service_capacity_modeling.interface import QueryPattern
from service_capacity_modeling.interface import RegionContext
from service_capacity_modeling.interface import Requirements
Expand Down Expand Up @@ -172,21 +176,42 @@ def model_desires_percentiles(
return results, d


def _allow_hardware(
name: str,
lifecycle: Lifecycle,
def _allow_instance(
instance: Instance,
allowed_names: Sequence[str],
allowed_lifecycles: Sequence[Lifecycle],
allowed_platforms: Set[Platform],
) -> bool:
# If the user has explicitly asked for particular families instead
# of all lifecycles filter based on that
if allowed_names:
if name not in allowed_names:
if instance.name not in allowed_names:
return False
# Otherwise consider lifecycle (default)
# Otherwise consider lifecycle (default) and platform
else:
if lifecycle not in allowed_lifecycles:
if instance.lifecycle not in allowed_lifecycles:
return False
if allowed_platforms.isdisjoint(instance.platforms):
return False

return True


def _allow_drive(
drive: Drive,
allowed_names: Sequence[str],
allowed_lifecycles: Sequence[Lifecycle],
) -> bool:
# If the user has explicitly asked for particular families instead
# of all lifecycles filter based on that
if allowed_names:
if drive.name not in allowed_names:
return False
# Otherwise consider lifecycle (default) and platform
else:
if drive.lifecycle not in allowed_lifecycles:
return False

return True


Expand Down Expand Up @@ -366,22 +391,24 @@ def _plan_certain(
desires.data_shape.reserved_instance_app_mem_gib
+ desires.data_shape.reserved_instance_system_mem_gib
)
model = self._models[model_name]
allowed_platforms: Set[Platform] = set(model.allowed_platforms())

plans = []
for instance in hardware.instances.values():
if not _allow_hardware(
instance.family, instance.lifecycle, instance_families, lifecycles
if not _allow_instance(
instance, instance_families, lifecycles, allowed_platforms
):
continue

if per_instance_mem > instance.ram_gib:
continue

for drive in hardware.drives.values():
if not _allow_hardware(drive.name, drive.lifecycle, drives, lifecycles):
if not _allow_drive(drive, drives, lifecycles):
continue

plan = self._models[model_name].capacity_plan(
plan = model.capacity_plan(
instance=instance,
drive=drive,
context=context,
Expand Down
16 changes: 8 additions & 8 deletions service_capacity_modeling/hardware/profiles/shapes/aws.json
Original file line number Diff line number Diff line change
Expand Up @@ -726,7 +726,7 @@
"ram_gib": 15.71,
"net_mbps": 500,
"drive": null,
"platforms": "Aurora MySQL,Aurora PostgreSQL"
"platforms": ["Aurora MySQL","Aurora PostgreSQL"]
},
"db.r5.xlarge": {
"name": "db.r5.xlarge",
Expand All @@ -735,7 +735,7 @@
"ram_gib": 31.65,
"net_mbps": 1000,
"drive": null,
"platforms": "Aurora MySQL,Aurora PostgreSQL"
"platforms": ["Aurora MySQL","Aurora PostgreSQL"]
},
"db.r5.2xlarge": {
"name": "db.r5.2xlarge",
Expand All @@ -744,7 +744,7 @@
"ram_gib": 63.62,
"net_mbps": 2000,
"drive": null,
"platforms": "Aurora MySQL,Aurora PostgreSQL"
"platforms": ["Aurora MySQL","Aurora PostgreSQL"]
},
"db.r5.4xlarge": {
"name": "db.r5.4xlarge",
Expand All @@ -753,7 +753,7 @@
"ram_gib": 128,
"net_mbps": 4000,
"drive": null,
"platforms": "Aurora MySQL,Aurora PostgreSQL"
"platforms": ["Aurora MySQL","Aurora PostgreSQL"]
},
"db.r5.8xlarge": {
"name": "db.r5.8xlarge",
Expand All @@ -762,7 +762,7 @@
"ram_gib": 256,
"net_mbps": 10000,
"drive": null,
"platforms": "Aurora MySQL,Aurora PostgreSQL"
"platforms": ["Aurora MySQL","Aurora PostgreSQL"]
},
"db.r5.12xlarge": {
"name": "db.r5.12xlarge",
Expand All @@ -771,7 +771,7 @@
"ram_gib": 384,
"net_mbps": 10000,
"drive": null,
"platforms": "Aurora MySQL,Aurora PostgreSQL"
"platforms": ["Aurora MySQL","Aurora PostgreSQL"]
},
"db.r5.16xlarge": {
"name": "db.r5.16xlarge",
Expand All @@ -780,7 +780,7 @@
"ram_gib": 512,
"net_mbps": 13600,
"drive": null,
"platforms": "Aurora MySQL,Aurora PostgreSQL"
"platforms": ["Aurora MySQL","Aurora PostgreSQL"]
},
"db.r5.24xlarge": {
"name": "db.r5.24xlarge",
Expand All @@ -789,7 +789,7 @@
"ram_gib": 768,
"net_mbps": 19000,
"drive": null,
"platforms": "Aurora MySQL,Aurora PostgreSQL"
"platforms": ["Aurora MySQL","Aurora PostgreSQL"]
}
},
"drives": {
Expand Down
29 changes: 22 additions & 7 deletions service_capacity_modeling/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,24 @@ def annual_cost(self):
return size * self.annual_cost_per_gib + r_cost + w_cost


class Platform(str, Enum):
"""Represents the platform of the hardware
For example a particular hardware type might offer x86_64, arm, or be a managed
instance type that only works with managed RDBMS like Aurora Postgres.
"""

# Most Intel and AMD instance types
amd64 = "amd64"
# Graviton and other ARM based instance types
arm64 = "arm64"
# Special purpose aurora type
aurora_mysql = "Aurora MySQL"
# Special purpose aurora type
aurora_postgres = "Aurora PostgreSQL"


class Instance(ExcludeUnsetModel):
"""Represents a cloud instance aka Hardware Shape
Expand All @@ -271,21 +289,18 @@ class Instance(ExcludeUnsetModel):
drive: Optional[Drive]
annual_cost: float = 0
lifecycle: Lifecycle = Lifecycle.stable
platforms: str = "EC2"
# Typically hardware has a single platform, but sometimes they can act in multiple
platforms: List[Platform] = [Platform.amd64]

family_separator: str = "."

@property
def family(self):
if self.name.startswith("db."):
return f"db.{self.name.split(self.family_separator)[1]}"
return self.name.split(self.family_separator)[0]
return self.name.rsplit(self.family_separator, 1)[0]

@property
def size(self):
if self.name.startswith("db."):
return self.name.split(self.family_separator)[2]
return self.name.split(self.family_separator)[1]
return self.name.rsplit(self.family_separator, 1)[1]


class Service(ExcludeUnsetModel):
Expand Down
10 changes: 10 additions & 0 deletions service_capacity_modeling/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from service_capacity_modeling.interface import FixedInterval
from service_capacity_modeling.interface import GlobalConsistency
from service_capacity_modeling.interface import Instance
from service_capacity_modeling.interface import Platform
from service_capacity_modeling.interface import QueryPattern
from service_capacity_modeling.interface import RegionContext

Expand Down Expand Up @@ -236,6 +237,15 @@ def compose_with(
(_, _) = user_desires, extra_model_arguments
return tuple()

@staticmethod
def allowed_platforms() -> Tuple[Platform, ...]:
"""Return which platforms this model accepts.
Most software can run on amd64 (Intel and AMD), but some models might
accept others
"""
return (Platform.amd64,)

@staticmethod
def default_desires(
user_desires: CapacityDesires, extra_model_arguments: Dict[str, Any]
Expand Down
Loading

0 comments on commit 041dd5c

Please sign in to comment.