Drop status event processing at large
Since we figured out how to pass through ems dialog ids to the `openOrders` sub we don't really need to do much with status updates other then error handling. This drops `process_status()` and moves the error handling logic into a status handler sub-block; we now just info-log status updates for troubleshooting purposes.kraken_userref_hackzin
parent
1cbf45b4c4
commit
69e501764a
|
@ -140,11 +140,8 @@ async def handle_order_requests(
|
||||||
try:
|
try:
|
||||||
txid = reqids2txids[reqid]
|
txid = reqids2txids[reqid]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
assert 0
|
|
||||||
|
|
||||||
# XXX: not sure if this block ever gets hit now?
|
# XXX: not sure if this block ever gets hit now?
|
||||||
log.error('TOO FAST EDIT')
|
log.error('TOO FAST EDIT')
|
||||||
|
|
||||||
reqids2txids[reqid] = TooFastEdit(reqid)
|
reqids2txids[reqid] = TooFastEdit(reqid)
|
||||||
await ems_order_stream.send(
|
await ems_order_stream.send(
|
||||||
BrokerdError(
|
BrokerdError(
|
||||||
|
@ -971,68 +968,15 @@ async def handle_order_updates(
|
||||||
chain = apiflows[reqid]
|
chain = apiflows[reqid]
|
||||||
chain.maps.append(event)
|
chain.maps.append(event)
|
||||||
|
|
||||||
resps, errored = process_status(
|
if status == 'error':
|
||||||
event,
|
|
||||||
oid,
|
|
||||||
token,
|
|
||||||
chain,
|
|
||||||
reqids2txids,
|
|
||||||
)
|
|
||||||
|
|
||||||
if resps:
|
|
||||||
for resp in resps:
|
|
||||||
await ems_stream.send(resp)
|
|
||||||
|
|
||||||
txid = txid or lasttxid
|
|
||||||
if (
|
|
||||||
# errored likely on a rate limit or bad input
|
|
||||||
errored
|
|
||||||
and txid
|
|
||||||
|
|
||||||
# we throttle too-fast-requests on the ems side
|
|
||||||
or (txid and isinstance(txid, TooFastEdit))
|
|
||||||
):
|
|
||||||
# client was editting too quickly
|
|
||||||
# so we instead cancel this order
|
|
||||||
log.cancel(
|
|
||||||
f'Cancelling {reqid}@{txid} due to error:\n {event}')
|
|
||||||
await ws.send_msg({
|
|
||||||
'event': 'cancelOrder',
|
|
||||||
'token': token,
|
|
||||||
'reqid': reqid or 0,
|
|
||||||
'txid': [txid],
|
|
||||||
})
|
|
||||||
case _:
|
|
||||||
log.warning(f'Unhandled trades update msg: {msg}')
|
|
||||||
|
|
||||||
|
|
||||||
def process_status(
|
|
||||||
event: dict[str, str],
|
|
||||||
oid: str,
|
|
||||||
token: str,
|
|
||||||
chain: ChainMap,
|
|
||||||
reqids2txids: dict[int, str],
|
|
||||||
|
|
||||||
) -> tuple[list[MsgUnion], bool]:
|
|
||||||
'''
|
|
||||||
Process `'[add/edit/cancel]OrderStatus'` events by translating to
|
|
||||||
and returning the equivalent EMS-msg responses.
|
|
||||||
|
|
||||||
'''
|
|
||||||
match event:
|
|
||||||
case {
|
|
||||||
'event': etype,
|
|
||||||
'status': 'error',
|
|
||||||
'reqid': reqid,
|
|
||||||
'errorMessage': errmsg,
|
|
||||||
}:
|
|
||||||
# any of ``{'add', 'edit', 'cancel'}``
|
# any of ``{'add', 'edit', 'cancel'}``
|
||||||
action = etype.removesuffix('OrderStatus')
|
action = etype.removesuffix('OrderStatus')
|
||||||
|
errmsg = rest['errorMessage']
|
||||||
log.error(
|
log.error(
|
||||||
f'Failed to {action} order {reqid}:\n'
|
f'Failed to {action} order {reqid}:\n'
|
||||||
f'{errmsg}'
|
f'{errmsg}'
|
||||||
)
|
)
|
||||||
resp = BrokerdError(
|
await ems_stream.send(BrokerdError(
|
||||||
oid=oid,
|
oid=oid,
|
||||||
# XXX: use old reqid in case it changed?
|
# XXX: use old reqid in case it changed?
|
||||||
reqid=reqid,
|
reqid=reqid,
|
||||||
|
@ -1040,68 +984,28 @@ def process_status(
|
||||||
|
|
||||||
reason=f'Failed {action}:\n{errmsg}',
|
reason=f'Failed {action}:\n{errmsg}',
|
||||||
broker_details=event
|
broker_details=event
|
||||||
)
|
))
|
||||||
return [resp], True
|
|
||||||
|
|
||||||
# successful request cases
|
txid = txid or lasttxid
|
||||||
case {
|
if (
|
||||||
'event': 'addOrderStatus',
|
txid
|
||||||
'status': "ok",
|
|
||||||
'reqid': reqid, # oid from ems side
|
|
||||||
'txid': txid,
|
|
||||||
'descr': descr, # only on success?
|
|
||||||
}:
|
|
||||||
log.info(
|
|
||||||
f'Submitted order: {descr}\n'
|
|
||||||
f'ems oid: {oid}\n'
|
|
||||||
f'brokerd reqid: {reqid}\n'
|
|
||||||
f'txid: {txid}\n'
|
|
||||||
)
|
|
||||||
return [], False
|
|
||||||
|
|
||||||
case {
|
# we throttle too-fast-requests on the ems side
|
||||||
'event': 'editOrderStatus',
|
or (txid and isinstance(txid, TooFastEdit))
|
||||||
'status': "ok",
|
):
|
||||||
'reqid': reqid, # oid from ems side
|
# client was editting too quickly
|
||||||
'descr': descr,
|
# so we instead cancel this order
|
||||||
|
log.cancel(
|
||||||
|
f'Cancelling {reqid}@{txid} due to:\n {event}')
|
||||||
|
await ws.send_msg({
|
||||||
|
'event': 'cancelOrder',
|
||||||
|
'token': token,
|
||||||
|
'reqid': reqid or 0,
|
||||||
|
'txid': [txid],
|
||||||
|
})
|
||||||
|
|
||||||
# NOTE: for edit request this is a new value
|
case _:
|
||||||
'txid': txid,
|
log.warning(f'Unhandled trades update msg: {msg}')
|
||||||
'originaltxid': origtxid,
|
|
||||||
}:
|
|
||||||
log.info(
|
|
||||||
f'Editting order {oid}[requid={reqid}]:\n'
|
|
||||||
f'brokerd reqid: {reqid}\n'
|
|
||||||
f'txid: {origtxid} -> {txid}\n'
|
|
||||||
f'{descr}'
|
|
||||||
)
|
|
||||||
|
|
||||||
# XXX: update the expected txid since the ``openOrders`` sub
|
|
||||||
# doesn't relay through the ``userref`` value..
|
|
||||||
# (hopefully kraken will fix this so we don't need this
|
|
||||||
# line.)
|
|
||||||
# reqids2txids[reqid] = txid
|
|
||||||
# deliver another ack to update the ems-side `.reqid`.
|
|
||||||
return [], False
|
|
||||||
|
|
||||||
case {
|
|
||||||
"event": "cancelOrderStatus",
|
|
||||||
"status": "ok",
|
|
||||||
'reqid': reqid,
|
|
||||||
|
|
||||||
# XXX: sometimes this isn't provided!?
|
|
||||||
# 'txid': txids,
|
|
||||||
**rest,
|
|
||||||
}:
|
|
||||||
for txid in rest.get('txid', [chain['reqid']]):
|
|
||||||
log.info(
|
|
||||||
f'Cancelling order {oid}[requid={reqid}]:\n'
|
|
||||||
f'brokerd reqid: {reqid}\n'
|
|
||||||
)
|
|
||||||
# if txid == reqids2txids[reqid]:
|
|
||||||
# reqids2txids.pop(reqid)
|
|
||||||
|
|
||||||
return [], False
|
|
||||||
|
|
||||||
|
|
||||||
def norm_trade_records(
|
def norm_trade_records(
|
||||||
|
|
|
@ -39,7 +39,11 @@ from docker.errors import (
|
||||||
APIError,
|
APIError,
|
||||||
# ContainerError,
|
# ContainerError,
|
||||||
)
|
)
|
||||||
from requests.exceptions import ConnectionError, ReadTimeout
|
import requests
|
||||||
|
from requests.exceptions import (
|
||||||
|
ConnectionError,
|
||||||
|
ReadTimeout,
|
||||||
|
)
|
||||||
|
|
||||||
from ..log import get_logger, get_console_log
|
from ..log import get_logger, get_console_log
|
||||||
from .. import config
|
from .. import config
|
||||||
|
@ -188,13 +192,12 @@ class Container:
|
||||||
|
|
||||||
def hard_kill(self, start: float) -> None:
|
def hard_kill(self, start: float) -> None:
|
||||||
delay = time.time() - start
|
delay = time.time() - start
|
||||||
log.error(
|
|
||||||
f'Failed to kill container {self.cntr.id} after {delay}s\n'
|
|
||||||
'sending SIGKILL..'
|
|
||||||
)
|
|
||||||
# get out the big guns, bc apparently marketstore
|
# get out the big guns, bc apparently marketstore
|
||||||
# doesn't actually know how to terminate gracefully
|
# doesn't actually know how to terminate gracefully
|
||||||
# :eyeroll:...
|
# :eyeroll:...
|
||||||
|
log.error(
|
||||||
|
f'SIGKILL-ing: {self.cntr.id} after {delay}s\n'
|
||||||
|
)
|
||||||
self.try_signal('SIGKILL')
|
self.try_signal('SIGKILL')
|
||||||
self.cntr.wait(
|
self.cntr.wait(
|
||||||
timeout=3,
|
timeout=3,
|
||||||
|
@ -218,21 +221,26 @@ class Container:
|
||||||
self.try_signal('SIGINT')
|
self.try_signal('SIGINT')
|
||||||
|
|
||||||
start = time.time()
|
start = time.time()
|
||||||
for _ in range(30):
|
for _ in range(6):
|
||||||
|
|
||||||
with trio.move_on_after(0.5) as cs:
|
with trio.move_on_after(0.5) as cs:
|
||||||
cs.shield = True
|
|
||||||
log.cancel('polling for CNTR logs...')
|
log.cancel('polling for CNTR logs...')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await self.process_logs_until(stop_msg)
|
await self.process_logs_until(stop_msg)
|
||||||
except ApplicationLogError:
|
except ApplicationLogError:
|
||||||
hard_kill = True
|
hard_kill = True
|
||||||
|
else:
|
||||||
# if we aren't cancelled on above checkpoint then we
|
# if we aren't cancelled on above checkpoint then we
|
||||||
# assume we read the expected stop msg and terminated.
|
# assume we read the expected stop msg and
|
||||||
|
# terminated.
|
||||||
break
|
break
|
||||||
|
|
||||||
|
if cs.cancelled_caught:
|
||||||
|
# on timeout just try a hard kill after
|
||||||
|
# a quick container sync-wait.
|
||||||
|
hard_kill = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
log.info(f'Polling for container shutdown:\n{cid}')
|
log.info(f'Polling for container shutdown:\n{cid}')
|
||||||
|
|
||||||
|
@ -254,9 +262,16 @@ class Container:
|
||||||
except (
|
except (
|
||||||
docker.errors.APIError,
|
docker.errors.APIError,
|
||||||
ConnectionError,
|
ConnectionError,
|
||||||
|
requests.exceptions.ConnectionError,
|
||||||
|
trio.Cancelled,
|
||||||
):
|
):
|
||||||
log.exception('Docker connection failure')
|
log.exception('Docker connection failure')
|
||||||
self.hard_kill(start)
|
self.hard_kill(start)
|
||||||
|
raise
|
||||||
|
|
||||||
|
except trio.Cancelled:
|
||||||
|
log.exception('trio cancelled...')
|
||||||
|
self.hard_kill(start)
|
||||||
else:
|
else:
|
||||||
hard_kill = True
|
hard_kill = True
|
||||||
|
|
||||||
|
@ -305,15 +320,12 @@ async def open_ahabd(
|
||||||
))
|
))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
# TODO: we might eventually want a proxy-style msg-prot here
|
# TODO: we might eventually want a proxy-style msg-prot here
|
||||||
# to allow remote control of containers without needing
|
# to allow remote control of containers without needing
|
||||||
# callers to have root perms?
|
# callers to have root perms?
|
||||||
await trio.sleep_forever()
|
await trio.sleep_forever()
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
# needed?
|
|
||||||
with trio.CancelScope(shield=True):
|
|
||||||
await cntr.cancel(stop_msg)
|
await cntr.cancel(stop_msg)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue