-
Notifications
You must be signed in to change notification settings - Fork 65
feat: add data model for client side metrics #1187
Changes from 1 commit
d2175f1
5e107fc
c4a97e1
e71b1d5
b81a9be
a1dffb5
e3ec02b
4e13783
7d90a04
26cd601
375332f
428d75a
4b39bc5
3f090c2
883ceab
04c762a
29dff4d
e4f8238
fcb062e
ac8dbe4
d155f8a
9fece96
aec2577
ec4e847
8f99e4e
f8e6603
f5e057e
8c397bb
2c34198
9bd1e07
96d1355
de5d07b
d73f379
a2070f9
4a4f80a
5ea9f0e
708a35a
67c08fd
5ae7acc
cb32296
f07e765
a34c01e
84f61ee
d4ae637
1fbcadd
edacd04
c628d21
6d585ec
01e6b36
05fe577
4871abd
b6eac6c
07b3295
e3ac131
557b54a
3306ad7
65f15de
16c5b6a
00cc52f
019a8c2
4ccfdab
bd9ab70
50b3e48
2b35127
9cbda99
73f4b3c
d5e012d
486068b
bb00b8b
c89f6d4
bebeb70
5ba2bbe
4098fd9
144d75e
e8785ac
c1cc24d
c433f3c
85d4cf0
ed9d3cf
bc6036e
18ec330
f1be54a
9a33d86
e94e143
596e54e
16c8ed8
4887830
5c80c79
8de4875
6a4d742
a474560
5390813
3e0d134
6b48242
dcf3d0a
c94e4ff
3a87a35
b5c361b
1b0b857
ac315d0
87e78d1
54b7208
819e1ae
22eb2e1
0ec8d14
fa25c2b
f9ac548
284c8a6
16f7d57
d167487
c408e14
a70824b
4ceab60
78c640b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -64,7 +64,23 @@ class OperationType(Enum): | |
|
|
||
|
|
||
| class OperationState(Enum): | ||
| """Enum for the state of the active operation.""" | ||
| """Enum for the state of the active operation. | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in end_with_status, it seems like CREATED can transition to COMPLETED, which I think it's valid if the rpc timed out before attempt is started (but probably rare), I think we should update the comment here?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point, I added it to the diagram |
||
| ┌───────────┐ | ||
| │ CREATED │ | ||
| └─────┬─────┘ | ||
| │ | ||
| ▼ | ||
| ┌▶ ACTIVE_ATTEMPT ───┐ | ||
| │ │ │ | ||
| │ ▼ │ | ||
| └─ BETWEEN_ATTEMPTS │ | ||
| │ │ | ||
| ▼ ▼ | ||
| ┌───────────┐ │ | ||
| │ COMPLETED │ ◀─────┘ | ||
| └───────────┘ | ||
| """ | ||
|
|
||
| CREATED = 0 | ||
| ACTIVE_ATTEMPT = 1 | ||
|
|
@@ -138,6 +154,7 @@ class ActiveOperationMetric: | |
|
|
||
| op_type: OperationType | ||
| uuid: str = field(default_factory=lambda: str(uuid.uuid4())) | ||
| state: OperationState = OperationState.CREATED | ||
| # create a default backoff generator, initialized with standard default backoff values | ||
| backoff_generator: TrackedBackoffGenerator = field( | ||
| default_factory=lambda: TrackedBackoffGenerator( | ||
|
|
@@ -151,7 +168,6 @@ class ActiveOperationMetric: | |
| zone: str | None = None | ||
| completed_attempts: list[CompletedAttemptMetric] = field(default_factory=list) | ||
| is_streaming: bool = False # only True for read_rows operations | ||
| was_completed: bool = False | ||
| handlers: list[MetricsHandler] = field(default_factory=list) | ||
| # the time it takes to recieve the first response from the server, in nanoseconds | ||
| # attached by interceptor | ||
|
|
@@ -186,18 +202,6 @@ def from_context(cls) -> ActiveOperationMetric | None: | |
| return None | ||
| return op | ||
|
|
||
| @property | ||
| def state(self) -> OperationState: | ||
| if self.was_completed: | ||
| return OperationState.COMPLETED | ||
| elif self.active_attempt is None: | ||
| if self.completed_attempts: | ||
| return OperationState.BETWEEN_ATTEMPTS | ||
| else: | ||
| return OperationState.CREATED | ||
| else: | ||
| return OperationState.ACTIVE_ATTEMPT | ||
|
|
||
| def __post_init__(self): | ||
| """ | ||
| Save new instances to contextvars on init | ||
|
|
@@ -209,7 +213,8 @@ def start(self) -> None: | |
| Optionally called to mark the start of the operation. If not called, | ||
| the operation will be started at initialization. | ||
|
|
||
| Assumes operation is in CREATED state. | ||
| StartState: CREATED | ||
| EndState: CREATED | ||
| """ | ||
| if self.state != OperationState.CREATED: | ||
| return self._handle_error(INVALID_STATE_ERROR.format("start", self.state)) | ||
|
|
@@ -221,7 +226,8 @@ def start_attempt(self) -> ActiveAttemptMetric | None: | |
| """ | ||
| Called to initiate a new attempt for the operation. | ||
|
|
||
| Assumes operation is in either CREATED or BETWEEN_ATTEMPTS states | ||
| StartState: CREATED | BETWEEN_ATTEMPTS | ||
| EndState: ACTIVE_ATTEMPT | ||
| """ | ||
| if ( | ||
| self.state != OperationState.BETWEEN_ATTEMPTS | ||
|
|
@@ -244,6 +250,7 @@ def start_attempt(self) -> ActiveAttemptMetric | None: | |
| backoff_ns = 0 | ||
|
|
||
| self.active_attempt = ActiveAttemptMetric(backoff_before_attempt_ns=backoff_ns) | ||
| self.state = OperationState.ACTIVE_ATTEMPT | ||
| return self.active_attempt | ||
|
|
||
| def add_response_metadata(self, metadata: dict[str, bytes | str]) -> None: | ||
|
|
@@ -252,7 +259,8 @@ def add_response_metadata(self, metadata: dict[str, bytes | str]) -> None: | |
|
|
||
| If not called, default values for the metadata will be used. | ||
|
|
||
| Assumes operation is in ACTIVE_ATTEMPT state. | ||
| StartState: ACTIVE_ATTEMPT | ||
| EndState: ACTIVE_ATTEMPT | ||
|
|
||
| Args: | ||
| - metadata: the metadata as extracted from the grpc call | ||
|
|
@@ -312,7 +320,8 @@ def end_attempt_with_status(self, status: StatusCode | BaseException) -> None: | |
| be attempted, `end_with_status` or `end_with_success` should be used | ||
| to finalize the operation along with the attempt. | ||
|
|
||
| Assumes operation is in ACTIVE_ATTEMPT state. | ||
| StartState: ACTIVE_ATTEMPT | ||
| EndState: BETWEEN_ATTEMPTS | ||
|
|
||
| Args: | ||
| - status: The status of the attempt. | ||
|
|
@@ -332,6 +341,7 @@ def end_attempt_with_status(self, status: StatusCode | BaseException) -> None: | |
| ) | ||
| self.completed_attempts.append(complete_attempt) | ||
| self.active_attempt = None | ||
| self.state = OperationState.BETWEEN_ATTEMPTS | ||
| for handler in self.handlers: | ||
| handler.on_attempt_complete(complete_attempt, self) | ||
|
|
||
|
|
@@ -340,7 +350,8 @@ def end_with_status(self, status: StatusCode | BaseException) -> None: | |
| Called to mark the end of the operation. If there is an active attempt, | ||
| end_attempt_with_status will be called with the same status. | ||
|
|
||
| Assumes operation is not already in COMPLETED state. | ||
| StartState: CREATED | ACTIVE_ATTEMPT | BETWEEN_ATTEMPTS | ||
| EndState: COMPLETED | ||
|
|
||
| Causes on_operation_completed to be called for each registered handler. | ||
|
|
||
|
|
@@ -356,7 +367,7 @@ def end_with_status(self, status: StatusCode | BaseException) -> None: | |
| ) | ||
| if self.state == OperationState.ACTIVE_ATTEMPT: | ||
| self.end_attempt_with_status(final_status) | ||
| self.was_completed = True | ||
| self.state = OperationState.COMPLETED | ||
| finalized = CompletedOperationMetric( | ||
| op_type=self.op_type, | ||
| uuid=self.uuid, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this a uuid per operation? what is it used for?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch. In an older version of this code, I used a map to track active operations, with the uuid as the look-up key. But I changed it to use |
||
|
|
@@ -376,7 +387,8 @@ def end_with_success(self): | |
| """ | ||
| Called to mark the end of the operation with a successful status. | ||
|
|
||
| Assumes operation is not already in COMPLETED state. | ||
| StartState: CREATED | ACTIVE_ATTEMPT | BETWEEN_ATTEMPTS | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the only valid state transition for the success case is from BETWEEN_ATTEMPTS -> COMPLETE? I think the state can only transition from CREATED -> COMPLETED if the rpc timed out before the attempt even started? and can only transition from ACTIVE_ATTEMPT -> COMPLETED if the server errored out or rpc timed out? which won't be the case here? So I think we should add a check to make sure the previous state was BETWEEN_ATTEMPTS?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok I see i was confused by end_attempt_with_status transitions to BETWEEN_ATTEMPT state. But when status is ok this is transisioned from ACTIVE STATE and immediately to COMPLETE. |
||
| EndState: COMPLETED | ||
|
|
||
| Causes on_operation_completed to be called for each registered handler. | ||
| """ | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.