跳到正文
cccskills
免费在 GitHub 公开

starrocks-self-healing

StarRocks self-healing automation — auto-restart failed Routine Load jobs (detect PAUSED/CANCELLED + RESUME), rebalance tablet distribution after BE add/remove (REBALANCE command), trigger manual compaction for high-score tablets, auto-ANALYZE stale statistics, partition management (auto-drop old partitions), dead load label cleanup, BE disk space alerts + auto-tiering, scheduled health check + remediation agent

中文简介正在准备中。当前显示原文说明。

ivanshamaev/de-agent-skills星标 22(GitHub 22 + cccskills 0)

实测评分 未实测

查看安装方法

包含的文件(1)

  • SKILL.md15.8 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

StarRocks Self-Healing Automation

When to Use

  • Automatically recover Routine Load jobs that pause due to transient errors
  • Rebalance tablets after adding new BEs to the cluster
  • Trigger compaction when compaction score exceeds threshold
  • Automatically clean up expired load labels and old partitions
  • Schedule periodic health checks that remediate common issues without manual intervention

Auto-Restart Routine Load Jobs

import pymysql
import time
import logging
from dataclasses import dataclass
from typing import List, Optional

logger = logging.getLogger(__name__)


@dataclass
class RoutineLoadJob:
    db: str
    name: str
    state: str
    reason: str
    error_log_urls: str


class StarRocksSelfHealingAgent:
    def __init__(self, host: str, port: int = 9030, user: str = "root", password: str = ""):
        self.conn_params = dict(host=host, port=port, user=user, password=password)

    def _query(self, sql: str, db: str = None) -> list:
        params = {**self.conn_params}
        if db:
            params["db"] = db
        conn = pymysql.connect(**params)
        try:
            cursor = conn.cursor()
            cursor.execute(sql)
            cols = [d[0] for d in cursor.description] if cursor.description else []
            return [dict(zip(cols, r)) for r in cursor.fetchall()]
        finally:
            conn.close()

    def _execute(self, sql: str):
        conn = pymysql.connect(**self.conn_params)
        try:
            conn.cursor().execute(sql)
            conn.commit()
        finally:
            conn.close()

    # ─────────────────────────────────────────────────────────
    # Routine Load: auto-resume transient failures
    # ─────────────────────────────────────────────────────────

    TRANSIENT_PAUSE_PATTERNS = [
        "connection refused",
        "kafka broker not available",
        "network timeout",
        "leader not available",
    ]

    HARD_PAUSE_PATTERNS = [
        "errortoomain",      # data quality: must fix source
        "offset out of range",  # Kafka retention: need to reset
        "schema",
    ]

    def heal_routine_loads(self, databases: List[str]) -> List[dict]:
        """Check all databases for paused Routine Load jobs; auto-resume transient ones."""
        actions = []

        for db in databases:
            try:
                rows = self._query(f"SHOW ROUTINE LOAD FROM {db}", db=db)
            except Exception as e:
                logger.warning(f"Failed to check RL in {db}: {e}")
                continue

            for row in rows:
                state = row.get("State", "")
                name = row.get("Name", "")
                reason = (row.get("ReasonOfStateChanged") or "").lower()

                if state != "PAUSED":
                    continue

                # Determine if this is a transient or hard pause
                is_transient = any(p in reason for p in self.TRANSIENT_PAUSE_PATTERNS)
                is_hard = any(p in reason for p in self.HARD_PAUSE_PATTERNS)

                if is_transient and not is_hard:
                    logger.info(f"Auto-resuming {db}.{name} (transient: {reason[:100]})")
                    try:
                        self._execute(f"RESUME ROUTINE LOAD FOR {db}.{name}")
                        actions.append({
                            "action": "RESUMED",
                            "job": f"{db}.{name}",
                            "reason": reason[:100],
                        })
                    except Exception as e:
                        logger.error(f"Failed to resume {db}.{name}: {e}")
                        actions.append({"action": "RESUME_FAILED", "job": f"{db}.{name}", "error": str(e)})
                else:
                    logger.warning(f"Manual intervention required for {db}.{name}: {reason[:200]}")
                    actions.append({
                        "action": "REQUIRES_MANUAL_FIX",
                        "job": f"{db}.{name}",
                        "reason": reason[:200],
                    })

        return actions

