Skip to content

Add background loop health check#1472

Merged
EntchenEric merged 3 commits into
developmentfrom
fix/issue-1416-background-loop-health-check
May 26, 2026
Merged

Add background loop health check#1472
EntchenEric merged 3 commits into
developmentfrom
fix/issue-1416-background-loop-health-check

Conversation

@EntchenEric
Copy link
Copy Markdown
Member

@EntchenEric EntchenEric commented May 26, 2026

This PR implements a health check for all background loops in the bot, ensuring they are running as expected.

Summary by CodeRabbit

  • New Features
    • Added a health check for background loop tasks that verifies whether configured loops are running, reports any stopped loops in the status details, and marks overall checks as degraded (not critical) when one or more loops are stopped; reports healthy when all loops are running.

Review Change Stack

@EntchenEric
Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 26, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 26, 2026

📝 Walkthrough

Walkthrough

Adds BackgroundLoopHealthCheck: a non-critical health check that fetches LoopCog, returns critical if missing, otherwise calls is_running() on configured background loop tasks and returns degraded (listing stopped loops) or healthy.

Changes

Background Loop Health Check

Layer / File(s) Summary
BackgroundLoopHealthCheck implementation
extensions/health_check.py
New BackgroundLoopHealthCheck class with name and critical properties and async run() that checks LoopCog presence, iterates configured background loop callables with is_running(), and returns critical, degraded (with details["stopped_loops"]), or healthy results.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Possibly related PRs

  • TanjunBot/new_tanjun#1461: Introduces the same BackgroundLoopHealthCheck and similar run() logic checking LoopCog background loops.
  • TanjunBot/new_tanjun#1458: Adds BackgroundLoopHealthCheck inspecting LoopCog and reporting CRITICAL/DEGRADED/HEALTHY based on loop statuses.

Poem

🐰 A little loop hops through the night,
Health checks watch with gentle light,
If cogs are missing, alarms will peep,
If loops are quiet, names they keep.
Hooray for checks that guard our flight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add background loop health check' clearly and directly summarizes the main change: introducing a health check feature for background loops.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-1416-background-loop-health-check

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@extensions/health_check.py`:
- Around line 1-79: Replace the Markdown blob with a valid Python module that
defines BackgroundLoopHealthCheck: add imports (from __future__ annotations,
discord.ext.commands, and HealthCheck/HealthCheckResult/HealthStatus), implement
class BackgroundLoopHealthCheck(bot: commands.Bot) with properties name and
critical, and an async run() method that calls self.bot.get_cog("LoopCog")
(check cog is None), builds the loop_tasks list referencing the cog methods
(sendSendReadyGiveaways, endGiveawaysLoop, checkVoiceUsers, addVoiceUserLoop,
refillAiTokenLoop, pingServerLoop, backupDatabaseLoop,
removeExpiredClaimedBoosterRoles, removeExpiredClaimedBoosterChannels,
sendScheduledMessages, pollTwitchStreams), computes failed_loops via
task.is_running(), and returns HealthCheckResult with HealthStatus.CRITICAL if
cog missing, HealthStatus.DEGRADED with details when failed_loops nonempty, or
HealthStatus.HEALTHY otherwise.
- Around line 39-51: The health check's loop_tasks list is missing the
clearNotifiedUsersLoop and sendPokemonWerbung background loops, causing false
positives; update the loop_tasks array (where it's defined) to include entries
like ("Clear Notified Users", cog.clearNotifiedUsersLoop) and ("Pokemon
Werbung", cog.sendPokemonWerbung) so those loops are checked alongside
cog.sendSendReadyGiveaways, cog.endGiveawaysLoop, etc.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: bdde44a3-4deb-45e6-97f5-6eeaf0bdf09a

📥 Commits

Reviewing files that changed from the base of the PR and between 1c96800 and 2caa935.

📒 Files selected for processing (1)
  • extensions/health_check.py

Comment on lines +1 to +79
# Background Loop Health Check

Requires the HealthCheck framework from the main health check issue.

The bot relies on multiple background task loops (`extensions/loops.py`):
- Giveaway sending (10s)
- Giveaway ending (10s)
- Voice XP processing (5s)
- AI token refill (60s)
- Server pinging (5s)
- Database backup (1h)
- Booster role/channel cleanup (10s)
- Scheduled messages (10s)
- Twitch polling (10s)

If one of these loops crashes silently (they all use `except Exception: pass`), the feature stops working without any notification.

## Implementation

```python
class BackgroundLoopHealthCheck(HealthCheck):
def __init__(self, bot):
self.bot = bot

@property
def name(self) -> str: return "Background Loops"
@property
def critical(self) -> bool: return False # Individual loops failing is degraded, not critical

async def run(self) -> HealthCheckResult:
cog = self.bot.get_cog("LoopCog")
if not cog:
return HealthCheckResult(
self.name, HealthStatus.CRITICAL,
"LoopCog not found. No background tasks registered."
)

# Check each loop task
loop_tasks = [
("Giveaway Sender", cog.sendSendReadyGiveaways),
("Giveaway Ender", cog.endGiveawaysLoop),
("Voice Checker", cog.checkVoiceUsers),
("Voice XP", cog.addVoiceUserLoop),
("AI Token Refill", cog.refillAiTokenLoop),
("Ping Server", cog.pingServerLoop),
("Database Backup", cog.backupDatabaseLoop),
("Booster Roles", cog.removeExpiredClaimedBoosterRoles),
("Booster Channels", cog.removeExpiredClaimedBoosterChannels),
("Scheduled Messages", cog.sendScheduledMessages),
("Twitch Polling", cog.pollTwitchStreams)
]

failed_loops = []
for name, task in loop_tasks:
if not task.is_running():
failed_loops.append(name)
# Optionally check how long since last iteration
# if task.delta and task.delta.total_seconds() > expected_interval * 3:
# failed_loops.append(f"{name} (stuck)")

if failed_loops:
return HealthCheckResult(
self.name, HealthStatus.DEGRADED,
f"Stopped loops: {', '.join(failed_loops)}",
details={"stopped_loops": failed_loops}
)

