نظرة عامة

في المقال ده هنشرح خدمة الـ Alerts & Alarms على الـ Cloud، واللي بتتابع استهلاك الموارد زي الـCPU والـMemory، وبتقيّم حالات مركّبة Composite Conditions، وتبعت إشعارات (Email، SMS، Push، أو AWS SNS)، وكمان بتنفّذ Actions مخصصة.

المقال كمان بيوضح الـ Architecture، والأنماط، والخوارزميات اللي بيتم استعمالها، وفيها نموذج تجريبي معمول بـ C++.


Requirements

  • إنشاء وإدارة الإنذارات (سواء لمرة واحدة أو بشكل مجدول)
  • تحديد شروط زي: CPU أكبر من 80% وMemory أقل من 20%
  • مراقبة أكتر من مقياس في نفس الوقت (زي CPU وMemory مع بعض)
  • وسائل الإشعار: إيميل، SMS، Push Notifications، أو تكامل مع AWS SNS
  • إدارة الحالات: OK, ALARM, INSUFFICIENT_DATA
  • تنفيذ Actions معينة (زي إيقاف السيرفرات أو توسيع الخدمات تلقائيًا)
  • حفظ البيانات في قاعدة بيانات (زي DynamoDB)
  • دعم التوسّع الأفقي Horizontal Scaling وسرعة الاستجابة Latency العالية

Design Patterns

النمطالغرض
Observerبيخلّي النظام يبلّغ كذا مشترك في نفس الوقت لما الإنذار يشتغل
Builderلتكوين إعدادات إنذار معقّدة بطريقة واضحة وسهلة القراءة
Stateلإدارة حالات الإنذار (OK، ALARM، INSUFFICIENT_DATA)
Strategyلتقييم شروط الإنذار باستخدام خوارزميات مختلفة
Singletonلإدارة المكوّنات المشتركة زي مدير الإشعارات ومدير الحالات
Repository (مستقبلاً)عشان يبقى فيه طبقة فصل أوضح للوصول للبيانات وتسهل عملية الاختبار

Algorithms

الـ Threshold Evaluation
النظام بيقارن القيم اللي بيراقبها (metrics) مع حدود معينة (thresholds) عشان يحدد الحالة الحالية.
بيدعم معاملات منطقية زي (AND / OR)، وكمان عنده آليات لتفادي التغييرات السريعة (debouncing)، والتعامل مع البيانات الناقصة، وفترات التقييم، وفترات التهدئة (cooldowns).


State Machine

الإنذار بيتنقل بين 3 حالات: OK، ALARM، وINSUFFICIENT_DATA.
لو البيانات ناقصة لفترات معينة (N فترات)، الحالة بتتحول إلى INSUFFICIENT_DATA.
وفيه إعدادات (policies) بتتحكم هل البيانات الناقصة تعتبر حاجة "كويسة" ولا "وحشة".


Data Model – DynamoDB Sketch

  • الـ Alarms الـ Keys → (PK: alarm_id, SK: metadata)
    بيشمل الـ: name، owner، schedule، evaluation config، enabled
  • الـ Subscriptions الـ Keys → (PK: alarm_id, SK: subscriber_id)
    بيشمل: نوع الإشعار (email / sms / push / SNS)، (endpoint)، filters
  • الـ Evaluations الـ Keys → (PK: alarm_id, SK: ts_bucket)
    بيشمل: last state، reason، (datapoints)

Notification Mechanism

نمط الـ Observer : هو اللي بيقوم بنشر الإشعارات لكل الـ Channels زي (Email، SMS، Push، AWS SNS).

فيه نظام إعادة المحاولة: (retries) بخوارزمية exponential backoff (يعني بيزود الوقت بين المحاولات بشكل تدريجي).

والنظام كمان idempotent — يعني نفس الإشعار مش بيتبعت مرتين بفضل الـ (de-duplication keys).


C++ Proof of Concept

#include <iostream>
#include <string>
#include <vector>
#include <functional>
#include <cmath>

// ================================
// Interfaces: Subscriber and concrete channels
// ================================
class Subscriber {
public:
    virtual void notify(const std::string& msg) = 0;
    virtual ~Subscriber() = default;
};

class EmailSubscriber : public Subscriber {
public:
    void notify(const std::string& msg) override {
        std::cout << "[EMAIL] " << msg << std::endl;
    }
};

class SMSSubscriber : public Subscriber {
public:
    void notify(const std::string& msg) override {
        std::cout << "[SMS] " << msg << std::endl;
    }
};

// ================================
// Alarm state enum
// ================================
enum class AlarmState { OK, ALARM, INSUFFICIENT_DATA };

// ================================
// MetricAlarm with simple threshold logic
// ================================
class MetricAlarm {
    std::string name;
    double threshold;
    std::function<bool(double, double)> cmp;
    std::vector<Subscriber*> subs;
    AlarmState state = AlarmState::INSUFFICIENT_DATA;

public:
    MetricAlarm(std::string n, double th, std::function<bool(double, double)> f)
        : name(std::move(n)), threshold(th), cmp(std::move(f)) {}

    void subscribe(Subscriber* s) { subs.push_back(s); }

    void evaluate(double value) {
        AlarmState prev = state;

        if (std::isnan(value)) {
            state = AlarmState::INSUFFICIENT_DATA;
        } else {
            state = cmp(value, threshold) ? AlarmState::ALARM : AlarmState::OK;
        }

        if (state != prev) {
            for (auto* s : subs) {
                s->notify(name + " => state changed");
            }
        }
    }
};

// ================================
// Builder for readable configuration
// ================================
class AlarmBuilder {
    std::string name;
    double threshold = 0;
    std::function<bool(double, double)> cmp;
    std::vector<Subscriber*> subs;

public:
    AlarmBuilder& withName(std::string n) {
        name = std::move(n);
        return *this;
    }

    AlarmBuilder& greaterThan(double th) {
        threshold = th;
        cmp = std::greater<double>();
        return *this;
    }

    AlarmBuilder& lessThan(double th) {
        threshold = th;
        cmp = std::less<double>();
        return *this;
    }

    AlarmBuilder& addSubscriber(Subscriber* s) {
        subs.push_back(s);
        return *this;
    }

    MetricAlarm build() {
        MetricAlarm a(name, threshold, cmp);
        for (auto* s : subs) a.subscribe(s);
        return a;
    }
};

// ================================
// Demo
// ================================
int main() {
    EmailSubscriber email;
    SMSSubscriber sms;

    auto cpu = AlarmBuilder()
        .withName("CPU > 80%")
        .greaterThan(80)
        .addSubscriber(&email)
        .addSubscriber(&sms)
        .build();

    cpu.evaluate(50); // OK
    cpu.evaluate(85); // ALARM -> notify
    cpu.evaluate(70); // OK -> notify

    return 0;
}

Future Enhancements

  • إضافة Strategy Pattern: عشان نقدر نقيّم الشروط بطريقة مرنة، زي مثلًا قياس معدل التغيّر أو اكتشاف الحالات الشاذة (anomaly detection).
  • إضافة Repository Pattern: عشان نخلي الوصول للبيانات أفضل وأسهل في الاختبار.
  • دعم الإنذارات المركّبة: (Composite Alarms) والعلاقات بينها، زي مثلًا إنذار بيعتمد على CPU وMemory مع بعض، أو إنذار بيتمثل في علاقة (Parent/Child).
  • دعم الـ (Multi-Threading): عشان نستغل موارد النظام بشكل أفضل ونقدر ننشئ إنذارات في نفس الوقت بالتوازي.