CI/CD و اتوماسیونمهندسانسطح متوسط

حلقه بی‌پایان Jira Automation: وقتی قانون خودش را صدا می‌زند

اگر trigger و action یک قانون Jira Automation روی یک رویداد بیفتند، قانون خودش را دوباره اجرا می‌کند و issue با صدها کامنت تکراری پر می‌شود. این مقاله علت، پاک‌سازی و راه‌حل پایدار را نشان می‌دهد.

تحریریه کلودیکپ ۷ دقیقه مطالعه
حلقه بی‌پایان Jira Automation: وقتی قانون خودش را صدا می‌زند

نشانه مشکل: issue به یک ربات کامنت‌ساز تبدیل می‌شود

صبح دوشنبه چند نفر همزمان پیام می‌دهند که «تسک OPS-1423 دیوانه شده». issue را باز می‌کنی و می‌بینی ۴۰۰ کامنت پشت سر هم با متن یکسان زیر آن نشسته، وضعیت بین In Review و Done بالا و پایین می‌پرد و صندوق ایمیل watcherها پر شده است. مهم‌ترین سرنخ این است که همه کامنت‌ها یک author مشترک دارند: Automation for Jira.

اگر به مسیر Project settings > Automation > Audit log بروی، تصویر واضح‌تر می‌شود: یک قانون در چند ثانیه چند صد بار اجرا شده و هر اجرا با وضعیت Success ثبت شده است. این الگو تقریباً همیشه یعنی یک چیز: قانون دارد خودش را trigger می‌کند.

نشانه قطعی حلقه این است که تعداد اجراهای audit log با تعداد رویدادهای واقعی تیم هم‌خوانی ندارد. اگر ۵ نفر در روز کامنت می‌گذارند ولی قانون ۹۰۰ بار اجرا شده، مشکل پیکربندی است نه رفتار کاربران.

علت ریشه‌ای: trigger و action روی یک رویداد می‌افتند

سناریوی رایج این است که قانونی شبیه زیر ساخته شده:

  • Trigger: Issue commented on با شرطی که متن کامنت شامل کلمه deploy باشد.
  • Action: انتقال issue به Done و افزودن کامنت «✅ Deployed by pipeline».

اجرای اول از یک کامنت واقعی تیم شروع می‌شود. قانون issue را به Done می‌برد و کامنتی می‌گذارد که... خودش شامل کلمه deploy است. پس دوباره شرط trigger برقرار می‌شود، دوباره transition، دوباره کامنت. هر دور یک ورودی تازه برای دور بعد می‌سازد و حلقه تا وقتی چیزی جلویش را نگیرد ادامه دارد.

سه عامل این وضعیت را بدتر می‌کنند:

  • خروجی action همان ورودی trigger است. ریشه‌ی اصلی همین است؛ نه Jira، نه سرعت، نه بار سرور.
  • اجازه‌ی trigger شدن قانون توسط قوانین دیگر. در تنظیمات هر قانون گزینه‌ای با عنوان Allow other rule actions to trigger this rule وجود دارد که اگر فعال باشد، action یک قانون می‌تواند قانون دیگر (یا خودش) را دوباره بیدار کند.
  • ترکیب چند قانون روی یک رویداد. یک قانون کامنت می‌گذارد، قانون دوم روی «Issue commented on» گوش می‌دهد و وضعیت را عوض می‌کند، قانون سوم دوباره کامنت می‌گذارد. حلقه بین دو یا سه قانون شکل می‌گیرد، نه داخل یکی.

Jira در بعضی موارد پس از تعداد زیادی اجرا در بازه کوتاه، قانون را غیرفعال می‌کند. این یک تور محافظتی است، نه راه‌حل؛ تا وقتی قانون را درست نکنی، اولین فعال‌سازی دستی دوباره حلقه را روشن می‌کند.

راه‌حل گام‌به‌گام

۱) قانون را همین حالا خاموش کن

قبل از هر کار دیگری جلوی خون‌ریزی را بگیر. برای قوانین پروژه‌ای: Project settings > Automation و کلید قانون را خاموش کن. برای قوانین سراسری: Settings > System > Automation rules. خاموش کردن، اجرای در جریان را متوقف نمی‌کند اما از دور بعد جلوگیری می‌کند.

۲) مقیاس خرابی را از audit log بردار

در audit log فیلدهای زمان اجرا، نام قانون و وضعیت را ببین. اگر زمان شروع مشخص است، می‌توانی بازه‌ی آلودگی را دقیق تعیین کنی: آستانه‌ی زمانی قبل از شروع را در جست‌وجوهای بعدی استفاده کن.

۳) issueهای آلوده را با JQL پیدا کن

متن کامنتی که ربات می‌گذاشته را به‌عنوان اثر انگشت استفاده کن:

project = OPS AND comment ~ "Deployed by pipeline" AND updated >= -1d ORDER BY updated DESC

عملگر ~ روی فیلد comment یک جست‌وجوی متنی است، پس دقیقاً همان کامنت‌های تکراری را برمی‌گرداند و issueهای سالم را کنار می‌گذارد.

۴) کامنت‌های تکراری را با REST API پاک کن