return HealthCheckResult(
self.name, HealthStatus.HEALTHY,
f"All {len(loop_tasks)} background loops are running."
)
```

## Checks

- **Critical**: No (startup check would always pass since loops start after ready)
- **Startup check**: No
- **Periodic check**: Yes (every 2 minutes - detects failures quickly)
- **Failure action**: Notify alert channel with which loop(s) stopped No newline at end of file
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Replace Markdown prose with executable Python module code.

extensions/health_check.py currently contains raw Markdown text, so Python cannot parse it (e.g., Line 3, Line 6). This blocks import, lint, and type-check entirely.

Proposed fix (convert to valid module code)
-# Background Loop Health Check
-
-Requires the HealthCheck framework from the main health check issue.
-
-The bot relies on multiple background task loops (`extensions/loops.py`):
-- Giveaway sending (10s)
-- Giveaway ending (10s)
-- Voice XP processing (5s)
-- AI token refill (60s)
-- Server pinging (5s)
-- Database backup (1h)
-- Booster role/channel cleanup (10s)
-- Scheduled messages (10s)
-- Twitch polling (10s)
-
-If one of these loops crashes silently (they all use `except Exception: pass`), the feature stops working without any notification.
-
-## Implementation
-
-```python
-class BackgroundLoopHealthCheck(HealthCheck):
-    def __init__(self, bot):
-        self.bot = bot
-    
-    `@property`
-    def name(self) -> str: return "Background Loops"
-    `@property`
-    def critical(self) -> bool: return False  # Individual loops failing is degraded, not critical
-    
-    async def run(self) -> HealthCheckResult:
-        cog = self.bot.get_cog("LoopCog")
-        if not cog:
-            return HealthCheckResult(
-                self.name, HealthStatus.CRITICAL,
-                "LoopCog not found. No background tasks registered."
-            )
-        
-        # Check each loop task
-        loop_tasks = [
-            ("Giveaway Sender", cog.sendSendReadyGiveaways),
-            ("Giveaway Ender", cog.endGiveawaysLoop),
-            ("Voice Checker", cog.checkVoiceUsers),
-            ("Voice XP", cog.addVoiceUserLoop),
-            ("AI Token Refill", cog.refillAiTokenLoop),
-            ("Ping Server", cog.pingServerLoop),
-            ("Database Backup", cog.backupDatabaseLoop),
-            ("Booster Roles", cog.removeExpiredClaimedBoosterRoles),
-            ("Booster Channels", cog.removeExpiredClaimedBoosterChannels),
-            ("Scheduled Messages", cog.sendScheduledMessages),
-            ("Twitch Polling", cog.pollTwitchStreams)
-        ]
-        
-        failed_loops = []
-        for name, task in loop_tasks:
-            if not task.is_running():
-                failed_loops.append(name)
-            # Optionally check how long since last iteration
-            # if task.delta and task.delta.total_seconds() > expected_interval * 3:
-            #     failed_loops.append(f"{name} (stuck)")
-        
-        if failed_loops:
-            return HealthCheckResult(
-                self.name, HealthStatus.DEGRADED,
-                f"Stopped loops: {', '.join(failed_loops)}",
-                details={"stopped_loops": failed_loops}
-            )
-        
-        return HealthCheckResult(
-            self.name, HealthStatus.HEALTHY,
-            f"All {len(loop_tasks)} background loops are running."
-        )
-```
-
-## Checks
-
-- **Critical**: No (startup check would always pass since loops start after ready)
-- **Startup check**: No
-- **Periodic check**: Yes (every 2 minutes - detects failures quickly)
-- **Failure action**: Notify alert channel with which loop(s) stopped
+from __future__ import annotations
+
+from discord.ext import commands
+from health_check import HealthCheck, HealthCheckResult, HealthStatus
+
+
+class BackgroundLoopHealthCheck(HealthCheck):
+    def __init__(self, bot: commands.Bot) -> None:
+        self.bot = bot
+
+    `@property`
+    def name(self) -> str:
+        return "Background Loops"
+
+    `@property`
+    def critical(self) -> bool:
+        return False
+
+    async def run(self) -> HealthCheckResult:
+        cog = self.bot.get_cog("LoopCog")
+        if cog is None:
+            return HealthCheckResult(
+                self.name,
+                HealthStatus.CRITICAL,
+                "LoopCog not found. No background tasks registered.",
+            )
+
+        loop_tasks = [
+            ("Giveaway Sender", cog.sendSendReadyGiveaways),
+            ("Giveaway Ender", cog.endGiveawaysLoop),
+            ("Voice Checker", cog.checkVoiceUsers),
+            ("Voice XP", cog.addVoiceUserLoop),
+            ("AI Token Refill", cog.refillAiTokenLoop),
+            ("Ping Server", cog.pingServerLoop),
+            ("Database Backup", cog.backupDatabaseLoop),
+            ("Booster Roles", cog.removeExpiredClaimedBoosterRoles),
+            ("Booster Channels", cog.removeExpiredClaimedBoosterChannels),
+            ("Scheduled Messages", cog.sendScheduledMessages),
+            ("Twitch Polling", cog.pollTwitchStreams),
+        ]
+
+        failed_loops = [name for name, task in loop_tasks if not task.is_running()]
+        if failed_loops:
+            return HealthCheckResult(
+                self.name,
+                HealthStatus.DEGRADED,
+                f"Stopped loops: {', '.join(failed_loops)}",
+                details={"stopped_loops": failed_loops},
+            )
+
+        return HealthCheckResult(
+            self.name,
+            HealthStatus.HEALTHY,
+            f"All {len(loop_tasks)} background loops are running.",
+        )
🧰 Tools
🪛 Flake8 (7.3.0)

[error] 6-6: SyntaxError: invalid decimal literal

(E999)

🪛 GitHub Actions: Python Type Checking with Issue Management / 0_type-check (3.12).txt

[error] 6-6: mypy syntax error: invalid decimal literal [syntax]. Command: mypy . --explicit-package-bases --soft-error-limit -1 --show-error-codes --no-error-summary --disallow-untyped-defs --disallow-incomplete-defs --no-implicit-optional --warn-return-any --warn-unused-ignores --warn-redundant-casts --warn-unreachable

🪛 GitHub Actions: Python Type Checking with Issue Management / type-check (3.12)

[error] 6-6: mypy: invalid decimal literal [syntax].

🪛 GitHub Actions: Ruff Lint & Format / 0_lint_and_format.txt

[error] 3-3: ruff format failed to parse extensions/health_check.py:3:10: Simple statements must be separated by newlines or semicolons.

🪛 GitHub Actions: Ruff Lint & Format / lint_and_format

[error] 3-3: ruff format failed: Failed to parse extensions/health_check.py:3:10: Simple statements must be separated by newlines or semicolons

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@extensions/health_check.py` around lines 1 - 79, Replace the Markdown blob
with a valid Python module that defines BackgroundLoopHealthCheck: add imports
(from __future__ annotations, discord.ext.commands, and
HealthCheck/HealthCheckResult/HealthStatus), implement class
BackgroundLoopHealthCheck(bot: commands.Bot) with properties name and critical,
and an async run() method that calls self.bot.get_cog("LoopCog") (check cog is
None), builds the loop_tasks list referencing the cog methods
(sendSendReadyGiveaways, endGiveawaysLoop, checkVoiceUsers, addVoiceUserLoop,
refillAiTokenLoop, pingServerLoop, backupDatabaseLoop,
removeExpiredClaimedBoosterRoles, removeExpiredClaimedBoosterChannels,
sendScheduledMessages, pollTwitchStreams), computes failed_loops via
task.is_running(), and returns HealthCheckResult with HealthStatus.CRITICAL if
cog missing, HealthStatus.DEGRADED with details when failed_loops nonempty, or
HealthStatus.HEALTHY otherwise.

