@@ -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"
0 commit comments