Compaction Backlog Healing

    def heal_compaction_backlog(self, compaction_score_threshold: int = 100) -> List[dict]:
        """Trigger manual compaction on BEs with high compaction score."""
        backends = self._query("SHOW BACKENDS")
        actions = []

        for be in backends:
            host = be.get("Host", "")
            score_str = be.get("CompactionScore", "0")
            try:
                score = float(score_str)
            except (ValueError, TypeError):
                continue

            if score > compaction_score_threshold:
                logger.info(f"Triggering compaction on {host} (score={score:.0f})")
                try:
                    # Trigger compaction via StarRocks admin command
                    self._execute(f"ADMIN COMPACT TABLE TABLETS ALL")
                    actions.append({
                        "action": "COMPACTION_TRIGGERED",
                        "be_host": host,
                        "compaction_score": score,
                    })
                except Exception as e:
                    logger.error(f"Compaction trigger failed on {host}: {e}")
                    actions.append({
                        "action": "COMPACTION_FAILED",
                        "be_host": host,
                        "error": str(e),
                    })

        return actions

Auto-ANALYZE Stale Statistics

    def heal_stale_statistics(
        self,
        databases: List[str],
        max_age_hours: int = 24,
    ) -> List[dict]:
        """Find and refresh stale statistics."""
        actions = []

        for db in databases:
            try:
                jobs = self._query(f"""
                    SELECT TableName, Status, EndTime
                    FROM _statistics_.analyze_jobs
                    WHERE DbName = '{db}'
                    ORDER BY EndTime DESC
                """)
            except Exception:
                continue

            from datetime import datetime, timedelta
            from collections import defaultdict

            latest_by_table: dict = defaultdict(lambda: None)
            for job in jobs:
                table = job.get("TableName")
                end_time = job.get("EndTime")
                if table and end_time and latest_by_table[table] is None:
                    latest_by_table[table] = job

            # Find all tables in the database
            try:
                tables = self._query(f"SHOW TABLES FROM {db}")
                table_names = [t.get(f"Tables_in_{db}") or list(t.values())[0] for t in tables]
            except Exception:
                continue

            now = datetime.utcnow()
            for table in table_names:
                job = latest_by_table.get(table)
                needs_analyze = False

                if job is None:
                    needs_analyze = True
                    age_desc = "never analyzed"
                else:
                    status = job.get("Status", "")
                    end_time = job.get("EndTime")
                    if isinstance(end_time, str):
                        try:
                            end_time = datetime.strptime(end_time[:19], "%Y-%m-%d %H:%M:%S")
                            age_hours = (now - end_time).total_seconds() / 3600
                            needs_analyze = age_hours > max_age_hours or status != "SUCCESS"
                            age_desc = f"{age_hours:.1f}h old"
                        except ValueError:
                            needs_analyze = True
                            age_desc = "unknown age"

                if needs_analyze:
                    try:
                        self._execute(f"ANALYZE TABLE {db}.{table} WITH ASYNC MODE")
                        logger.info(f"ANALYZE triggered for {db}.{table} ({age_desc})")
                        actions.append({"action": "ANALYZE_TRIGGERED", "table": f"{db}.{table}"})
                    except Exception as e:
                        logger.error(f"ANALYZE failed for {db}.{table}: {e}")

        return actions

Auto-Drop Expired Partitions

    def heal_partition_retention(
        self,
        db: str,
        table: str,
        retention_days: int = 90,
        dry_run: bool = True,
    ) -> List[dict]:
        """Drop partitions older than retention_days."""
        from datetime import datetime, timedelta

        cutoff = datetime.utcnow() - timedelta(days=retention_days)
        cutoff_str = cutoff.strftime("%Y-%m-%d")

        partitions = self._query(f"SHOW PARTITIONS FROM {db}.{table}")
        actions = []

        for p in partitions:
            p_name = p.get("PartitionName", "")
            p_range = p.get("Range", "") or p.get("PartitionRange", "")

            # Parse range upper bound: "[2023-01-01, 2023-02-01)"
            range_match = __import__("re").search(r'\[([^,]+),\s*([^\)]+)\)', str(p_range))
            if not range_match:
                continue

            upper_bound = range_match.group(2).strip().rstrip(")")
            try:
                p_date = datetime.strptime(upper_bound[:10], "%Y-%m-%d")
            except ValueError:
                continue

            if p_date.date() < cutoff.date():
                action = {
                    "action": "DROP_PARTITION" if not dry_run else "DRY_RUN_DROP",
                    "table": f"{db}.{table}",
                    "partition": p_name,
                    "range": p_range,
                }
                if not dry_run:
                    try:
                        self._execute(f"ALTER TABLE {db}.{table} DROP PARTITION {p_name}")
                        logger.info(f"Dropped old partition {db}.{table}.{p_name}")
                    except Exception as e:
                        action["error"] = str(e)
                        logger.error(f"Failed to drop {p_name}: {e}")
                else:
                    logger.info(f"[DRY RUN] Would drop {db}.{table}.{p_name} ({p_range})")
                actions.append(action)

        return actions

Dead Load Label Cleanup

    def heal_failed_load_labels(
        self,
        databases: List[str],
        cleanup_cancelled: bool = True,
    ) -> List[dict]:
        """Report (and optionally clean up) CANCELLED broker load jobs."""
        actions = []

        for db in databases:
            rows = self._query(
                f"SHOW LOAD FROM {db} WHERE State = 'CANCELLED' ORDER BY CreateTime DESC LIMIT 100"
            )
            for row in rows:
                label = row.get("Label", "")
                finish_time = row.get("FinishTime", "")
                actions.append({
                    "db": db,
                    "label": label,
                    "finish_time": finish_time,
                    "note": "CANCELLED label — reuse with new label name to retry",
                })
            logger.info(f"{db}: found {len(rows)} CANCELLED load labels")

        return actions

Scheduled Health Check and Remediation

def run_full_health_check(agent: StarRocksSelfHealingAgent, databases: List[str]):
    """Run all healing checks and summarize actions taken."""
    all_actions = []

    # 1. Routine Load recovery
    logger.info("=== Checking Routine Load jobs ===")
    rl_actions = agent.heal_routine_loads(databases)
    all_actions.extend(rl_actions)

    # 2. Compaction backlog
    logger.info("=== Checking compaction backlog ===")
    comp_actions = agent.heal_compaction_backlog(compaction_score_threshold=100)
    all_actions.extend(comp_actions)

    # 3. Stale statistics
    logger.info("=== Checking stale statistics ===")
    stats_actions = agent.heal_stale_statistics(databases, max_age_hours=48)
    all_actions.extend(stats_actions)

    # 4. Partition retention (dry run by default)
    logger.info("=== Checking partition retention ===")
    for db in databases:
        # Only run on tables with known high retention overhead
        for table in ["orders", "events"]:
            try:
                part_actions = agent.heal_partition_retention(db, table, retention_days=90, dry_run=True)
                all_actions.extend(part_actions)
            except Exception:
                pass

    # Summary
    by_type: dict = {}
    for a in all_actions:
        key = a.get("action", "UNKNOWN")
        by_type[key] = by_type.get(key, 0) + 1

    logger.info(f"Health check complete. Actions: {by_type}")
    return all_actions


# Schedule via Airflow or cron:
# */10 * * * * python -c "
#   from starrocks_healer import run_full_health_check, StarRocksSelfHealingAgent
#   agent = StarRocksSelfHealingAgent('sr-fe.internal')
#   run_full_health_check(agent, ['sales', 'finance'])
# "

Airflow Integration

from airflow.decorators import dag, task
from datetime import datetime

@dag(
    dag_id="starrocks_self_healing",
    schedule="*/10 * * * *",  # every 10 minutes
    start_date=datetime(2024, 1, 1),
    catchup=False,
    tags=["starrocks", "self-healing"],
)
def starrocks_self_healing():

    @task
    def heal_routine_loads():
        from starrocks_healer import StarRocksSelfHealingAgent
        agent = StarRocksSelfHealingAgent("sr-fe.internal")
        return agent.heal_routine_loads(["sales", "finance"])

    @task
    def heal_compaction():
        from starrocks_healer import StarRocksSelfHealingAgent
        agent = StarRocksSelfHealingAgent("sr-fe.internal")
        return agent.heal_compaction_backlog(threshold=150)

    @task
    def heal_statistics():
        from starrocks_healer import StarRocksSelfHealingAgent
        agent = StarRocksSelfHealingAgent("sr-fe.internal")
        return agent.heal_stale_statistics(["sales"], max_age_hours=48)

    # Run checks in parallel — they are independent
    [heal_routine_loads(), heal_compaction(), heal_statistics()]


dag = starrocks_self_healing()

Anti-Patterns

  1. Auto-resuming ALL paused Routine Load jobs — data-quality pauses (ErrorTooMany) need root cause fix first; resuming blindly re-pauses in seconds. Only resume transient pauses.
  2. Triggering compaction on every check run — compaction is expensive; only trigger when CompactionScore exceeds a meaningful threshold (> 100).
  3. Dropping partitions without dry_run=True first — partition drop is irreversible; always test with dry_run before enabling live drops.
  4. Running self-healing with root/admin account — use a dedicated user with minimal permissions (ANALYZE, RESUME ROUTINE LOAD, ALTER TABLE partition-only).
  5. No rate limiting on ANALYZE triggers — triggering ANALYZE on all tables simultaneously saturates BEs; add a delay or limit to N tables per run.
  6. Ignoring NEED_SCHEDULE state — NEED_SCHEDULE means the FE is trying to schedule but can't (e.g., no alive BEs); don't confuse with PAUSED.

References

  • Routine Load management: docs.starrocks.io/docs/loading/RoutineLoad/#manage-a-routine-load-job
  • Compaction: docs.starrocks.io/docs/administration/management/BE_management/compaction/
  • ANALYZE TABLE: docs.starrocks.io/docs/sql-reference/sql-statements/data-definition/ANALYZE_TABLE/
  • Related skills: [[starrocks-admin-cluster-health]], [[starrocks-admin-compaction]], [[starrocks-routine-load-kafka]], [[starrocks-ai-incident-rca]]

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

AIOps autonomous incident response agent — LLM-driven diagnosis loop (Claude tool-use agent with kubectl/SQL/Prometheus tools), alert-to-action pipeline (PagerDuty webhook → agent trigger), automated RCA generation (failure taxonomy + log correlation), self-healing action executor with approval gate, incident severity classification, runbook automation (structured YAML runbooks executed by agent), escalation logic, incident timeline auto-generation, Slack integration for human-in-the-loop approvals

中文简介正在准备中。当前显示原文说明。

ivanshamaev/de-agent-skills222026年5月20日 更新

AIOps capacity planning agent — predictive resource forecasting (Prophet/linear regression on Prometheus metrics), Kubernetes HPA/VPA right-sizing recommendations, node capacity headroom calculator, Kafka partition and broker capacity model, Spark executor sizing from job history, data lake storage growth forecast (S3/GCS), cluster autoscaler simulation, automatic HPA threshold tuning, cost-per-job attribution, capacity planning report generator

中文简介正在准备中。当前显示原文说明。

ivanshamaev/de-agent-skills222026年5月20日 更新

AIOps infrastructure anomaly detection — statistical baseline (z-score/IQR/seasonal decomposition), Prometheus anomaly rules (predict_linear/stddev_over_time/MAD), ML-based detection (Isolation Forest/Prophet/LSTM autoencoder on metrics), metric correlation clustering, automated alert threshold tuning from historical data, Kubernetes resource anomaly detection, data pipeline anomaly signals (row count z-score/freshness drift/throughput drop), Python sklearn/Prophet integration, Grafana anomaly panel

中文简介正在准备中。当前显示原文说明。

ivanshamaev/de-agent-skills222026年5月20日 更新

AIOps observability copilot — natural language to PromQL/LogQL translation, alert explanation in plain English (what fired/why/impact), Grafana dashboard auto-generation from service topology, anomaly narrative generation from metric patterns, on-call context enrichment (related alerts/recent deploys/runbook links), log pattern clustering for noise reduction, SLO status explanation, automated weekly observability health digest

中文简介正在准备中。当前显示原文说明。

ivanshamaev/de-agent-skills222026年5月20日 更新

AIOps continuous platform optimization agent — autonomous optimization loop (observe→analyze→recommend→apply), Kubernetes resource rightsizing (VPA recommendation reader + apply), Kafka consumer lag auto-scale, Spark job configuration tuning from history, idle resource detection (unused deployments/PVCs/topics), compaction and VACUUM scheduling, auto-ANALYZE stale statistics, cost savings attribution, optimization audit trail, human approval gate for high-risk changes

中文简介正在准备中。当前显示原文说明。

ivanshamaev/de-agent-skills222026年5月20日 更新

AIOps autonomous query cost analyzer — Trino system.runtime.query_history cost SQL (CPU time/peak memory/bytes scanned), ClickHouse system.query_log top expensive queries, Spark UI cost attribution by user/job/DAG, BigQuery INFORMATION_SCHEMA.JOBS cost analysis, LLM-driven query rewrite recommendations (missing partition filters/broadcast hints/aggregation pushdown), cost anomaly detection (user over daily budget), automated query tagging, chargeback report by team/project

中文简介正在准备中。当前显示原文说明。

ivanshamaev/de-agent-skills222026年5月20日 更新

查看 ivanshamaev 的全部技能

报告此技能的问题