Access Hub Collector
Đồng bộ từ mã nguồn lúc 10:57, 03/10/2026
Skip to content

L3 - Nền tảng giám sát máy chủ - Collector - Alerting, Events, Outbox ​

Quy ước tên: L3 - <P&L> - <Hệ thống L2> - <Thành phần>. P&L: chưa chỉ định. Hệ thống L2: Collector. Thành phần: Detector (phát hiện agent im lặng), Events (ID và seq), Outbox (hàng đợi sự kiện bền) và Sender (gửi tới Access Hub). Mã: internal/alerting, internal/events, internal/outbox, nối dây trong internal/app/wiring.go.

Trạng thái
Bản nháp
Phiên bản
0.1, ngày 30/09/2026
Tài liệu cha
L2 SAD Collector
Cấp độ hệ thống
Cấp 3, đề xuất

Ghi chú: khi tài liệu và mã khác nhau, mã thắng

Đã làm nghĩa là có mã và kiểm thử trong repo. Chỉ thiết kế nghĩa là có trong tài liệu nhưng chưa có mã. Chỗ lệch được ghi ở mục nợ kỹ thuật.

Quy ước đánh dấu: "Đã làm" là có mã và kiểm thử. "Chỉ thiết kế" là chỉ có trong docs/. "Đề xuất" là ý của tác giả bản nháp. Mã thắng khi lệch tài liệu. Khóa Redis mang tiền tố redis.prefix (mặc định ah:).


0. Front matter ​

Thông tin tài liệu đầy đủ
Trạng tháiBẢN NHÁP, phiên bản 0.1, 2026-09-30. Người phê duyệt: chưa chỉ định
Thành phần (Component)Detector, Reporter (last_seen, heartbeat), Events (IDGen, SeqAllocator, Builder), Outbox (bộ nhớ, tệp, Redis, discard), Sender và Lease
Truy vết L2L2 SAD Collector: thành phần "Detector", "Outbox, Sender" (mục 2.3, 6.1), FR phát hiện agent down và gửi sự kiện (mục 3), NFR độ tin cậy sự kiện (mục 4), mục 7.1 (Event), rủi ro AR-001, AR-005, AR-006. Mục tiêu L1: G2 (qua L2)
Tài liệu chaL2 SAD Collector
Tài liệu anh emIngest API, Registry, Enroll, Presence, Bus, Worker, TSDB, Admin API
TierĐề xuất Cấp 3 (kế thừa L2, chưa xác nhận)
Phân loại dữ liệuTrung bình: định danh máy chủ và thời điểm mất tín hiệu. Không chứa bí mật
Blast radiusBỏ sót hoặc lặp sự kiện down làm Access Hub báo sai hoặc không báo. Mất sự kiện khi tiến trình chết đúng lúc (AR-001)
Sign-off gateChưa chỉ định. Không có ai đã sign-off

Truy vết mục L3 đến thành phần L2:

Mục L3Thành phần hoặc mục L2
1 Phạm vi2.3 Các thành phần chính
2 Yêu cầu3 Functional, 4 NFR
3 Kiến trúc6.1 Component table
4, 6 Domain, dữ liệu7.1 Data Model
5 Hợp đồng6.2 Integration (Sender đến Access Hub)
7 Thuật toán8.3 (luồng agent down)
8, 9 Lỗi, suy thoái6.3 Resilience, 12.2 Reliability
10 Đồng thời6.3 (lease người gửi)
11 Bảo mật9 Security
13 Telemetry13 Observability

1. Scope and Non-Goals ​

1.1 Vai trò ​

Collector là nơi duy nhất biết "agent đã im lặng bao lâu" (nhìn từ phía dữ liệu đẩy vào). Detector quét presence, quyết định agent down hoặc up, tạo sự kiện có ID duy nhất và số thứ tự seq, ghi vào Outbox bền, rồi Sender gửi tới Access Hub với ngữ nghĩa at-least-once (Access Hub khử trùng theo event_id). Access Hub là nơi tạo cảnh báo và thông báo; Collector không gửi thông báo.

1.2 Context diagram ​

mermaid
flowchart LR
    classDef bc fill:#1f3a5f,stroke:#4a90d9,color:#fff;
    classDef owned fill:#2d4a3e,stroke:#5fb37a,color:#fff;
    classDef entity fill:#3a3320,stroke:#d9b84a,color:#fff;
    classDef datastore fill:#3a2d4a,stroke:#a06fd9,color:#fff;

    PS["Presence"]:::bc
    DET["Detector"]:::owned
    EV["Events builder"]:::owned
    OB[("Outbox")]:::datastore
    SD["Sender"]:::owned
    HUB(["Access Hub (ext)"]):::entity

    DET -->|"quét agent quá hạn"| PS
    DET -->|"tạo sự kiện"| EV
    DET -->|"ghi bền"| OB
    SD -->|"đọc hàng đợi"| OB
    SD -->|"POST events"| HUB
    DET -->|"seen, heartbeat"| HUB
ChiềuBênNội dung
VàoPresenceExpired, DrainSeen, Counts, CompanyCounts
VàoIngest APIGọi OnAgentUp khi Touch khôi phục agent đang down
RaOutboxAppend (bền trước khi trả về)
RaAccess HubPOST /events, POST /agents/seen, POST /heartbeat
RaRedisKhóa ob:* và lease người gửi outbox:sender

1.3 Trong phạm vi ​

  • Phát hiện agent down và up theo ngưỡng.
  • Sinh event_id (UUIDv7) và seq đơn điệu theo (máy chủ, luật).
  • Outbox nhiều backend và quy tắc dead-letter.
  • Sender: lô, lùi mũ, tách lô bị từ chối, lease một người gửi.
  • Báo last_seen theo lô và heartbeat collector.

1.4 Ngoài phạm vi ​

Nội dungThuộc về
group_down, chống dao động (flapping), silence, bảo trì, cài đặt theo công tyCOL-9 và sau, chưa làm (chỉ thiết kế)
Chống nhiễu: silence, bảo trì, flapping, group_down (COL-9)Gói internal/suppress (silence, Suppressor), internal/rules/flap.go, internal/alerting/group*.go, xem docs/05-alerting.md mục 3 và 4
Luật ngưỡng: kéo luật, biên dịch, đánh giá, trạng thái, snapshotGói internal/rules (COL-8, xong), xem docs/05-alerting.md mục 2.1. Phát sự kiện qua alerting.Emitter.Emit vào Outbox của tài liệu này
Tạo cảnh báo, thông báo, tắt cảnh báoAccess Hub
Bản ghi presence (Touch, Ensure)L3 Registry, Enroll, Presence
Hợp đồng HTTP sự kiện phía Access HubHUB-* (chưa xây; dev dùng cmd/mockhub)

2. Requirements ​

2.1 Functional Requirements ​

#Trách nhiệmGiải thíchHiện thực ở
FR-ALR-01Ngưỡng downmax(3 x agent_interval, down_min)TestThresholdAndGrace
FR-ALR-02Ân hạn khởi độngKhông khai báo down trong 2 x agent_interval đầuTestNothingIsDeclaredDownDuringStartupGrace
FR-ALR-03Down đúng một lầnAgent quá ngưỡng chỉ phát một sự kiện downTestAgentDeclaredDownOnceAfterThreshold
FR-ALR-04Agent chưa từng báo cáoVẫn bị khai báo down sau ngưỡngTestAgentThatNeverReportedIsDeclaredDown
FR-ALR-05Up sau phục hồiCó dữ liệu hợp lệ thì phát agent.upTestAgentUpEventAfterRecovery
FR-ALR-06Sự kiện tới Access Hub đúng một lầnĐầu cuối Detector đến Sender đến hubTestEventsReachTheHubExactlyOnce
FR-ALR-07Thử lại khi Outbox lỗiSự kiện down lỗi ghi được giữ và ghi lạiTestDownEventsAreRetriedWhenTheOutboxFails
FR-ALR-08Báo last_seenChỉ lần thấy thật, mỗi lần một lượtTestReportSeenSendsOnlyRealContactsOnce
FR-ALR-09HeartbeatMang số đếm và tốc độTestHeartbeatCarriesCountsAndRate
FR-ALR-10Kiểm tra tham sốTùy chọn không hợp lệ bị từ chốiTestNewValidatesOptions
FR-EVT-01UUIDv7Định dạng đúng, dấu thời gian đúngTestUUIDv7FormatAndTimestamp
FR-EVT-02ID tăng chặt và duy nhấtKể cả khi đồng hồ lùiTestUUIDv7IsStrictlyIncreasingAndUnique, TestUUIDv7SurvivesClockGoingBackwards
FR-EVT-03seq tăng chặt theo (máy chủ, luật)max(now_ms, last+1)TestSeqIsStrictlyIncreasingPerServerAndRule
FR-EVT-04Quên seq một máy chủKhông ảnh hưởng máy chủ khácTestSeqForgetOnlyDropsThatServer
FR-EVT-05Nội dung sự kiệnLoại agent.down và agent.up, cùng luật agent_downTestAgentDownAndUpEvents
FR-OUT-01Hợp đồng Outbox chungMọi backend cùng hành viTestOutboxContract, TestRedisOutboxContract
FR-OUT-02Bỏ ID trùngAppend lặp không nhân đôiTestFileOutboxIgnoresDuplicateIDs, TestRedisOutboxIgnoresDuplicateIDs
FR-OUT-03Bền qua khởi động lại (tệp)Mở lại đọc được hàng đợiTestFileOutboxSurvivesReopen
FR-OUT-04Bỏ qua dòng hỏng (tệp)Không chặn cả hàng đợiTestFileOutboxSkipsCorruptLines
FR-OUT-05Nén tệpThu gọn nhật kýTestFileOutboxCompacts
FR-OUT-06Dùng chung giữa hai tiến trình (Redis)Cùng một hàng đợiTestRedisOutboxSharedByTwoProcesses
FR-OUT-07Lỗi Redis trả lỗiKhông nuốt lỗiTestRedisOutboxErrorsWhenRedisIsDown
FR-OUT-08ID không có thânBị loại khỏi hàng đợiTestRedisOutboxDropsQueuedIDWithoutBody
FR-SND-01Gửi và xác nhậnGửi lô, Ack phần được nhậnTestSenderDeliversAndAcks
FR-SND-02Giữ khi hub lỗiGửi lại đúng một lần khi hub hồi phụcTestSenderKeepsEventsWhileHubIsDownAndRedeliversOnce
FR-SND-03Dead-letter sự kiện bị từ chốiHub trả rejectedTestSenderDeadLettersRejectedEvents
FR-SND-04Tách lô bị từ chối vĩnh viễnGửi từng sự kiện mộtTestSenderSplitsPermanentlyRefusedBatch
FR-SND-05Hết hạn lưu giữSự kiện quá retention vào dead-letterTestSenderDeadLettersExpiredEntries
FR-SND-06Trần lô 200TestSenderBatchesAtMost200
FR-SND-07Hồi phục sau sự cốVòng Run tự khôi phụcTestSenderRunRecoversAfterOutage
FR-SND-08Một người gửi tại một thời điểmBỏ qua khi lease ở nơi khácTestSenderSkipsWhenLeaseIsHeldElsewhere, TestLeaseHasOneHolderAndFailsOver
FR-SND-09Kiểm tra tham sốTestNewSenderValidatesOptions

2.2 Non-Functional Requirements ​

Allocated:

NFR L2TargetCách đáp ứng ở L3
NFR-09 (không mất sự kiện khi hub lỗi)Hàng đợi bềnOutbox Redis hoặc tệp, giữ tối đa retention (24 giờ)
NFR-10 (không lặp gây hại)Khử trùngevent_id UUIDv7, hub khử trùng
NFR-13 (chịu lỗi node)Mất node không mất trạng tháiPresence và Outbox Redis, lease người gửi

Inherited: client Redis internal/redisx; hub client internal/hubclient.

Owned:

IDTargetParent L2-NFRSatisfied-by
NFR-ALR-01Phát hiện down không sớm hơn ngưỡngNFR-09Ngưỡng max(3 x interval, down_min)
NFR-ALR-02Quét mỗi 10 giâyNFR-09alerting.scan_interval
NFR-OUT-01Sự kiện chưa gửi lưu tối đa 24 giờ rồi dead-letterNFR-09outbox.retention
NFR-OUT-02Lô gửi tối đa 200NFR-09Giới hạn hub và outbox.batch_max (1 đến 200)
NFR-OUT-03Lùi 1 giây đến 5 phútNFR-09outbox.backoff_min, backoff_max

2.3 Acceptance Criteria ​

ACGiven / When / ThenTest ID
AC-ALR-01Given agent ngừng báo, When quá ngưỡng, Then đúng một agent.downTestAgentDeclaredDownOnceAfterThreshold
AC-ALR-02Given collector vừa khởi động, When trong ân hạn, Then không có downTestNothingIsDeclaredDownDuringStartupGrace
AC-ALR-03Given agent down rồi báo lại, When có lô hợp lệ, Then agent.upTestAgentUpEventAfterRecovery
AC-ALR-04Given hub lỗi, When sự kiện phát sinh, Then vào hàng đợi và giao đúng một lần khi hub hồi phụcTestSenderKeepsEventsWhileHubIsDownAndRedeliversOnce
AC-ALR-05Given hai collector, When cùng gửi, Then một bên giữ leaseTestSenderSkipsWhenLeaseIsHeldElsewhere
AC-ALR-06Given agent im lặng thật, When chạy đầu cuối với Access Hub giả, Then hub nhận down rồi upTestSilentAgentGoesDownThenUpAtAccessHub
AC-ALR-07Given Outbox lỗi lúc ghi down, When thử lại, Then vẫn gửi đượcTestDownEventsAreRetriedWhenTheOutboxFails

2.4 Quality Attribute Scenarios ​

NguồnKích thíchMôi trườngPhản hồiThước đo
AgentMất điệnBình thườngCollector khai báo down sau ngưỡng (90 giây với mặc định)Sự kiện tới hub trong chu kỳ quét kế tiếp cộng thời gian gửi
Access HubNgừng hoạt động nhiều giờBình thườngSự kiện xếp hàngKhông mất sớm hơn 24 giờ
Vận hànhKhởi động lại collectorNhiều agentÂn hạn tránh báo down hàng loạtKhông có down giả trong ân hạn
Vận hànhChết tiến trình ngay sau khi khai báo downBình thườngCó thể mất sự kiện (AR-001)Chưa có cơ chế bù, đề xuất dựng lại từ presence

3. Application Architecture ​

3.1 Kiến trúc thời chạy (C&C) ​

mermaid
flowchart LR
    classDef bc fill:#1f3a5f,stroke:#4a90d9,color:#fff;
    classDef owned fill:#2d4a3e,stroke:#5fb37a,color:#fff;
    classDef datastore fill:#3a2d4a,stroke:#a06fd9,color:#fff;

    SC["Scan loop"]:::owned
    RT["Retry list (bộ nhớ)"]:::owned
    EB["Events builder"]:::owned
    RP["Reporter"]:::owned
    OB[("Outbox")]:::datastore
    SND["Sender loop"]:::owned
    LS["Lease"]:::owned

    SC --> EB
    SC --> OB
    SC --> RT
    RP --> SC
    SND --> OB
    SND --> LS
Thành phầnTrách nhiệmVòng đời
Scan loopMỗi scan_interval hỏi Presence agent quá hạn, tạo agent.down, ghi OutboxTheo tiến trình
Retry listDanh sách bộ nhớ các sự kiện down ghi lỗi, thử lại ở vòng sau (mất khi chết tiến trình)Theo tiến trình
Events builderIDGen (UUIDv7) và SeqAllocatorTheo tiến trình
ReporterDrainSeen theo lô 500 mỗi seen_interval, Heartbeat mỗi heartbeat_intervalTheo tiến trình
Sender loopPeek, gửi, Ack, lùi mũ, hết hạnTheo tiến trình
Leaseredisx lease <prefix>outbox:sender, TTL 15 giây (chỉ với Outbox Redis)Theo tiến trình
Kết nốiKiểuChi tiết
Detector đến PresenceĐồng bộTimeout Redis 500 ms
Detector đến OutboxĐồng bộAppend chỉ trả về khi bền
Sender đến Access HubĐồng bộLô 1 đến 200, lùi mũ khi lỗi
Sender đến LeaseĐồng bộGia hạn mỗi vòng; chỉ gửi khi giữ lease

3.2 Module view ​

