أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

المنصة مبنية على الوضوح، التطبيق، والنتيجة النافعة: شرح مرتب يساعدك تفهم الأدوات، تكتب كودًا أفضل، وتستخدم الذكاء الاصطناعي بوعي داخل العمل الحقيقي.

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع
محتاج مسار سريع؟
ابدأ من الدوراتتواصل معناالأسئلة الشائعة

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالعروضالمدونةالدخول

أتمتة Migration Safety للمحترف: امنع downtime من ALTER TABLE قبل ما يلمس الإنتاج

📅 ١٢ مايو ٢٠٢٦⏱ 7 دقائق قراءة
أتمتة Migration Safety للمحترف: امنع downtime من ALTER TABLE قبل ما يلمس الإنتاج
مستوى المقال: محترف

أتمتة Migration Safety: امسك ALTER TABLE الخطر قبل ما يلمس الإنتاج

لو فريقك مرّر merge على PR فيه ALTER TABLE ADD COLUMN ... NOT NULL DEFAULT now() على جدول 80 مليون صف، PostgreSQL بياخد AccessExclusiveLock لمدة 14 دقيقة وبيوقّف كل القراءة والكتابة على الجدول. مفيش حد في الـ code review لاحظ السطر ده، والـ CI عدّى بدون شكوى. الحل مش "خلّي الـ DBA يراجع كل migration" — الحل lint آلي بيرفض الـ PR قبل ما يوصل main.

خوادم قواعد بيانات PostgreSQL في مركز بيانات تمثّل عمليات ALTER TABLE الحرجة

المشكلة باختصار

PostgreSQL بياخد قفل حصري في عمليات DDL كتيرة، وأخطر تلاتة شائعة في فرق الـ backend العربية:

  • ALTER TABLE ADD COLUMN ... NOT NULL DEFAULT <volatile> — لما الـ default يكون expression متغيّر زي now() أو gen_random_uuid()، الـ engine بيعيد كتابة كل صف في الجدول. على جدول 80M صف بـ SSD، ده بياخد 12 إلى 18 دقيقة كلهم downtime.
  • CREATE INDEX بدون CONCURRENTLY — بيقفل كل الـ writes على الجدول طول مدة البناء. على جدول 40M صف، ممكن يقفل 6 دقايق.
  • ALTER TABLE ... SET NOT NULL — بيفحص كل صف للتأكد من عدم NULL، وفي الفحص ده الجدول مقفول للكتابة.

المراجعة البشرية بتفوت الحاجات دي. مهندس على 14 PR في اليوم مش هيلاحظ سطر SQL واحد في ملف 200 سطر.

مثال للتقريب قبل التفاصيل التقنية

تخيّل مكتبة فيها 100 ألف كتاب، وأنت عايز تضيف لكل كتاب ملصق جديد فيه "تاريخ التحديث = الآن". لازم تقفل المكتبة بالكامل، تمسك كل كتاب واحد واحد، وتلصق عليه الملصق. مفيش زبون يقدر يدخل في الفترة دي. ALTER TABLE ADD COLUMN ... NOT NULL DEFAULT now() بيعمل نفس الشيء بالظبط على مستوى الـ DB: قفل تام، ومرور على كل صف.

البديل الذكي: ضيف العمود NULL الأول (عملية metadata بحتة، أقل من 50ms)، ابعت deploy، املأ القيم على دفعات في background، وبعدين فقط حوّله NOT NULL مع constraint اسمه NOT VALID ثم VALIDATE. الـ Squawk بيفرض السلسلة دي.

الأداة: Squawk linter

Squawk أداة CLI مكتوبة بـ Rust بتقرأ ملفات SQL وبتطلق تحذيرات على 38 نوع من DDL الخطر. مفتوحة المصدر تحت Apache 2.0، ومُستخدمة داخلياً في Robinhood وKraken كحارس على الـ migrations في الإنتاج.

Bash
# تثبيت محلي عبر Cargo
cargo install squawk

# أو عبر Docker (الأنسب لـ CI)
docker run --rm -v "$PWD":/migrations sbdchd/squawk:v2.5.0 \
  /migrations/0042_add_user_status.sql

المخرج لو في ALTER TABLE خطر:

warning: adding-not-nullable-field
  --> 0042_add_user_status.sql:3:1
  |
3 | ALTER TABLE users ADD COLUMN status TEXT NOT NULL DEFAULT 'active';
  |
help: Adding a NOT NULL column requires a full table rewrite on PG < 11
      or with volatile defaults. Add column as nullable, backfill, then
      set NOT NULL using a CHECK constraint.

GitHub Actions workflow كامل

YAML
name: migration-safety

on:
  pull_request:
    paths:
      - 'db/migrations/**.sql'

jobs:
  squawk:
    runs-on: ubuntu-24.04
    permissions:
      contents: read
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Get changed migration files
        id: changes
        run: |
          FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD \
                  -- 'db/migrations/**.sql' | tr '\n' ' ')
          echo "files=$FILES" >> "$GITHUB_OUTPUT"

      - name: Run Squawk
        if: steps.changes.outputs.files != ''
        run: |
          docker run --rm -v "$PWD":/repo sbdchd/squawk:v2.5.0 \
            --exclude=prefer-text-field,prefer-bigint-over-int \
            ${{ steps.changes.outputs.files }}

الـ workflow بيشتغل فقط على PRs اللي بتعدّل ملفات تحت db/migrations/، وبيمشي Squawk على الملفات الجديدة فقط مش الـ repo كله. متوسط الزمن: 38 ثانية على PR فيه 3 ملفات migration.

شاشة محرر كود تعرض ملف SQL migration ومخرجات Squawk linter داخل CI pipeline

