Skip to content

Commit 373d133

Browse files
committed
ensure can reach stackcoin
1 parent 6ff91ce commit 373d133

3 files changed

Lines changed: 59 additions & 6 deletions

File tree

lucky_pot.py

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import asyncio
2+
import random
3+
24
import hikari
35
from loguru import logger
46

@@ -17,6 +19,14 @@
1719
from luckypot.discord.commands import register_commands
1820
from luckypot.discord.scheduler import run_daily_draw_loop
1921

22+
# Startup retry parameters for reaching StackCoin. Tuned so that luckypot waits
23+
# roughly long enough for stackcoin to come up after a simultaneous restart
24+
# (~16s in practice), but gives up well inside Docker's automatic-restart window
25+
# so the whole container gets recycled by `restart: unless-stopped`.
26+
STACKCOIN_CONNECT_MAX_ATTEMPTS = 12
27+
STACKCOIN_CONNECT_BASE_DELAY = 1.0
28+
STACKCOIN_CONNECT_MAX_DELAY = 30.0
29+
2030

2131
logger.add("lucky_pot.log", rotation="1 day", retention="7 days", level="INFO")
2232

@@ -43,6 +53,44 @@ def _task_done_callback(task: asyncio.Task) -> None:
4353
logger.error(f"Background task {task.get_name()} failed: {exc!r}")
4454

4555

56+
async def _fetch_stackcoin_discord_id_or_die() -> str:
57+
"""Retry ``fetch_stackcoin_discord_id`` against a not-yet-ready StackCoin.
58+
59+
stackcoin-python normalises transport faults into ``StackCoinError`` (since
60+
v0.1.6), so we only catch that. If we exhaust all attempts we raise — the
61+
caller in :func:`on_started` shuts the whole bot down by closing the hikari
62+
bot, which exits the process non-zero and lets Docker's
63+
``restart: unless-stopped`` policy recycle the container. By the next boot
64+
StackCoin has been up for ages.
65+
"""
66+
last_exc: BaseException | None = None
67+
for attempt in range(1, STACKCOIN_CONNECT_MAX_ATTEMPTS + 1):
68+
try:
69+
discord_id = await stk.fetch_stackcoin_discord_id()
70+
if discord_id is not None:
71+
return discord_id
72+
# Defence in depth: SDK returned None without raising — treat as a
73+
# soft miss and retry, since the only None path in stk.py is the
74+
# StackCoinError swallow at stk.py:40-42.
75+
last_exc = RuntimeError("fetch_stackcoin_discord_id returned None")
76+
except stackcoin.StackCoinError as e:
77+
last_exc = e
78+
delay = min(
79+
STACKCOIN_CONNECT_MAX_DELAY,
80+
STACKCOIN_CONNECT_BASE_DELAY * (2 ** (attempt - 1)),
81+
) + random.random()
82+
logger.warning(
83+
f"StackCoin not ready (attempt {attempt}/"
84+
f"{STACKCOIN_CONNECT_MAX_ATTEMPTS}), retrying in {delay:.1f}s: "
85+
f"{last_exc!r}"
86+
)
87+
await asyncio.sleep(delay)
88+
raise RuntimeError(
89+
f"Could not reach StackCoin after {STACKCOIN_CONNECT_MAX_ATTEMPTS} "
90+
f"attempts; refusing to start half-functional. Last error: {last_exc!r}"
91+
)
92+
93+
4694
@bot.listen()
4795
async def on_started(_event: hikari.StartedEvent) -> None:
4896
global _gateway
@@ -64,7 +112,12 @@ def persist_event_id(event_id: int) -> None:
64112
finally:
65113
c.close()
66114

67-
await stk.fetch_stackcoin_discord_id()
115+
try:
116+
await _fetch_stackcoin_discord_id_or_die()
117+
except RuntimeError as e:
118+
logger.error(str(e))
119+
await bot.close()
120+
return
68121

69122
_gateway = stackcoin.Gateway(
70123
ws_url=settings.stackcoin_ws_url,

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ dependencies = [
99
"hikari-lightbulb>=3.1.1",
1010
"loguru>=0.7.3",
1111
"pydantic-settings>=2.0",
12-
"stackcoin>=0.1.5",
12+
"stackcoin>=0.1.6",
1313
]
1414

1515
[build-system]

uv.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)