Rule Service - Event Sözleşmeleri
Giriş Eventleri
window.ready.v1
Window Service'ten gelen event
{
"event": "window.ready.v1",
"meta": {
"schema_version": 1,
"trace_id": "window-34cd...",
"producer_service": "window-service",
"produced_at": "2026-03-14T20:30:15.200Z",
"process_ms": 45
},
"context": {
"device_id": "400000011D081B70",
"stream_id": 9823412
},
"data": {
"window_epoch": 1647353400,
"window_start_ms": 1647353400000,
"window_end_ms": 1647353450000,
"window_duration_ms": 50000,
"measurements": {
"VRMS_R": 232.5,
"VRMS_S": 234.1,
"VRMS_T": 231.8,
"IRMS_R": 15.3,
"IRMS_S": 14.9,
"IRMS_T": 15.1,
"POWER_REAL": 10245,
"POWER_REACT": 1200
},
"synthesis": {
"i_imb": 2.1,
"v_imb": 1.3,
"pf": 0.994,
"p95_rtt_ms": 125
},
"status_register": {
"value": "0x0101",
"bits": {
"STATUS_PUMP": true,
"STATUS_FAULT_MP": false,
"STATUS_FAULT_TH": false,
"STATUS_P_HIGH": false
}
}
},
"error": null
}
Önemli Alanlar:
context.device_id: Hangi cihazdancontext.stream_id: Hangi stream'deki verilerdata.window_epoch: Penceren zamanıdata.measurements: Raw ölçüm değerleridata.synthesis: Hesaplanmış değerlerdata.status_register: Cihaz durumu bitleri
Çıkış Eventleri
rule.triggered.v1
Kural tetiklendiğinde (false→true geçişi veya multi_trigger=true)
{
"event": "rule.triggered.v1",
"meta": {
"schema_version": 1,
"trace_id": "rule-9f3f-8c2d...",
"producer_service": "rule-service",
"produced_at": "2026-03-14T20:30:20.125Z",
"process_ms": 15
},
"context": {
"device_id": "400000011D081B70",
"stream_id": 9823412
},
"data": {
"group_id": 12,
"rule_id": 114,
"event_type": "trigger",
"rule_hash": "a8be3f9c2e1d...",
"source_event": "window.ready.v1",
"trigger_reason": "VRMS_R > 250 AND duration >= 600s",
"severity": "high",
"trigger_count": 57,
"multi_trigger": false,
"state_changed": true,
"actions": [
{
"action_id": 21,
"action_type": "inbox_message",
"priority": 1
},
{
"action_id": 22,
"action_type": "push_notification",
"priority": 2
},
{
"action_id": 23,
"action_type": "notify_roles",
"priority": 3
}
],
"matched_rules": {
"114": true,
"115": true
},
"match_type": "all",
"duration_sec": 615
},
"error": null
}
Event Type: trigger — Tetiklenme olayı
group_id: Hangi kural grubu tetiklendirule_id: Tetiklenen kural (eğer varsa)trigger_reason: İnsan okunabilir koşul açıklamasıseverity: critical/high/medium/lowtrigger_count: Bu grubun kaç kere tetiklendiği (toplam)actions: Tetiklenen aksiyon listesimatched_rules: Grupta hangi kurallar true oldumatch_type: Kurallar AND/OR/NOT ile kombinlendi
Kullanıcı: Action Executor Service'in bu event'i dinlemesi expected.
rule.triggered.v1 (Reset Örneği)
Kural sıfırlandığında (true→false geçişi) ve notify_on_reset=true
{
"event": "rule.triggered.v1",
"meta": {
"schema_version": 1,
"trace_id": "rule-9f3f-8c2d...",
"producer_service": "rule-service",
"produced_at": "2026-03-14T20:45:30.250Z",
"process_ms": 12
},
"context": {
"device_id": "400000011D081B70",
"stream_id": 9823519
},
"data": {
"group_id": 12,
"rule_id": 114,
"event_type": "reset",
"rule_hash": "a8be3f9c2e1d...",
"source_event": "window.ready.v1",
"trigger_reason": "VRMS_R ≤ 250 AND duration reset",
"severity": "info",
"trigger_count": 57,
"multi_trigger": false,
"state_changed": true,
"actions": [
{
"action_id": 24,
"action_type": "push_notification",
"priority": 1
},
{
"action_id": 25,
"action_type": "notify_roles",
"priority": 2
}
],
"matched_rules": {
"114": false,
"115": false
},
"match_type": "all",
"previous_state": "triggered"
},
"error": null
}
Event Type: reset — Sıfırlama olayı
event_type= "reset"severity= "info" (sorun çözülmüş)actions: trigger_action_plan_id yerine reset_action_plan_id kullanılırprevious_state: "triggered" (ne durumdan geldiği)
rule.completed.v1
Rule değerlendirmesi tamamlandığında (tetik olsa da olmasa da)
{
"event": "rule.completed.v1",
"meta": {
"schema_version": 1,
"trace_id": "rule-9f3f-8c2d...",
"producer_service": "rule-service",
"produced_at": "2026-03-14T20:30:20.190Z",
"process_ms": 19
},
"context": {
"device_id": "400000011D081B70",
"stream_id": 9823412
},
"data": {
"result": "success",
"evaluation_status": "completed",
"groups_evaluated": 3,
"groups_triggered": 0,
"reason": "no_trigger",
"source_event": "window.ready.v1"
},
"error": null
}
rule.completed.v1 aşağıdaki tüm başarılı senaryolarda üretilmelidir:
- En az bir grup tetiklendi (
reason=triggered) - Hiçbir grup tetiklenmedi (
reason=no_trigger) - Cihaza atanmış aktif grup yok (
reason=no_rule_assigned)
Bu event, Observer için lifecycle bitiş sinyalidir.
rule.completed.v1 Enum Sozlugu
data.evaluation_status
| Deger | Anlami |
|---|---|
completed | Rule degerlendirmesi teknik olarak basariyla tamamlandi |
data.reason
| Deger | Anlami |
|---|---|
triggered | En az bir grup tetiklendi |
no_trigger | Gruplar degerlendirildi ama hicbiri tetiklenmedi |
no_rule_assigned | Cihaza atanmis aktif grup/kural bulunmadi |
Not: evaluation_status=completed oldugunda event teknik olarak basari kabul edilir; reason sadece is sonucunun kategorisini belirtir.
action.execute.requested.v1
Action Executor'a aksiyon çalıştırması için gönderilen event
{
"event": "action.execute.requested.v1",
"meta": {
"schema_version": 1,
"trace_id": "rule-9f3f-8c2d...",
"producer_service": "rule-service",
"produced_at": "2026-03-14T20:30:20.150Z",
"process_ms": 15
},
"context": {
"device_id": "400000011D081B70",
"stream_id": 9823412
},
"data": {
"group_id": 12,
"rule_id": 114,
"action_id": 21,
"action_type": "inbox_message",
"channel_type": "inbox",
"severity": "high",
"request_id": "action-9f3f-8c2d-uuid",
"payload": {
"title": "High Voltage Alert",
"message": "Voltage (VRMS_R) exceeded 250V for 10 minutes. Current: 320V",
"device_name": "Farm Pump #1",
"metric": "VRMS_R",
"threshold": 250,
"current_value": 320,
"unit": "V",
"timestamp": "2026-03-14T20:30:15Z"
},
"recipients": {
"owner": true,
"admin": true,
"technician": false
},
"publish": true
},
"error": null
}
Aksiyon Tipleri:
inbox_message— Inbox'a mesajpush_notification— Push notificationdevice_command— Cihaza komut göndernotify_roles— Belirtilen rollere bildir
publish Alanı:
true→ Mesaj push channel'da gönderilecekfalse→ Mesaj sadece inbox/audit, push yok
action.execute.requested.v1 (Push Örneği)
{
"event": "action.execute.requested.v1",
"meta": {
"schema_version": 1,
"trace_id": "rule-9f3f-8c2d...",
"producer_service": "rule-service",
"produced_at": "2026-03-14T20:30:20.160Z",
"process_ms": 15
},
"context": {
"device_id": "400000011D081B70",
"stream_id": 9823412
},
"data": {
"group_id": 12,
"rule_id": 114,
"action_id": 22,
"action_type": "push_notification",
"channel_type": "push",
"severity": "high",
"request_id": "action-9f3f-8c2d-uuid",
"payload": {
"template_id": 5,
"title": "⚠️ High Voltage",
"body": "VRMS_R: 320V (limit: 250V)",
"device_name": "Farm Pump #1",
"metric": "VRMS_R",
"threshold": 250,
"current_value": 320,
"delta": 70,
"unit": "V",
"timestamp": "2026-03-14T20:30:15Z"
},
"recipients": {
"owner": true,
"admin": true,
"technician": false
},
"publish": true
},
"error": null
}
action.execute.requested.v1 (Device Command Örneği)
{
"event": "action.execute.requested.v1",
"meta": {
"schema_version": 1,
"trace_id": "rule-9f3f-8c2d...",
"producer_service": "rule-service",
"produced_at": "2026-03-14T20:30:20.170Z",
"process_ms": 15
},
"context": {
"device_id": "400000011D081B70",
"stream_id": 9823412
},
"data": {
"group_id": 12,
"rule_id": 114,
"action_id": 26,
"action_type": "device_command",
"channel_type": "device",
"severity": "critical",
"request_id": "action-9f3f-8c2d-uuid",
"payload": {
"command_id": 7,
"command_type": "reset",
"command_name": "Emergency Reset",
"target_device_id": "400000011D081B70",
"reason": "High voltage protection",
"timeout_sec": 30
},
"publish": false
},
"error": null
}
Action Executor Tüketim Sözleşmesi
Bu bölüm, action.execute.requested.v1 event'ini tüketen Action Executor için bağlayıcı kuralları tanımlar.
Zorunlu Alanlar
Action Executor event'i işlerken aşağıdaki alanları zorunlu kabul etmelidir:
meta.schema_versionmeta.trace_idcontext.device_idcontext.stream_iddata.group_iddata.action_iddata.action_typedata.request_iddata.payload
Zorunlu alanlardan biri eksikse event başarısız sayılır ve rule.failed.v1 üretimi tetiklenir.
action_type ve Beklenen payload Şeması
inbox_message:- Beklenen alanlar:
title,message,timestamp
- Beklenen alanlar:
push_notification:- Beklenen alanlar:
title/bodyveyatemplate_id,timestamp
- Beklenen alanlar:
device_command:- Beklenen alanlar:
command_id,command_type,target_device_id
- Beklenen alanlar:
notify_roles:- Beklenen alanlar: en az bir hedef alıcı (
recipients.owner/admin/technician)
- Beklenen alanlar: en az bir hedef alıcı (
payload içeriği action_type ile uyumsuzsa event işlenmemelidir.
publish Davranışı
publish=true: Push kanalına yayın yapılabilir.publish=false: Push gönderimi yapılmaz; inbox/audit benzeri iç kanallar işlenmeye devam eder.
Idempotency ve Duplicate Koruması
Action Executor aşağıdaki anahtarı idempotency key olarak kullanmalıdır:
sha1(device_id | stream_id | group_id | action_id | request_id)
Aynı anahtar tekrar gelirse ikinci ve sonraki event'ler duplicate kabul edilip skip edilmelidir.
Retry Politikası
- Geçici hatalar (network, downstream timeout): exponential backoff ile retry.
- Kalıcı hatalar (şema uyumsuzluğu, zorunlu alan eksikliği): retry yapılmadan failed olarak işaretlenir.
- Önerilen retry sınırı: en fazla 3 deneme.
Gözlemlenebilirlik (Observability)
Action Executor her işleme için en az aşağıdaki alanları loglamalıdır:
trace_idrequest_iddevice_idaction_idaction_typeresult(success/failed/duplicate_skipped)latency_ms
Action Executor Response Eventleri
Action Executor, action.execute.requested.v1 event'ini işledikten sonra aşağıdaki ack eventlerinden birini üretmelidir.
action.executed.v1
Aksiyon başarıyla tamamlandığında
{
"event": "action.executed.v1",
"meta": {
"schema_version": 1,
"trace_id": "rule-9f3f-8c2d...",
"producer_service": "action-executor-service",
"produced_at": "2026-03-14T20:30:20.420Z",
"process_ms": 28
},
"context": {
"device_id": "400000011D081B70",
"stream_id": 9823412
},
"data": {
"request_id": "action-9f3f-8c2d-uuid",
"group_id": 12,
"rule_id": 114,
"action_id": 21,
"action_type": "inbox_message",
"result": "success",
"executed_at": "2026-03-14T20:30:20.415Z",
"delivery": {
"channel": "inbox",
"provider_message_id": "inbox-3b12a9",
"status": "sent"
}
},
"error": null
}
action.failed.v1
Aksiyon kalıcı veya geçici hata ile tamamlanamadığında
{
"event": "action.failed.v1",
"meta": {
"schema_version": 1,
"trace_id": "rule-9f3f-8c2d...",
"producer_service": "action-executor-service",
"produced_at": "2026-03-14T20:30:21.010Z",
"process_ms": 210
},
"context": {
"device_id": "400000011D081B70",
"stream_id": 9823412
},
"data": {
"request_id": "action-9f3f-8c2d-uuid",
"group_id": 12,
"rule_id": 114,
"action_id": 22,
"action_type": "push_notification",
"result": "failed",
"attempt": 3,
"max_attempt": 3,
"failed_at": "2026-03-14T20:30:21.002Z"
},
"error": {
"error_code": "PUSH_PROVIDER_TIMEOUT",
"error_message": "push provider timeout after 5s",
"retryable": true,
"failed_stage": "provider_call"
}
}
Correlation ve Tüketim Kuralı
- Rule Service,
request_idüzerinden request/response eşlemesi yapar. action.executed.v1geldiğinde ilgili aksiyon başarıyla tamamlandı olarak işaretlenir.action.failed.v1geldiğinderetryabledurumuna göre yeniden deneme veya kalıcı hata kaydı uygulanır.- Aynı
request_idiçin birden fazla ack event gelirse idempotency kuralı uygulanır; ilk terminal sonuç geçerli kabul edilir.
Hata Eventleri
rule.failed.v1
Kural değerlendirme sırasında hata olduğunda
{
"event": "rule.failed.v1",
"meta": {
"schema_version": 1,
"trace_id": "rule-9f3f-8c2d...",
"producer_service": "rule-service",
"produced_at": "2026-03-14T20:30:21.180Z",
"process_ms": 15
},
"context": {
"device_id": "400000011D081B70",
"stream_id": 9823412
},
"data": null,
"error": {
"failed_stage": "rule_evaluation",
"error_code": "RULE_CONDITION_INVALID",
"error_message": "unknown variable in rule: P95_X",
"rule_id": 114,
"group_id": 12,
"retryable": false,
"failed_at": "2026-03-14T20:30:21Z",
"stacktrace": "com.example.rule.RuleEvaluator.evaluate..."
}
}
Error Codes:
RULE_CONDITION_INVALID— Kural tanımı hatalıVARIABLE_NOT_FOUND— Ölçüm değişkeni bulunamadıCACHE_MISS_DB_UNAVAILABLE— Redis ve DB inaccessibleDEVICE_NOT_FOUND— Cihaz bulunamadıGROUP_NOT_FOUND— Kural grubu bulunamadıMALFORMED_EVENT— Event payload hatalı
retryable:
true→ Retry etmek mantıklı (e.g., temporary connection issue)false→ Permanent error (e.g., invalid rule definition)
Event Ordering Guarantees
Per-Device Ordering
Events bir cihaz için FIFO garantilidir.
Device E2E-001:
├─ window.ready.v1 (stream_id=9823412) → rule.triggered.v1 (group_id=12) → rule.completed.v1
├─ window.ready.v1 (stream_id=9823413) → rule.completed.v1 (reason=no_trigger)
├─ window.ready.v1 (stream_id=9823414) → rule.triggered.v1 (group_id=15)
│
Order: garantili
Cross-Device Ordering
Farklı cihazlar arasında ordering garantisi yoktur.
Device E2E-001: rule.triggered.v1@20:30:20Z
Device E2E-002: rule.triggered.v1@20:30:19Z
Order: Tanımsız (producer'ın paralel işleme yapması)
Idempotency
Rule Service tüm output events idempotent yapmalıdır.
Idempotency Key = sha1(device_id | stream_id | group_id | rule_id | event_type)
Örnek:
device_id = 400000011D081B70
stream_id = 9823412
group_id = 12
rule_id = 114
event_type = trigger
Key = sha1("400000011D081B70|9823412|12|114|trigger")
= "3f9c2e1d8a4b5c6d7e8f9g0h1i2j3k4l"
Aynı key ile duplicate event gelirse, Action Executor'da ilk event'i işlemesi expected, duplicate skip.
rule.completed.v1 için önerilen key:
sha1(device_id | stream_id | "rule.completed.v1")
Topic Naming
Events Topic Mapping:
rule.triggered.v1 → cinga.rule.triggered
rule.completed.v1 → cinga.rule.completed
rule.failed.v1 → cinga.rule.failed
action.execute.requested.v1 → cinga.action.requested
action.executed.v1 → cinga.action.executed
action.failed.v1 → cinga.action.failed
SLA (Service Level Agreement)
| Metrik | Hedef |
|---|---|
| Event Processing Latency (p95) | < 200ms |
| Event Publishing Latency (p95) | < 50ms |
| Event Schema Validation Rate | 100% |
| Duplicate Event Rate | < 0.01% |
| Lost Event Rate | 0% (Kafka replication) |
Backward Compatibility
Event schema versioning: schema_version field'i kullanılır.
Mevcut: schema_version = 1
Breaking Change Yapılırsa:
├─ schema_version = 2
├─ Consumers: v1 ve v2 handle etmeli
└─ Deprecated: eski alanlar -> null veya default value
Compat Strategy:
- Yeni alanlar: backward compat (consumer ignore edebilir)
- Removed alanlar: new schema version required