Jira در UI حذف گروهی کامنت ندارد. اول accountId ربات را از یکی از کامنت‌های آلوده بردار:

curl -s -u "$JIRA_EMAIL:$JIRA_TOKEN" \
  "https://your-domain.atlassian.net/rest/api/3/issue/OPS-1423/comment?maxResults=1" \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['comments'][0]['author']['accountId'])"

سپس اسکریپت زیر را اجرا کن. این اسکریپت فقط کامنت‌هایی را حذف می‌کند که author آن‌ها همان اکانت ربات است، پس کامنت‌های واقعی تیم دست‌نخورده می‌مانند:

import os
import time
import requests

BASE = os.environ["JIRA_BASE"].rstrip("/")   # https://your-domain.atlassian.net
AUTH = (os.environ["JIRA_EMAIL"], os.environ["JIRA_TOKEN"])
BOT_ID = os.environ["AUTOMATION_ACCOUNT_ID"]
API = f"{BASE}/rest/api/3"


def search(jql):
    keys, token = [], None
    while True:
        params = {"jql": jql, "maxResults": 100, "fields": "key"}
        if token:
            params["nextPageToken"] = token
        r = requests.get(f"{API}/search/jql", auth=AUTH, params=params, timeout=30)
        r.raise_for_status()
        data = r.json()
        keys += [i["key"] for i in data.get("issues", [])]
        token = data.get("nextPageToken")
        if not token:
            return keys


def iter_comments(key):
    start = 0
    while True:
        r = requests.get(
            f"{API}/issue/{key}/comment",
            auth=AUTH,
            params={"startAt": start, "maxResults": 100},
            timeout=30,
        )
        r.raise_for_status()
        data = r.json()
        batch = data.get("comments", [])
        for c in batch:
            yield c
        start += len(batch)
        if not batch or start >= data.get("total", 0):
            return


def purge(key):
    removed = 0
    for c in iter_comments(key):
        if c["author"].get("accountId") == BOT_ID:
            r = requests.delete(
                f"{API}/issue/{key}/comment/{c['id']}", auth=AUTH, timeout=30
            )
            if r.status_code in (200, 204):
                removed += 1
            time.sleep(0.1)          # مرز ایمن برای rate limit
    return removed


if __name__ == "__main__":
    jql = 'project = OPS AND comment ~ "Deployed by pipeline" AND updated >= -1d'
    for k in search(jql):
        print(k, purge(k))

اگر روی نسخه‌ای از Jira هستید که /search/jql را ندارد، همان تابع search را به /rest/api/3/search و صفحه‌بندی startAt تغییر بده؛ بقیه‌ی منطق یکی است.

۵) قانون را بدون حلقه بازنویسی کن

سه الگو را به ترتیب اولویت امتحان کن:

  1. trigger را از «کامنت» به «تغییر فیلد» منتقل کن. اگر تریگر روی Field value changed برای یک فیلد مثل Deploy Status باشد و action آن فیلد را تغییر ندهد، حلقه از پایه غیرممکن می‌شود.
  2. گارد بگذار. اگر مجبوری روی کامنت تریگر بزنی، بلافاصله بعد از trigger یک Advanced compare condition اضافه کن: مقدار اول {{comment.body}}، شرط does not contain، مقدار دوم Deployed by pipeline. کامنتی که خود ربات می‌گذارد شرط را رد می‌کند و کار تمام است.
  3. عنوان قانون را در متن action تکرار نکن. متن کامنت ربات را طوری بنویس که هیچ‌وقت با کلمه‌ی کلیدی trigger هم‌پوشانی نداشته باشد. کامنت واقعی بهتری هم می‌شود.

۶) محافظ‌های سطح پلتفرم

  • در تنظیمات قانون، گزینه‌ی Allow other rule actions to trigger this rule را غیرفعال نگه دار.
  • قوانین را با یک پروژه‌ی sandbox تست کن و فقط بعد از دیدن یک اجرای تمیز روی پروژه‌ی واقعی فعالش کن.
  • مالکیت قانون را به یک سرویس‌اکانت اختصاصی بده تا خروج فرد از تیم باعث از کار افتادن قانون نشود.

چطور مطمئن شویم مشکل واقعاً حل شده

سه بررسی کوتاه کافی است:

  1. تست دستی روی یک issue آزمایشی: کامنتی بگذار که کلمه‌ی کلیدی را ندارد. باید هیچ اتفاقی نیفتد. بعد کامنتی با کلمه‌ی کلیدی بگذار؛ باید دقیقاً یک transition و یک کامنت اضافه شود.
  2. شمارش در audit log: بعد از تست، تعداد اجراهای قانون باید برابر تعداد رویدادهای واقعی باشد، نه بیشتر. اگر عدد بزرگ‌تر بود، گارد دوم لازم داری.
  3. پایش ۱۰ دقیقه‌ای: JQL را دوباره اجرا کن و تعداد کامنت‌های ربات را با تعداد قبلی مقایسه کن. عدد باید ثابت بماند.

یک JQL سریع برای این پایش:

project = OPS AND comment ~ "Deployed by pipeline" AND updated >= -10m

پیشگیری و بهترین روش‌ها

  • قاعده‌ی طلایی: هیچ actionی نباید رویدادی تولید کند که trigger همان قانون (یا قانون دیگری) روی آن گوش می‌دهد.
  • برای هر قانون حداکثر یک مسئولیت تعریف کن. قانونی که هم وضعیت را عوض می‌کند هم کامنت می‌گذارد هم فیلد ویرایش می‌کند، سه کاندید حلقه است.
  • قوانین Issue commented on گران‌ترین تریگرها برای نگهداری‌اند؛ اگر می‌توانی روی transition یا تغییر فیلد تریگر بزن.
  • قبل از فعال‌سازی هر قانون جدید، به سؤال «اگر این action دوباره اجرا شود چه می‌شود؟» جواب بده.
  • audit log را هفتگی چک کن. جهش ناگهانی تعداد اجرا اولین نشانه‌ی حلقه است، حتی وقتی هنوز کسی شکایتی نکرده.
  • پس از حل مشکل، محتوای قانون را در مخزن IaC یا مستندات تیم ثبت کن تا فرد بعدی همان ابتدا گاردها را ببیند.

Jira Automation ابزار قدرتمندی است و بیشتر خرابی‌هایش از خود Jira نیست؛ از طراحی rule است. اگر trigger و action را روی دو رویداد جدا نگه داری، ۹۰٪ حلقه‌های بی‌پایان هیچ‌وقت شکل نمی‌گیرند.

اشتراک‌گذاری:

نویسنده

تحریریه کلودیکپ

تیم محتوای کلودیکپ

تیم فنی و محتوای کلودیکپ؛ مهندسانی که هر روز با DevOps، Kubernetes و زیرساخت ابری کار می‌کنند و تجربه‌هایشان را اینجا می‌نویسند.

سوالات متداول

سوالات متداول این مقاله

در audit log نام قانونی که مکرراً اجرا شده را ببین. اگر فقط یک نام تکرار می‌شود، حلقه درون همان قانون است. اگر دو یا سه نام به‌صورت زنجیره‌ای پشت سر هم اجرا می‌شوند، action یک قانون، trigger قانون بعدی را فعال می‌کند و باید گارد را روی همان حلقه‌ی بین‌قانونی بگذاری.

کامنت حذف‌شده در تاریخچه باقی نمی‌ماند، اما transitionها و changelog دست‌نخورده می‌مانند. چون اسکریپت فقط کامنت‌های متعلق به accountId ربات را حذف می‌کند، بحث‌های واقعی تیم از بین نمی‌رود. پیش از اجرا، یک نسخه از کلید issueهای هدف را جایی ذخیره کن.

Jira در بعضی موارد پس از تعداد زیادی اجرا در بازه کوتاه، قانون را به‌عنوان محافظت غیرفعال می‌کند، اما این رفتار تضمینی نیست و به بار و نسخه‌ی پلتفرم بستگی دارد. راه‌حل پایدار، افزودن شرط گارد در خود قانون است تا حلقه هرگز شکل نگیرد.

ادامه مطالعه

مقالات مرتبط

قطع شدن WebSocket در HAProxy هر ۵۰ ثانیه؛ علت و راه‌حل
Linux و مدیریت سرورمتوسط

قطع شدن WebSocket در HAProxy هر ۵۰ ثانیه؛ علت و راه‌حل

اگر WebSocket یا SSE پشت HAProxy هر ۵۰ ثانیه قطع می‌شود، مقصر تایماوت‌های پیش‌فرض فایل نمونه است. در این مقاله علت ریشه‌ای و پیکربندی درست timeout tunnel را بررسی می‌کنیم.

تحریریه کلودیکپ ۷ دقیقه مطالعه
پرشدن دیسک PostgreSQL با WAL اسلات replication غیرفعال
مانیتورینگ و پایداریمتوسط

پرشدن دیسک PostgreSQL با WAL اسلات replication غیرفعال

یک replication slot غیرفعال می‌تواند pg_wal را تا پر شدن کامل دیسک نگه دارد و دیتابیس را عملاً از کار بیندازد. در این مقاله ریشه‌یابی، رفع و پیشگیری از آن را گام‌به‌گام بررسی می‌کنیم.

تحریریه کلودیکپ ۶ دقیقه مطالعه
«idle in transaction» در PostgreSQL: قاتل خاموش autovacuum
مانیتورینگ و پایداریمتوسط

«idle in transaction» در PostgreSQL: قاتل خاموش autovacuum

نشست‌های idle in transaction تراکنش را باز نگه می‌دارند، جلوی autovacuum را می‌گیرند و جدول‌ها را باد می‌کنند تا جایی که دیسک پر و اتصال‌ها تمام می‌شود. این مقاله تشخیص و درمان کامل آن است.

تحریریه کلودیکپ ۸ دقیقه مطالعه

در پیاده‌سازی به کمک نیاز دارید؟

تیم کلودیکپ همین کار را هر روز برای تیم‌های دیگر انجام می‌دهد. اگر جایی گیر کرده‌اید، با ما صحبت کنید.