mermaid
flowchart TB
    classDef bc fill:#1f3a5f,stroke:#4a90d9,color:#fff;
    classDef owned fill:#2d4a3e,stroke:#5fb37a,color:#fff;

    APP["internal/app"]:::bc
    ALP["internal/alerting"]:::owned
    EVP["internal/events"]:::owned
    OBP["internal/outbox"]:::owned
    PRE["internal/presence"]:::bc
    HCP["internal/hubclient"]:::bc
    RX["internal/redisx"]:::bc

    APP --> ALP
    APP --> OBP
    ALP --> EVP
    ALP --> OBP
    ALP --> PRE
    ALP --> HCP
    EVP --> HCP
    OBP --> HCP
    OBP --> RX

Tệp: internal/alerting/alerting.go, internal/events/events.go, internal/outbox/outbox.go (interface, bộ nhớ), file.go, redis.go, sender.go.


4. Domain Model ​

mermaid
classDiagram
    class Event {
        <<hubclient.Event>>
        event_id
        type
        seq
        occurred_at
        company_id
        server_id
        agent_id
        rule_id
        severity
        series_key
        suppressed
        labels
    }
    class Outbox {
        <<interface>>
        Append
        Peek
        Ack
        Dead
        Expire
        Stats
    }
    class Entry {
        event
        enqueued_at
    }
    class DeadEntry {
        reason
        dead_at
    }
    class Sender {
        <<service>>
        Run
        FlushOnce
    }
    Outbox "1" --> "*" Entry
    Outbox "1" --> "*" DeadEntry
    Entry --> Event
    Sender --> Outbox

Bất biến:

  • event_id là UUIDv7 duy nhất, tăng chặt kể cả khi đồng hồ lùi.
  • seq tăng chặt theo (máy chủ, luật): max(now_ms, last+1).
  • Luật agent_down phát agent.down, agent.up: mức critical cho down, info cho up, series_key là agent.
  • Luật ngưỡng (COL-8) phát alert.opened, alert.resolved, alert.reminder qua Emitter.Emit, mang rule_id, rule_version, series_key, value, threshold, labels. seq tăng chặt theo (máy chủ, luật, series_key). alert.resolved có duration_seconds, peak_value, reason.
  • Sự kiện luật chưa vào được Outbox (đầy hoặc lỗi) được giữ lại và thử lại ở lần quét kế tiếp (ahc_rules_events_retry).
  • suppressed luôn false ở Collector hiện tại (silence, bảo trì, flapping thuộc COL-9, chưa làm).
  • Outbox chỉ xóa sự kiện khi Access Hub đã nhận (accepted) hoặc đã dead-letter.

5. API Contract ​

Không có API HTTP mở ra ngoài. Hợp đồng gồm lời gọi ra Access Hub và interface Go nội bộ.

5.1 Operations ​

InterfacePhương thứcHành vi
OutboxAppend(events, now)Chỉ trả về sau khi bền; ID trùng bị bỏ
OutboxPeek(max)Trả các mục cũ nhất, không xóa
OutboxAck(ids)Xóa sự kiện đã giao
OutboxDead(ids, reason, now)Chuyển vào dead-letter
OutboxExpire(cutoff, now)Dead-letter mọi mục xếp hàng trước cutoff
OutboxStats(now)Độ sâu, tuổi mục cũ nhất, số dead-letter
LockerAcquire(ctx)True khi tiến trình giữ lease

Hub API (gọi ra, xem L3 Registry mục 5.1): POST /events, POST /agents/seen, POST /heartbeat.

5.2 Request và Response schema ​

POST /events
  events![] (1..200): Event
Event:
  event_id!: UUIDv7
  type!: "agent.down" | "agent.up"
  seq!: int64
  occurred_at!: RFC3339
  company_id!, server_id!: string
  agent_id?, rule_id?, rule_version?, series_key?, severity?: string
  value?, threshold?: number
  suppressed!: bool
  suppress_reason?, group_key?: string
  labels?: map
    agent.down: last_seen_at
    agent.up: down_since, down_duration_s
Phản hồi: { accepted![]: event_id, rejected![]: { event_id, reason } }

POST /agents/seen và /heartbeat: nội dung chi tiết xem internal/hubclient/hubclient.go (heartbeat mang số đếm agent và tốc độ).

5.3 Error codes ​

Phản hồi của hubHành động của SenderMetric
200 với acceptedAckahc_outbox_events_sent_total
200 với rejectedDead("rejected: <lý do>")ahc_outbox_dead_letter_total{reason="rejected"}
Lỗi mạngGiữ, lùi mũahc_outbox_send_failures_total{kind="network"}
429, 5xx, 401, 403, 408Giữ, lùi mũkind="temporary"
4xx vĩnh viễn (lô nhiều sự kiện)Gửi từng sự kiện; bị từ chối thì Dead("refused: ...")kind="permanent", dead-letter reason="refused"
Sự kiện quá retentionExpire rồi dead-letterreason="expired"

5.4 Versioning ​

Hợp đồng /api/v1/monitoring/collector do Access Hub định nghĩa (HUB-*, chưa xây). Event có các trường cho luật ngưỡng (rule_id, rule_version, value, threshold, duration_seconds, peak_value, reason) do COL-8 điền. group_key vẫn chưa điền (COL-9).

5.5 Authz ​

Sender dùng Bearer hub.token. Không có phân quyền người dùng. Sự kiện luôn mang company_id từ bản ghi registry của agent, không từ dữ liệu agent gửi.


6. Physical Data Schema ​

6.1 Mapping ​

Redis Outbox (tiền tố ah:):

KhóaKiểuNội dung
ob:qzsetevent_id đang chờ gửi, điểm là thời điểm xếp hàng (ms)
ob:ehashevent_id đến Entry JSON (thân sự kiện và thời điểm xếp hàng)
ob:deadhashevent_id đến DeadEntry JSON (lý do và dead_at)
outbox:senderkhóa leaseChủ người gửi, TTL 15 giây, không có fencing token. Chỉ tạo khi Outbox là Redis (wiring.go)

Độ bền bằng độ bền của Redis: cần AOF (ít nhất appendfsync everysec) và replica (chú thích trong internal/outbox/redis.go).

Tệp Outbox (outbox.dir):

TệpNội dung
outbox.logNhật ký JSONL (thêm, ack, dead), nén định kỳ
deadletter.jsonlSự kiện dead-letter

Bộ nhớ: hàng đợi trong RAM, mất khi khởi động lại (chỉ dev và kiểm thử). Discard: dùng khi không có hub, bỏ mọi sự kiện.

Chọn backend auto: Redis nếu có redis.addr; nếu không thì tệp nếu có outbox.dir; nếu không thì bộ nhớ.

mermaid
flowchart LR
    classDef datastore fill:#3a2d4a,stroke:#a06fd9,color:#fff;

    Q[("ob:q")]:::datastore
    E[("ob:e")]:::datastore
    D[("ob:dead")]:::datastore
    Q -->|"id tra thân"| E
    E -.->|"Dead"| D

6.2 Phân loại và lưu giữ ​

Dữ liệuPhân loạiLưu giữ
Sự kiện xếp hàngTrung bìnhĐến khi hub nhận, tối đa retention (24 giờ)
Dead-letterTrung bìnhKhông có TTL trong mã; chưa có quy trình xem hoặc phát lại (đề xuất, OQ-ALR-3)
PresenceXem L3 Registry

7. Algorithms ​

7.1 Sequences (đường thành công) ​

mermaid
sequenceDiagram
    participant DT as Detector
    participant PS as Presence
    participant EB as Events builder
    participant OB as Outbox
    participant SD as Sender
    participant HB as Access Hub (ext)

    DT->>PS: Expired(ngưỡng, lô 500)
    PS-->>DT: agent quá hạn (SADD pres:down thắng)
    DT->>EB: AgentDown(agent, last_seen)
    EB-->>DT: event (UUIDv7, seq)
    DT->>OB: Append (bền)
    SD->>OB: Peek(tối đa 200)
    OB-->>SD: sự kiện
    SD->>HB: POST /events
    HB-->>SD: accepted
    SD->>OB: Ack

7.2 Sequences (đường lỗi) ​

mermaid
sequenceDiagram
    participant DT as Detector
    participant OB as Outbox
    participant SD as Sender
    participant HB as Access Hub (ext)

    DT->>OB: Append
    alt Append lỗi
        OB--)DT: lỗi
        DT->>DT: đưa vào retry list (bộ nhớ)
        DT->>OB: Append lại ở vòng quét sau
    end
    SD->>HB: POST /events
    alt lỗi tạm thời
        HB--)SD: lỗi
        SD->>SD: lùi 1 s nhân đôi đến 5 phút
    else 4xx vĩnh viễn
        HB-->>SD: 4xx
        SD->>HB: gửi lại từng sự kiện một
        SD->>OB: Dead cho sự kiện bị từ chối
    end

7.3 State machines ​

mermaid
stateDiagram-v2
    direction LR
    state "Hoạt động (up)" as UP
    state "Mất tín hiệu (down)" as DOWN
    [*] --> UP: Ensure (ân hạn)
    UP --> DOWN: quá ngưỡng, phát agent.down
    DOWN --> UP: có dữ liệu hợp lệ, phát agent.up
mermaid
stateDiagram-v2
    direction LR
    state "Xếp hàng (queued)" as Q
    state "Đã nhận (acked)" as A
    state "Dead-letter" as D
    [*] --> Q: Append
    Q --> A: hub accepted
    Q --> D: hub rejected hoặc refused
    Q --> D: quá retention
    A --> [*]
Chuyển trạng tháiSequence
Up đến Down7.1
Down đến UpTouch ở Ingest gọi OnAgentUp, Detector phát agent.up
Queued đến Acked7.1
Queued đến Dead7.2

7.4 Core algorithms ​

Vấn đềGiải phápTrade-off
Ngưỡng downmax(3 x agent_interval, down_min) (90 giây với mặc định)Phát hiện chậm hơn vài chục giây để chống báo nhầm
Báo nhầm lúc khởi độngÂn hạn 2 x agent_intervalSự cố thật trong ân hạn bị báo muộn
Hai tiến trình cùng thấy quá hạnSADD pres:down thắng thì sở hữu chuyển trạng tháiNếu chết sau SADD, sự kiện mất (AR-001)
Append lỗiRetry list bộ nhớMất khi chết tiến trình; đề xuất dựng lại từ trạng thái pres:down chưa có sự kiện
Sự kiện trùngevent_id UUIDv7Hub cần khử trùng theo event_id
Thứ tự sự kiệnseq = max(now_ms, last+1) theo (máy chủ, luật)Trạng thái seq nằm trong bộ nhớ: sau khởi động lại vẫn bắt đầu từ now_ms nên đơn điệu nhờ đồng hồ (giả định đồng hồ không lùi quá xa)
Một người gửiLease outbox:sender TTL 15 giây, không fencingHai bên có thể cùng gửi trong khoảng chuyển lease; hub khử trùng nên không hại
Lô bị từ chốiTách từng sự kiệnNhiều request hơn khi có sự kiện xấu
Hết hạnExpire mỗi vòng gửiMất sự kiện cũ hơn 24 giờ (vào dead-letter)
Lô last_seenDrainSeen ZPOPMIN theo chunk 500Mất mục nếu gửi hub lỗi sau khi rút

8. Error Handling ​

8.1 Bảng lỗi theo bước ​

Bước lỗiNguyên nhânCơ chế xử lýTrạng thái cuối
Quét PresenceRedis lỗiGhi log, thử lại vòng sauDown chậm
Append sự kiện downRedis hoặc đĩa lỗiRetry list bộ nhớ, thử lại vòng quét sauCó thể mất nếu chết tiến trình
Append sự kiện upRedis hoặc đĩa lỗiKhông thử lại: tăng ahc_events_lost_total{type="agent.up"} và ghi log mức errorMất sự kiện up
GửiMạng, 5xx, 429, 401, 403, 408Giữ, lùi mũSự kiện còn trong Outbox
Gửi4xx vĩnh viễnTách từng sự kiện, dead-letterreason="refused"
Gửirejected trong phản hồiDead-letterreason="rejected"
LeaseRedis lỗiKhông giữ lease, bỏ qua vòngKhông gửi
Báo last_seenHub lỗiGhi logMục đã rút có thể mất
TắtCòn sự kiệnGửi lần cuối tối đa 3 giâyPhần còn lại ở Outbox bền

ahc_events_lost_total chỉ tăng ở đường Emitter.AgentUp (sự kiện up không có retry list). Nhánh down dùng retry list nên chưa tính là mất cho đến khi tiến trình chết.

8.2 Fail-fast ​

  • outbox.batch_max ngoài 1 đến 200 bị từ chối (NewSender).
  • Tùy chọn Detector không hợp lệ bị từ chối (NewDetector, TestNewValidatesOptions).
  • Khi không có hub: Outbox Discard (sự kiện bị bỏ, vẫn chạy được ở dev).

8.3 Race conditions ​

Tình huốngXử lý
Hai collector quét cùng lúcSADD pres:down chọn một
Hai collector cùng gửiLease; hub khử trùng theo event_id
Chuyển leaseKhông fencing, có thể gửi lặp, an toàn nhờ khử trùng
Gửi lặp sau mất câu trả lờiHub khử trùng

9. Degradation ​

Nguyên tắc: sự kiện quan trọng hơn tốc độ. Giữ trong Outbox bền và thử lại, chấp nhận trễ.

9.1 Dependency matrix ​

Dependency lỗiHành viKiểu suy thoáiHệ quả
Access HubSự kiện xếp hàng đến 24 giờTrễCảnh báo trễ
RedisKhông quét, không gửi (lease), Outbox Redis không dùng đượcMùKhông phát hiện down (AR-005)
Đĩa (Outbox tệp)Append lỗiMấtRetry list bộ nhớ
Cả Access Hub và RedisKhông có sự kiệnMù

9.2 Backup và recovery ​

Outbox Redis: AOF everysec ở dev (mất tối đa khoảng 1 giây). Outbox tệp bền qua khởi động lại, không chia sẻ giữa node. Sự kiện dead-letter giữ để điều tra, chưa có công cụ phát lại. RTO và RPO chính thức: chưa định nghĩa (đề xuất, OQ-ALR-1).


10. Concurrency ​

10.1 Ranh giới giao dịch ​

Mỗi Append là bền độc lập. Quyết định down (SADD) và ghi sự kiện (Append) không cùng giao dịch: khoảng hở này là nguồn AR-001. Không có outbox giao dịch theo nghĩa kép với trạng thái presence.

10.2 Cơ chế ​

Cơ chếMô tảMối nguy
SADD pres:downMột chủ chuyển trạng tháiMất sự kiện nếu chết ngay sau
Lease outbox:senderMột người gửi, TTL 15 giâyKhông fencing, có thể lặp ngắn hạn
event_id UUIDv7Khử trùngPhụ thuộc hub
Khóa trong tiến trình (SeqAllocator, IDGen)An toàn luồngTrạng thái mất khi khởi động lại

11. Security ​

11.1 Ba lớp ​

LớpBiện pháp
Truyền thôngBearer hub.token tới Access Hub, TLS ở triển khai
Mãcompany_id lấy từ registry; sự kiện không chứa dữ liệu nhạy cảm
Dữ liệuOutbox không chứa bí mật; tệp Outbox nên có quyền 0600 (đề xuất rà soát, OQ-ALR-4)

11.2 Pipeline phân quyền năm bước ​

Thành phần nội bộ, không phân quyền người dùng. Điều kiện tiên quyết: company_id và server_id của sự kiện đến từ bản ghi registry đã xác thực.

11.3 Chỉ mục neo ​

Nguyên tắc L2Biện pháp nội bộ
NFR-12 cô lập tenantSự kiện mang company_id từ registry; hub kiểm lại
9.3 secretshub.token từ tệp hoặc biến môi trường, không vào log

12. Configuration ​

12.1 Tunables ​

Nguồn mặc định: Default() trong internal/config/config.go. Các khóa này chỉ YAML (trừ AHC_HUB_*, AHC_REDIS_*).

