Skip to content

Commit

Permalink
[Core 125] Compute: job statistics (#12279)
Browse files Browse the repository at this point in the history
GitOrigin-RevId: e84f674ff496349f4675bd42d65e1b30265e2459
  • Loading branch information
tkrause authored and Descartes Labs Build committed Oct 30, 2023
1 parent 2d481fd commit 5c975dd
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 6 deletions.
28 changes: 23 additions & 5 deletions descarteslabs/core/common/client/attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ def __set__(self, instance: "Document", value, force: bool = False):
self._raise_immutable("set", instance)

if self.type and value is not None:
value = self.deserialize(value, instance)
value = self.deserialize(value, instance, force=force)

# Only update the value if it has changed
if (self.name not in instance._attributes and value is None) or (
Expand Down Expand Up @@ -218,7 +218,9 @@ def _serialize_to_filter(self, value: Any):
"""Serializes a value to a filter expression value."""
return self.serialize(value)

def deserialize(self, value: Any, instance: "Document" = None) -> T:
def deserialize(
self, value: Any, instance: "Document" = None, force: bool = False
) -> T:
"""Deserializes a value to the type in the attribute.
Parameters
Expand All @@ -232,8 +234,20 @@ def deserialize(self, value: Any, instance: "Document" = None) -> T:
if value is None or isinstance(value, self.type):
return value

from .document import Document

try:
return self.type(value)
if issubclass(self.type, Document):
# Support nested documents
if isinstance(value, dict):
return self.type(**value, saved=force)
elif isinstance(value, Iterable):
return self.type(*value, saved=force)
else:
return self.type(value, saved=force)
else:
# Support single or native values
return self.type(value)
except (ValueError, TypeError) as e:
raise ValueError(f"Unable to assign {type(value)} to type {self.type}: {e}")

Expand Down Expand Up @@ -301,7 +315,9 @@ def __init__(
**extra,
)

def deserialize(self, value: str, instance: "Document" = None) -> T:
def deserialize(
self, value: str, instance: "Document" = None, force: bool = False
) -> T:
"""Deserialize a server datetime."""
if value is None:
return None
Expand Down Expand Up @@ -378,7 +394,9 @@ def __init__(
**extra,
)

def deserialize(self, value: Any, instance: "Document" = None) -> T:
def deserialize(
self, value: Any, instance: "Document" = None, force: bool = False
) -> T:
"""Deserialize a list of values."""
if value is None:
return None
Expand Down
10 changes: 9 additions & 1 deletion descarteslabs/core/common/client/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,15 @@ def to_dict(
if exclude_none and value is None:
continue

value = attribute.serialize(value)
if isinstance(value, Document):
value = value.to_dict(
only_modified=only_modified,
exclude_readonly=exclude_readonly,
exclude_none=exclude_none,
)
else:
value = attribute.serialize(value)

data[key] = value

return data
Expand Down
10 changes: 10 additions & 0 deletions descarteslabs/core/compute/job.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import warnings
from datetime import datetime
from typing import TYPE_CHECKING, Dict, List, Optional, Type

from strenum import StrEnum

from descarteslabs.exceptions import NotFoundError
Expand All @@ -31,6 +32,7 @@
Search,
)
from .compute_client import ComputeClient
from .job_statistics import JobStatistics
from .result import Serializable

if TYPE_CHECKING:
Expand Down Expand Up @@ -147,6 +149,14 @@ class Job(Document):
The status may occasionally need to be refreshed by calling :py:meth:`Job.refresh`
""",
)
statistics: Optional[Dict] = Attribute(
JobStatistics,
readonly=True,
doc="""The runtime utilization statistics for the Job.
The statistics include the cpu, memory, and network usage of the Job.
""",
)
tags: List[str] = ListAttribute(
str,
filterable=True,
Expand Down
107 changes: 107 additions & 0 deletions descarteslabs/core/compute/job_statistics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
from typing import List, Tuple

from descarteslabs.common.client import Attribute, Document, ListAttribute


class CpuStatistics(Document):
total: int = Attribute(
int,
readonly=True,
default=0,
doc="Total CPU usage as a percentage",
)
time: int = Attribute(
int,
readonly=True,
default=0,
doc="Number of CPU nanoseconds used",
)
timeseries: List[Tuple[int, int]] = ListAttribute(
tuple,
readonly=True,
doc="""Timeseries of CPU usage.
Each list element holds the cpu percentage and nanoseconds used for that 5 minute interval.
""",
)


class MemoryStatistics(Document):
total_bytes: int = Attribute(
int,
readonly=True,
default=0,
doc="Total memory usage in bytes",
)
total_percentage: int = Attribute(
int,
readonly=True,
default=0,
doc="Total memory usage as a percentage",
)
peak_bytes: int = Attribute(
int,
readonly=True,
default=0,
doc="Peak memory usage in bytes",
)
peak_percentage: int = Attribute(
int,
readonly=True,
default=0,
doc="Peak memory usage as a percentage",
)
timeseries: List[Tuple[int, int]] = ListAttribute(
tuple,
readonly=True,
doc="""Timeseries of the memory usage.
Each list element holds the memory percentage and memory used in bytes for
that 5 minute interval.
""",
)


class NetworkStatistics(Document):
rx_bytes: int = Attribute(
int,
readonly=True,
default=0,
doc="Total number of bytes received",
)
tx_bytes: int = Attribute(
int,
readonly=True,
default=0,
doc="Total number of bytes transmitted",
)
rx_dropped: int = Attribute(
int,
readonly=True,
default=0,
doc="Total number of packets dropped on receive",
)
tx_dropped: int = Attribute(
int,
readonly=True,
default=0,
doc="Total number of packets dropped on transmit",
)
rx_errors: int = Attribute(
int,
readonly=True,
default=0,
doc="Total number of receive errors",
)
tx_errors: int = Attribute(
int,
readonly=True,
default=0,
doc="Total number of transmit errors",
)


class JobStatistics(Document):
cpu: CpuStatistics = Attribute(CpuStatistics, readonly=True)
memory: MemoryStatistics = Attribute(MemoryStatistics, readonly=True)
network: NetworkStatistics = Attribute(NetworkStatistics, readonly=True)
1 change: 1 addition & 0 deletions descarteslabs/core/compute/tests/test_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ def test_get(self):
"last_execution_date": None,
"runtime": None,
"status": JobStatus.PENDING,
"statistics": None,
"tags": [],
"provisioning_time": None,
"pull_time": None,
Expand Down

0 comments on commit 5c975dd

Please sign in to comment.