Skip to content

Commit 0e0e72c

Browse files
committed
feat(hip-3-pusher): improved metrics
1 parent 35c90ab commit 0e0e72c

File tree

4 files changed

+54
-22
lines changed

4 files changed

+54
-22
lines changed

apps/hip-3-pusher/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "hip-3-pusher"
3-
version = "0.2.1"
3+
version = "0.2.2"
44
description = "Hyperliquid HIP-3 market oracle pusher"
55
readme = "README.md"
66
requires-python = "==3.13.*"

apps/hip-3-pusher/src/pusher/metrics.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,24 @@ def __init__(self, config: Config):
2121
self._init_metrics()
2222

2323
def _init_metrics(self):
24-
self.no_oracle_price_counter = self.meter.create_counter(
25-
name="hip_3_pusher_no_oracle_price_count",
26-
description="Number of failed push attempts with no valid oracle price",
24+
# labels: dex, symbol
25+
self.last_pushed_time = self.meter.create_gauge(
26+
name="hip_3_relayer_last_published_time",
27+
description="Time of last successful oracle update",
2728
)
28-
self.successful_push_counter = self.meter.create_counter(
29-
name="hip_3_pusher_successful_push_count",
30-
description="Number of successful push attempts",
29+
# labels: dex, status, error_reason
30+
self.update_attempts_total = self.meter.create_counter(
31+
name="hip_3_relayer_update_attempts_total",
32+
description="Number of update attempts",
3133
)
32-
self.failed_push_counter = self.meter.create_counter(
33-
name="hip_3_pusher_failed_push_count",
34-
description="Number of failed push attempts",
34+
# labels: dex
35+
self.no_oracle_price_counter = self.meter.create_counter(
36+
name="hip_3_relayer_no_oracle_price_count",
37+
description="Number of failed push attempts with no valid oracle price",
3538
)
39+
# labels: dex
3640
self.push_interval_histogram = self.meter.create_histogram(
37-
name="hip_3_pusher_push_interval",
41+
name="hip_3_relayer_push_interval",
3842
description="Interval between push requests (seconds)",
3943
unit="s",
4044
)

apps/hip-3-pusher/src/pusher/publisher.py

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,6 @@ def publish(self):
7979
# markPxs is a list of dicts of length 0-2, and so can be empty
8080
mark_pxs = [mark_pxs] if mark_pxs else []
8181

82-
# TODO: "Each update can change oraclePx and markPx by at most 1%."
83-
# TODO: "The markPx cannot be updated such that open interest would be 10x the open interest cap."
84-
8582
if self.enable_publish:
8683
try:
8784
if self.enable_kms:
@@ -103,12 +100,13 @@ def publish(self):
103100
all_mark_pxs=mark_pxs,
104101
external_perp_pxs=external_perp_pxs,
105102
)
106-
self._handle_response(push_response)
103+
self._handle_response(push_response, list(oracle_pxs.keys()))
107104
except PushError:
108-
logger.error("All push attempts failed")
109-
self.metrics.failed_push_counter.add(1, self.metrics_labels)
105+
# since rate limiting is expected, don't necessarily log
106+
pass
110107
except Exception as e:
111108
logger.exception("Unexpected exception in push request: {}", repr(e))
109+
self._update_attempts_total("error", "internal_error")
112110
else:
113111
logger.debug("push disabled")
114112

@@ -128,14 +126,22 @@ def _send_update(self, oracle_pxs, all_mark_pxs, external_perp_pxs):
128126

129127
raise PushError("all push endpoints failed")
130128

131-
def _handle_response(self, response):
129+
def _handle_response(self, response, symbols: list[str]):
132130
logger.debug("oracle update response: {}", response)
133131
status = response.get("status")
134132
if status == "ok":
135-
self.metrics.successful_push_counter.add(1, self.metrics_labels)
133+
self._update_attempts_total("success")
134+
time_secs = int(time.time())
135+
136+
# update last publish time for each symbol in dex
137+
for symbol in symbols:
138+
labels = {**self.metrics_labels, "symbol": symbol}
139+
self.metrics.last_pushed_time.set(time_secs, labels)
136140
elif status == "err":
137-
self.metrics.failed_push_counter.add(1, self.metrics_labels)
138-
logger.error("oracle update error response: {}", response)
141+
error_reason = self._get_error_reason(response)
142+
self._update_attempts_total("error", error_reason)
143+
if error_reason != "rate_limit":
144+
logger.error("Error response: {}", response)
139145

140146
def _record_push_interval_metric(self):
141147
now = time.time()
@@ -183,3 +189,25 @@ def _send_single_multisig_update(self, exchange, oracle_pxs, all_mark_pxs, exter
183189
outer_signer=self.oracle_account.address,
184190
)]
185191
return exchange.multi_sig(self.multisig_address, action, signatures, timestamp)
192+
193+
def _update_attempts_total(self, status: str, error_reason: str | None=None):
194+
labels = {**self.metrics_labels, "status": status}
195+
if error_reason:
196+
# don't flag rate limiting as this is expected with redundancy
197+
if error_reason == "rate_limit":
198+
return
199+
labels["error_reason"] = error_reason
200+
201+
self.metrics.update_attempts_total.add(1, labels)
202+
203+
def _get_error_reason(self, response):
204+
response = response.get("response")
205+
if not response:
206+
return None
207+
elif "Oracle price update too often" in response:
208+
return "rate_limit"
209+
elif "Too many cumulative requests" in response:
210+
return "user_limit"
211+
else:
212+
logger.warning("Unrecognized error response: {}", response)
213+
return "unknown"

apps/hip-3-pusher/uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)