Comment thread extensions/health_check.py
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
extensions/health_check.py (1)

1-83: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

The file is still Markdown prose, blocking all Python tooling.

This file remains in raw Markdown format and cannot be parsed as a Python module. Lines 22-76 contain Python code embedded in a Markdown code fence (\``python ... ````), but Python interpreters, linters, and type checkers cannot execute or analyze it. The pipeline failures (mypy syntax error at line 6, ruff parse failure at line 3) confirm that this critical issue from the previous review is unresolved.

While lines 52-54 correctly add the two missing loops (clearNotifiedUsersLoop and sendPokemonWerbung) to the embedded code, these changes have no effect until the file is converted to a valid Python module with proper imports and executable class definitions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@extensions/health_check.py` around lines 1 - 83, This file is still plain
Markdown with a fenced Python snippet, causing parse errors; convert it into a
real Python module by removing the Markdown prose and backticks, add necessary
imports (HealthCheck, HealthCheckResult, HealthStatus, typing if needed), and
implement the BackgroundLoopHealthCheck class as executable code using the run
method shown; reference the cog look-up (self.bot.get_cog("LoopCog")) and the
loop task names/methods (sendSendReadyGiveaways, endGiveawaysLoop,
checkVoiceUsers, addVoiceUserLoop, refillAiTokenLoop, pingServerLoop,
backupDatabaseLoop, removeExpiredClaimedBoosterRoles,
removeExpiredClaimedBoosterChannels, sendScheduledMessages, pollTwitchStreams,
clearNotifiedUsersLoop, sendPokemonWerbung) so linters/mypy can import and
type-check them, and place the human-readable description in module docstring or
separate docs rather than leaving Markdown fences in the .py file.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@extensions/health_check.py`:
- Around line 1-83: This file is still plain Markdown with a fenced Python
snippet, causing parse errors; convert it into a real Python module by removing
the Markdown prose and backticks, add necessary imports (HealthCheck,
HealthCheckResult, HealthStatus, typing if needed), and implement the
BackgroundLoopHealthCheck class as executable code using the run method shown;
reference the cog look-up (self.bot.get_cog("LoopCog")) and the loop task
names/methods (sendSendReadyGiveaways, endGiveawaysLoop, checkVoiceUsers,
addVoiceUserLoop, refillAiTokenLoop, pingServerLoop, backupDatabaseLoop,
removeExpiredClaimedBoosterRoles, removeExpiredClaimedBoosterChannels,
sendScheduledMessages, pollTwitchStreams, clearNotifiedUsersLoop,
sendPokemonWerbung) so linters/mypy can import and type-check them, and place
the human-readable description in module docstring or separate docs rather than
leaving Markdown fences in the .py file.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2f465ee0-6033-4235-b4bc-d2c10ae1cd98

📥 Commits

Reviewing files that changed from the base of the PR and between 2caa935 and a007927.

📒 Files selected for processing (1)
  • extensions/health_check.py

@EntchenEric EntchenEric merged commit e2e7d06 into development May 26, 2026
5 of 8 checks passed
@EntchenEric EntchenEric deleted the fix/issue-1416-background-loop-health-check branch May 26, 2026 18:04
EntchenEric added a commit that referenced this pull request May 26, 2026
* Add background loop health check implementation

* Add additional background loops to health check

* Add clearNotifiedUsersLoop and sendPokemonWerbung to health check

---------

Co-authored-by: EntchenEric <entcheneric@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant