Skip to main content

SLA Policy Evaluator System Behavior (Implemented)

Dokumen ini menjelaskan behavior runtime SLA Policy Evaluator yang sudah berjalan saat ini.

Komponen utama:

  • apps/worker-workflow/src/sla-policy-evaluator/services/cdc-forwarder/sla-cdc-forwarder.consumer.ts
  • apps/worker-workflow/src/sla-policy-evaluator/services/transition/sla-transition-check.service.ts
  • apps/worker-workflow/src/sla-policy-evaluator/services/transition/sla-aging.service.ts
  • apps/worker-workflow/src/sla-policy-evaluator/services/schedule/sla-schedule.consumer.ts
  • apps/worker-workflow/src/sla-policy-evaluator/utils/sla-checker.utils.ts

1. Source of Truth dan Kontrak Event

Sumber event SLA:

  • Hanya dari CDC wxl_submission_log.
  • Hanya memproses operasi CDC insert (op='c') untuk menjaga append-only semantics.

Payload transition yang dikirim ke checker:

  • submission_id
  • action_id
  • from_status_id
  • to_status_id
  • actor_user_id
  • changed_at
  • source_table = wxl_submission_log
  • submission_log_id (untuk dedupe; alias kompatibilitas: source_log_id)
  • source_lsn (metadata CDC)
  • submission_hint (snapshot ringan untuk troubleshooting)

Catatan:

  • Forwarder publish ke topic workflow.sla.check.on_transition.* (instant) jika changed_at <= now.
  • Forwarder publish ke topic workflow.sla.schedule.* jika changed_at > now, lalu dijalankan scheduler.
  • Checker tetap membaca wxl_submission terbaru dengan pessimistic_write.
  • submission_hint tidak dijadikan state final, hanya hint observability.

2. Flow Transition (Start/Resolve SLA)

Saat event transition diterima:

  1. Checker buka transaksi.
  2. Checker lock row wxl_submission.
  3. Checker resolve policy dari tabel live wml_sla_policy (authoritative), lalu fallback ke workflow_snapshot.slaPolicies jika live policy kosong.
  4. Untuk tiap policy, checker jalankan guard idempotency.
  5. Checker tentukan apakah policy harus RESOLVE.
  6. Jika tidak resolve, checker evaluasi apakah policy harus START.

Behavior resolve:

  • Jika event.to_status_id sudah di status resolve_status_id atau lebih maju (wml_status.order >= resolve order), instance open yang is_resolveable=true di-mark RESOLVED.
  • Log wxl_sla_log bertipe RESOLVED ditambahkan.

Behavior start:

  • Scope STATUS: start ketika event.to_status_id == policy.start_status_id.
  • Scope ACTION: wajib event dari submission log dan wajib ada action_id.
  • condition_policy diperlakukan sebagai aturan kepatuhan.
  • Jika aturan tidak terpenuhi (condition_policy = false) saat trigger start:
    • SLA instance tetap dicatat untuk jejak audit,
    • instance langsung ditandai BREACHED (tanpa menunggu due),
    • outcome policy dijalankan.
  • Jika aturan terpenuhi, instance RUNNING dibuat dan due schedule dipublish.

3. Flow Aging Check (Breach vs Resolve)

Saat due event diproses:

  1. Checker lock wxl_sla_instance.
  2. Skip jika instance sudah RESOLVED atau BREACHED.
  3. Jika due_at belum lewat, instance di-reschedule.
  4. Jika due sudah lewat, checker tentukan apakah harus resolve atau breach.

Resolve guard saat due:

  • Guard A: jika submission.current_status_id saat ini sudah resolve_status_id atau lebih maju (status.order >= resolve order), instance RESOLVED.
  • Guard B: jika current status belum memenuhi Guard A, checker cari histori wxl_submission_log sejak instance.started_at (atau date_created).
  • Jika pernah ada transisi to_status_id yang order-nya >= resolve order, instance tetap RESOLVED (tidak BREACHED).

Catatan is_resolveable:

  • Nilai wxl_sla_instance.is_resolveable diset saat start instance.
  • Jika resolve_status_id policy kosong, instance dibuat dengan is_resolveable=false.
  • Instance is_resolveable=false tidak akan masuk jalur auto-resolve.

Jika kedua guard tidak terpenuhi:

  • Instance di-mark BREACHED.
  • Log wxl_sla_log bertipe BREACHED ditambahkan.
  • Outcome policy dijalankan.

4. Behavior Condition Policy

Condition policy diproses sebagai SQL terparameter dengan konteks JSON. Semantik: true = patuh, false = melanggar. Jika submission.entity dan submission.entity_id tersedia, checker menjalankan query ke tabel entity sumber:

  • FROM <submission.entity> entity_row
  • WHERE entity_row.id::text = <submission.entity_id>
  • Row entity diinjeksi ke context sebagai key entity (jsonb_set(..., '{entity}', to_jsonb(entity_row), true)).

Didukung:

  • Group nested: AND, OR, NOT.
  • Tipe rule: string, number, boolean, date, datetime.
  • Operator umum: eq, neq, in, not_in, contains, between, gt, gte, lt, lte, empty, not_empty.
  • Alias operator Directus juga diterima, contoh: _eq, _neq, _icontains, _null, _nnull, _in, _nin.

Catatan field:

  • Field tanpa dot (contoh name, flag_id) otomatis diprefix menjadi entity.<field>.
  • Field yang sudah berprefix (event.*, submission.*, policy.*, dst) tetap dipakai apa adanya.

Semantik:

  • Hasil akhir harus boolean (matched true/false).
  • Jika condition_policy kosong/null, default matched = true.
  • Modifier runtime juga didukung:
    • {"exclude_weekend": true} untuk skip Sabtu/Minggu pada kalkulasi due.
    • {"business_hours_only": true} sebagai fallback aktivasi business-hours jika is_use_business_hours belum diisi.

5. Native Business Hours / Weekend

Jika policy is_use_business_hours = true:

  • Due tidak dihitung 24/7, tetapi hanya di window UTC:
    • business_start_utc
    • business_end_utc
  • Hari kerja dapat diatur dengan working_days (contoh [1,2,3,4,5] untuk Senin-Jumat).
  • Default window jika null/invalid: 09:00:00 sampai 17:00:00 (UTC).
  • Jika is_exclude_weekend = true, Sabtu/Minggu tidak dihitung ke SLA timer.
  • condition_policy.exclude_weekend masih didukung sebagai fallback kompatibilitas.

Contoh konfigurasi:

  • Kolom policy:
    • is_use_business_hours = true
    • is_exclude_weekend = true
    • working_days = [1,2,3,4,5]
    • business_start_utc = '08:00:00'
    • business_end_utc = '17:00:00'
  • JSON condition:
    • { "exclude_weekend": true }

6. Idempotency dan Concurrency Guard

Guard yang aktif:

  • Dedupe event by submission_log_id per submission + policy.
  • No double start jika masih ada instance open pada policy yang sama.
  • Stale event guard: event lama diabaikan jika event.changed_at lebih tua dari update instance terakhir.
  • Seluruh operasi kritikal berjalan dalam transaksi DB.

7. Outcome Policy Behavior

Saat breach, outcome policy yang berjalan:

  • setStatusTo: mengubah wxl_sla_instance.sla_status (nilai valid: OK, BREACHED, PAUSED, dan alias BREACH -> BREACHED).
  • publishSubject: publish event SLA_BREACHED via JetStream, fallback ke core NATS jika publish gagal.

Catatan:

  • Outcome policy tidak mengubah wxl_submission.current_status_id.

8. Failure Handling

Forwarder/Scheduler/Checker behavior:

  • Payload invalid: di-drop + ack.
  • Error proses: nak agar bisa redelivery.
  • Scheduler melakukan delay dengan mekanisme nak(delay) sampai check_at.

9. Timeline Example (Kasus “Status Sudah Lewat”)

Contoh:

  • t0: log A -> B (policy start di B) => SLA RUNNING.
  • t+30m: log B -> C (policy resolve di C).
  • t+45m: log C -> D.
  • t+60m: aging dieksekusi.

Hasil:

  • Walau current status sudah D, checker tetap menemukan histori to_status_id = C setelah start.
  • Instance ditandai RESOLVED, bukan BREACHED.

10. Batasan Saat Ini

  • Dedupe kuat jika submission_log_id tersedia di event.
  • Behavior cross-workflow SLA agregasi kompleks belum jadi native flow khusus.