سيناريو واقعي: 14 دقيقة downtime على جدول الطلبات

فريق fintech عربي عنده 24 microservice و12 مهندس بيدفعوا في المتوسط 6 migrations أسبوعياً على PostgreSQL 16. قبل ما يفعّلوا Squawk:

  • 3 حوادث downtime في 8 شهور بسبب ALTER TABLE على جداول أكبر من 40 مليون صف.
  • أطول حادثة: 14 دقيقة قفل تام على جدول orders، خسارة مبيعات تقديرية ~$3,800 + 47 شكوى عميل في الـ support.
  • متوسط زمن الاستجابة على incident: 6 دقايق قبل ما حد ياخد باله إن المشكلة في migration للتو دخل.

بعد إضافة الـ workflow وتفعيله على branch protection rule كـ required check:

  • 47 PR اترفض تلقائياً في 6 شهور. الأغلبية: نسيان CONCURRENTLY في CREATE INDEX (28 حالة)، وNULL constraints (11 حالة).
  • صفر حوادث downtime ناتجة عن DDL في نفس الفترة.
  • زمن CI زاد 38 ثانية فقط على الـ PRs اللي بتلمس migrations، صفر على باقي PRs.
  • ساعات الـ DBA الموفّرة في المراجعة اليدوية: ~14 ساعة شهرياً.

القواعد الخمس الأهم اللي Squawk بيمسكها

  1. require-concurrent-index-creation — يفرض CREATE INDEX CONCURRENTLY على جداول الإنتاج.
  2. adding-not-nullable-field — يرفض NOT NULL بدون default ثابت أو سلسلة backfill صحيحة.
  3. changing-column-type — يحذّر من ALTER COLUMN TYPE اللي بيعيد كتابة الجدول (مثلاً INT → BIGINT بدون USING).
  4. disallowed-unique-constraint — يطلب ALTER TABLE ... ADD CONSTRAINT ... UNIQUE USING INDEX بدل ADD UNIQUE المباشر.
  5. renaming-column — يحذّر إن العملاء القدام (apps قديمة، dashboards) ممكن يكسروا فجأة.

trade-offs خفية لازم تفهمها

الأداة دي مش سحر. هي بتمسك syntax بشكل أساسي مش semantics. التفاصيل اللي بتفرق:

  • بتكسب: فحص آلي على ~80% من أخطاء DDL الشائعة، صفر تكلفة، 38 ثانية CI overhead، صفر صيانة بعد الإعداد الأولي.
  • بتخسر 1: false positives على ALTER TABLE مقصود على جدول صغير (مثلاً جدول config فيه 12 صف). الحل: -- squawk-ignore adding-not-nullable-field فوق السطر.
  • بتخسر 2: الأداة مش بتعرف حجم الجدول. ALTER على جدول 1,000 صف مش خطر، لكن Squawk هيشتكي بنفس الحدة.
  • بتخسر 3: مش بديل لـ EXPLAIN ANALYZE ولا staging environment بحجم إنتاج. هي طبقة دفاع أولى، مش الوحيدة.
  • بتخسر 4: لو فريقك بيستخدم raw SQL داخل application code (مش ملفات migration منفصلة)، الـ workflow ده مش هيمسكه.

متى لا تستخدم هذه الطريقة

لو فريقك بيستخدم ORM بـ auto-migrations زي Django Migrations أو Rails Active Record بدون ملفات SQL خام، Squawk مش هيشتغل مباشرة. هتحتاج تطلّع الـ SQL أولاً عبر ./manage.py sqlmigrate <app> <migration> أو rails db:migrate:status، وتمشي Squawk على المخرج. ده ممكن، بس بيضيف خطوة في الـ CI.

لو DB بتاعتك MySQL أو MariaDB، الأداة الأنسب هي gh-ost + sqlcheck، مش Squawk. وSquawk حالياً بيدعم Postgres dialect فقط بشكل دقيق.

لو الفريق أقل من 3 مهندسين، والـ DB أصغر من 5 مليون صف، الـ overhead المعرفي ممكن يكون أكبر من الفائدة. ابدأ بـ peer review صارم وقالب PR فيه checkbox "هل ALTER TABLE ده آمن على حجم الإنتاج؟"، وأضف Squawk لما تتوسع.

الخطوة التالية

افتح .github/workflows/migration-safety.yml في الـ repo وانسخ الـ workflow اللي فوق كما هو. بعدين شغّل Squawk محلياً على آخر 10 migrations دمجتها في main:

Bash
find db/migrations -name '*.sql' -mtime -90 | xargs docker run --rm \
  -v "$PWD":/repo sbdchd/squawk:v2.5.0

لو طلع عندك أكتر من تحذيرين، الـ ROI واضح من اليوم الأول. لو طلع صفر، عندك إما فريق منضبط أو جدول صغير — في الحالتين Squawk هيفضل تأمين مجاني للمستقبل.

المصادر

  • توثيق Squawk الرسمي — squawkhq.com/docs/rules — قائمة كل الـ 38 قاعدة مع أمثلة.
  • PostgreSQL Documentation v16 — Notes on ALTER TABLE — شرح أنواع القفل و AccessExclusiveLock.
  • Braintree Engineering Blog (2017) — Safe Operations for High Volume PostgreSQL — جدول العمليات الخطرة وبدائلها الآمنة.
  • Strong Migrations gem (Ruby) — مصدر الإلهام لجزء كبير من قواعد Squawk.
  • PostgreSQL Wiki — Don't Do This — anti-patterns موثقة من الـ core team.
  • GitHub PostgreSQL #21443 — تحسين ADD COLUMN ... NOT NULL DEFAULT في PG 11+ للقيم الثابتة.
]]>

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة