fix: Add low-memory guard and cooldown for Discord message sending

This isn't quite the fix though just want to save my position till tomorrow and see what changes come up before and after
This commit is contained in:
Aaron 2025-11-14 21:48:19 -05:00
parent efea4a1384
commit 13e3a56fa6
2 changed files with 26 additions and 4 deletions

View File

@ -1,5 +1,7 @@
# Minimal module-level state (only what we need)
_CONFIG = {"discord_webhook_url": None, "discord_alert_webhook_url": None}
# Cooldown after low-memory failures (epoch seconds)
_NEXT_ALLOWED_SEND_TS = 0
def set_config(cfg: dict):
"""Initialize module with minimal values from loaded config (call from main)."""
@ -30,29 +32,41 @@ def send_discord_message(message, username="Auto Garden Bot", is_alert=False):
Send Discord message with aggressive GC and low-memory guard to avoid ENOMEM.
Returns True on success, False otherwise.
"""
global _NEXT_ALLOWED_SEND_TS
resp = None
url = _get_webhook_url(is_alert=is_alert)
if not url:
return False
# Respect cooldown if we recently saw ENOMEM
try:
import time # type: ignore
now = time.time()
if _NEXT_ALLOWED_SEND_TS and now < _NEXT_ALLOWED_SEND_TS:
return False
except:
pass
try:
# 1) Free heap before TLS
import gc # type: ignore
gc.collect()
try:
# If MicroPython provides mem_free, skip send if heap is very low
if hasattr(gc, "mem_free") and gc.mem_free() < 60000: # ~60KB threshold
# TLS can be spiky and fragmented; be conservative.
if hasattr(gc, "mem_free") and gc.mem_free() < 100000: # ~100KB threshold
return False
except:
pass
# 2) Import urequests locally (keeps RAM free when idle)
import urequests as requests # type: ignore
gc.collect() # collect again after import to reduce fragmentation
# 3) Keep payload tiny
url = str(url).strip().strip('\'"')
content = _escape_json_str(str(message)[:160])
user = _escape_json_str(str(username)[:40])
content = _escape_json_str(str(message)[:140]) # trim further
user = _escape_json_str(str(username)[:32])
body_bytes = ('{"content":"%s","username":"%s"}' % (content, user)).encode("utf-8")
# Minimal headers to reduce allocations
@ -65,6 +79,13 @@ def send_discord_message(message, username="Auto Garden Bot", is_alert=False):
return bool(status and 200 <= status < 300)
except Exception as e:
# On ENOMEM/MemoryError, back off for 30 seconds to avoid repeated failures
try:
if ("ENOMEM" in str(e)) or isinstance(e, MemoryError):
import time # type: ignore
_NEXT_ALLOWED_SEND_TS = time.time() + 30
except:
pass
print("Discord webhook exception:", e)
return False
@ -76,7 +97,7 @@ def send_discord_message(message, username="Auto Garden Bot", is_alert=False):
pass
# Free refs and force GC
try:
del resp, body_bytes
del resp, body_bytes, requests
except:
pass
try:

View File

@ -202,6 +202,7 @@ if wifi and wifi.isconnected():
# Send startup notification to Discord (with timeout, non-blocking)
try:
gc.collect() # free heap before HTTPS
success = discord_webhook.send_discord_message(f"Pico W online at http://{ifconfig[0]}")
if success:
print("Discord startup notification sent")