Tham sốDefaultÝ nghĩa và tác độngMục liên quan
alerting.agent_interval30 giâyChu kỳ đẩy kỳ vọng của agent7.4
alerting.down_min90 giâySàn ngưỡng down7.4
alerting.scan_interval10 giâyChu kỳ quét7.1
alerting.seen_interval1 phútChu kỳ báo last_seen. Phải nhỏ hơn một phần ba online_threshold_seconds của Access Hub (mặc định 180 giây), nếu không agent sẽ chập chờn online và offline7.4
alerting.heartbeat_interval1 phútChu kỳ heartbeat3.1
alerting.rules_sync_interval1 phútChu kỳ kéo luật từ Access Hub (COL-8). Ngoài ra kéo ngay khi POST /reload/rulesdocs/05 2.1
alerting.rules_state_interval30 giâyChu kỳ snapshot trạng thái đánh giá. Cũng ghi khi tắt êmdocs/05 2.1
alerting.no_data_after5 phútSeries im lặng quá mức này (máy vẫn gửi lô khác) thì áp no_data. Cũng là khoảng trống tối đa PENDING chịu đượcdocs/05 2.1
alerting.silences_sync_interval1 phútChu kỳ kéo GET /silences (COL-9). Ngoài ra kéo ngay khi POST /reload/silencesdocs/05 4.1
alerting.flap_transitions4Số lần chuyển FIRING <-> OK trong flap_window để vào flapping, 0 tắt (COL-9)docs/05 3.1
alerting.flap_window30 phútCửa sổ đếm chuyển trạng thái (COL-9)docs/05 3.1
alerting.flap_stable30 phútYên lặng bao lâu thì ra flapping (COL-9)docs/05 3.1
alerting.group_down.enabledtrueBật gom sự cố diện rộng. Nhiều worker thì chỉ một worker bật (COL-9)docs/05 3.2
alerting.group_down.min, .ratio, .window5, 0.30, 60 giâyMặc định của group_down. Access Hub GET /settings/{company_id} ghi đè theo công ty (COL-9)docs/05 3.2
alerting.group_down.companies.<company_id>rỗngGhi đè min, ratio, window theo công ty, thấp hơn cài đặt của Access Hub, cao hơn mặc định (COL-9)docs/05 3.2
alerting.rules_dirrỗngThư mục snapshot tệp khi không có Redis. Rỗng và không Redis thì snapshot chỉ ở bộ nhớ (có log cảnh báo, mất khi khởi động lại)docs/05 2.1
outbox.backendautoauto, memory, file, redis6.1
outbox.dirrỗngThư mục cho backend tệp6.1
outbox.batch_max200Sự kiện mỗi request (1 đến 200)7.4
outbox.flush_interval1 giâyChu kỳ thăm khi rảnh7.1
outbox.backoff_min1 giâyLùi tối thiểu7.2
outbox.backoff_max5 phútLùi tối đa7.2
outbox.retention24 giờQuá hạn thì dead-letter7.4

Hằng số cố định: TTL lease 15 giây, lô quét presence và last_seen 500, gửi lần cuối khi tắt 3 giây.

12.2 Feature flags ​

Không có công tắc cho luật: bộ luật rỗng thì không có gì để đánh giá. Khi không có hub.base_url, Outbox Discard.


13. Telemetry ​

13.1 Metrics ​

TênKiểuNhãnNgữ nghĩa
ahc_events_emitted_totalcountertypeSự kiện phát ra
ahc_events_lost_totalcountertypeSự kiện không ghi được vào Outbox (hiện chỉ đường agent.up)
ahc_agents_downgaugeAgent đang down
ahc_outbox_events_sent_totalcounterSự kiện giao thành công
ahc_outbox_send_failures_totalcounterkindLỗi gửi (network, temporary, permanent)
ahc_outbox_dead_letter_totalcounterreasonDead-letter (rejected, refused, expired)
ahc_outbox_depthgaugeĐộ sâu hàng đợi
ahc_outbox_oldest_age_secondsgaugeTuổi mục cũ nhất

Lệch tài liệu: docs/10 dùng tên khác (xem L2 TD-004). Mã thắng.

13.2 Log schema ​

JSON một dòng: time, level, msg, collector_id. Sự kiện đáng ghi: events dead-lettered after retention (mức error, kèm count), hub rejected event (kèm event_id, reason), khai báo down và up, lỗi gửi.

13.3 Cảnh báo đến runbook ​

Chưa có luật cảnh báo trong mã. Đề xuất: ahc_outbox_oldest_age_seconds cao (hub kẹt), ahc_outbox_dead_letter_total tăng bất kỳ, ahc_events_lost_total tăng bất kỳ, ahc_outbox_depth tăng liên tục. Runbook: docs/09 mục 6.

13.4 Probes ​

Readiness /readyz ("redis", "tsdb"); chưa có probe riêng cho Sender hoặc Detector (đề xuất, ví dụ thời điểm quét thành công cuối).

13.5 Trace propagation ​

event_id là khóa tương quan giữa log Collector và hub. Chưa có trace phân tán.


14. Test Plan ​

LoạiKiểm thửVị trí
DetectorTestThresholdAndGrace, TestAgentDeclaredDownOnceAfterThreshold, TestNothingIsDeclaredDownDuringStartupGrace, TestAgentThatNeverReportedIsDeclaredDown, TestAgentUpEventAfterRecovery, TestEventsReachTheHubExactlyOnce, TestDownEventsAreRetriedWhenTheOutboxFails, TestReportSeenSendsOnlyRealContactsOnce, TestHeartbeatCarriesCountsAndRate, TestNewValidatesOptionsinternal/alerting/alerting_test.go
EventsTestUUIDv7FormatAndTimestamp, TestUUIDv7IsStrictlyIncreasingAndUnique, TestUUIDv7SurvivesClockGoingBackwards, TestSeqIsStrictlyIncreasingPerServerAndRule, TestSeqForgetOnlyDropsThatServer, TestAgentDownAndUpEventsinternal/events/events_test.go
OutboxTestOutboxContract, TestFileOutboxIgnoresDuplicateIDs, TestFileOutboxSurvivesReopen, TestFileOutboxSkipsCorruptLines, TestFileOutboxCompacts, TestRedisOutboxContract, TestRedisOutboxIgnoresDuplicateIDs, TestRedisOutboxSharedByTwoProcesses, TestRedisOutboxDropsQueuedIDWithoutBody, TestRedisOutboxErrorsWhenRedisIsDowninternal/outbox
SenderTestSenderDeliversAndAcks, TestSenderKeepsEventsWhileHubIsDownAndRedeliversOnce, TestSenderDeadLettersRejectedEvents, TestSenderSplitsPermanentlyRefusedBatch, TestSenderDeadLettersExpiredEntries, TestSenderBatchesAtMost200, TestSenderRunRecoversAfterOutage, TestSenderSkipsWhenLeaseIsHeldElsewhere, TestNewSenderValidatesOptionsinternal/outbox/sender_test.go
LeaseTestLeaseHasOneHolderAndFailsOverinternal/redisx
Tích hợpTestSilentAgentGoesDownThenUpAtAccessHub, TestSplitProcessesShareStateThroughRedisinternal/app
Chưa cóChết tiến trình giữa SADD và Append (AR-001), kiểm thử dead-letter phát lại, kiểm thử với Access Hub thật

Bản nháp này không chạy kiểm thử (tránh Redis 6380 và dev stack).


15. Implementation Sequence ​

15.1 Milestone matrix ​

MilestoneNội dungPhụ thuộcĐóng góp nghiệm thu
COL-6Detector agent down, events, outbox, senderCOL-3AC-ALR-01..07
COL-ROutbox Redis, presence Redis, lease người gửiCOL-6AC-ALR-05
COL-8Luật ngưỡng, trạng thái, snapshot, sự kiện alert.*COL-6Kiểm thử gói internal/rules và internal/app
COL-9group_down, chống dao động, silence, bảo trìCOL-8Chưa có AC
COL-13 (chưa làm)Lease theo shard (liên quan bus)COL-R
Fencing lease (đề xuất, chưa có milestone)Fencing token cho người gửiCOL-RGiảm gửi lặp

Tên theo docs/11-roadmap.md; nếu khác, roadmap thắng.

15.2 Dependency flowchart ​

mermaid
flowchart LR
    C3["COL-3 registry"] --> C6["COL-6 detector, outbox"]
    C6 --> CR["COL-R Redis dùng chung"]
    C6 --> C8["COL-8 luật (xong)"]
    CR --> FN["fencing lease (đề xuất)"]

Appendix A. Open Questions ​

#Câu hỏiHành vi tạm thờiOwnerMã theo dõi
1RTO và RPO cho đường sự kiệnChưa định nghĩaChưa chỉ địnhOQ-ALR-1
2Bù sự kiện down bị mất khi chết tiến trình (AR-001): dựng lại từ pres:downKhông cóChưa chỉ địnhOQ-ALR-2
3Quy trình xem, phát lại và dọn dead-letterChưa cóChưa chỉ địnhOQ-ALR-3
4Quyền tệp Outbox và nơi đặt outbox.dir ở sản xuấtChưa xác minhChưa chỉ địnhOQ-ALR-4
5Sự kiện agent.up ghi Outbox lỗi bị mất, không có retry như agent.down: có cần đối xứngChỉ log và đếmChưa chỉ địnhOQ-ALR-5
6Có cần metric đếm mục trong retry list của DetectorKhông cóChưa chỉ địnhOQ-ALR-6
7Nguồn đồng bộ ConfigFor và cài đặt theo công tyLuật đã xong (COL-8), phần này chưa làmChưa chỉ địnhOQ-ALR-7
8Có cần fencing cho lease người gửiDựa vào khử trùng của hubChưa chỉ địnhOQ-ALR-8
9Hub thật có khử trùng theo event_id và theo seq không (HUB-*)Giả định cóChưa chỉ địnhOQ-ALR-9

Appendix B. ADR nội bộ ​

Mã ADRQuyết địnhTrạng tháiDriver
ADR 0008Sự kiện at-least-once với event_idChấp nhậnHub khử trùng
ADR 0012 (đề xuất)Redis dùng chung cho presence, outbox, leaseĐề xuấtCOL-R
ADR nội bộ (đề xuất)seq = max(now_ms, last+1) theo (máy chủ, luật)Đề xuất, là hành vi mãĐơn điệu không cần lưu
ADR nội bộ (đề xuất)Retry list bộ nhớ cho down ghi lỗiĐề xuất, là hành vi mã, có rủi ro AR-001Đơn giản
ADR nội bộ (đề xuất)Lease người gửi không fencingĐề xuất, là hành vi mãKhử trùng ở hub đủ an toàn

Appendix C. Section Profile ​

Phân mụcHồ sơTrạng thái điềnGiải trình
0 đến 4Bắt buộcĐã điền
5 API contractBắt buộcĐiền theo interface nội bộ và hub APIHợp đồng hub là HUB-* chưa xây
6 Physical dataBắt buộcĐã điềnKiểu khóa lấy từ chú thích redis.go
7.1 đến 7.4Bắt buộcĐã điền
8Bắt buộcĐã điền
9, 10, 11, 12, 13, 14, 15Bắt buộcĐã điềnKiểm thử chưa chạy trong bản nháp
Trang này có giúp được bạn không?
Sửa trang này

Nội dung đồng bộ từ kho mã access-hub-collector lúc 10:57, 03/10/2026. Khi tài liệu và mã khác nhau, mã thắng.