Mô hình dữ liệu
1. Redis (hoặc Valkey)
Tiền tố ah:. Mọi khóa có TTL trừ khi ghi khác. Redis là trạng thái nóng, mất Redis không được làm mất dữ liệu số liệu, chỉ làm suy giảm tạm thời (xem 08-scaling-ha).
| Khóa | Kiểu | Nội dung | TTL |
|---|---|---|---|
ah:reg:tok:{sha256(token)} | hash | agent_id, company_id, server_id, state, rate_class (registry) | 1 giờ, làm mới khi dùng |
ah:reg:neg:{sha256(token)} | string | Cache âm token không tồn tại | 60 giây |
ah:reg:agent:{agent_id} | hash | Đảo ngược: token_hashes (hiện tại và cũ), server_id, company_id | 1 giờ |
ah:seen | zset | member agent_id, score last_seen_ms | không, dọn theo agent thu hồi |
ah:agent:{agent_id}:st | hash | state (up/down), since_ms, version, proto | không |
ah:rl:{agent_id}:{class} | string | Bộ đếm token bucket | 10 phút |
ah:series:{agent_id} | hyperloglog | Ước lượng số series hoạt động để áp max_series | 1 giờ |
ah:rules:{collector_id}:rules | string (JSON) | Snapshot bộ luật thô đang chạy và con trỏ since (COL-8). Nạp lại khi khởi động để chạy được lúc Access Hub sập | không |
ah:rules:{collector_id}:state | string (JSON) | Snapshot trạng thái đánh giá (pha, mốc for, đỉnh, seq) của mọi cặp luật-máy-series, ghi mỗi 30 giây và khi tắt êm (COL-8) | không |
ah:rules:{collector_id}:silences | string (JSON) | Snapshot bộ silence thô và con trỏ since (COL-9) | không |
ah:rules:{collector_id}:groups | string (JSON) | Snapshot các nhóm group_down đang mở (COL-9) | không |
ah:silences:reload | string (số) | Bộ đếm tín hiệu nạp lại silence, cùng cơ chế ah:rules:reload (COL-9) | không |
ah:rules:reload | string (số) | Bộ đếm tín hiệu nạp lại luật: admin tách tiến trình tăng, worker thăm dò mỗi 2 giây | không |
ah:collector:{id} | hash | Heartbeat của node collector, dùng để chia shard | 30 giây |
ob:q, ob:e, ob:dead | zset, hash, hash | Outbox sự kiện gửi tới Access Hub dùng chung trong Redis: hàng đợi theo thời gian, nội dung sự kiện, sự kiện chết (ADR 0012). Khóa outbox:sender là lease của bộ gửi | theo outbox |
Ghi chú triển khai (COL-3): registry Redis không đặt TTL cho reg:tok và reg:agent (đồng bộ từ Access Hub qua agents?since= là nguồn xóa, mất mục do TTL sẽ buộc gọi lại hub với mỗi agent), và thêm ba tập chỉ mục reg:srv:{server_id}, reg:co:{company_id}, reg:all phục vụ đếm, thu hồi theo server và purge theo công ty. Client Redis là internal/redisx tự viết (RESP2, không thêm thư viện).
Registry là bản sao đọc của Access Hub. Khi Redis mất hoặc miss, ingest hỏi Access Hub (GET /api/v1/monitoring/collector/agents?token_hash=) tối đa một lần cho mỗi token trong 60 giây, có giới hạn tốc độ toàn cục để tránh bão truy vấn.
2. Bus
Interface Go:
type Bus interface {
Publish(ctx context.Context, shard int, batch *SampleBatch) error
Subscribe(ctx context.Context, shards []int, h Handler) error
}| Triển khai | Giai đoạn | Đặc điểm |
|---|---|---|
inproc (channel có giới hạn) | 1 | Một node, không bền vững. Đầy thì ingest trả 503 kèm Retry-After |
redis (Redis Streams) | 1 đến 2 | Một stream mỗi shard (bus:s:<shard>), consumer group workers, ít nhất một lần, tiến trình chết được nhận lại bằng XAUTOCLAIM sau bus.claim_idle. Cho phép tách ingest và worker thành các tiến trình riêng (ADR 0012) |
jetstream | chưa có | Dự kiến thay Redis khi cần quy mô lớn hơn, giữ interface Bus. Chưa cài đặt |
Số shard logic cố định 64 (hash(server_id) mod 64). Worker sở hữu tập shard, tất cả mẫu của một máy chủ luôn tới cùng một worker, nên trạng thái đánh giá nằm trong bộ nhớ mà không cần khóa phân tán. Khi chạy nhiều worker dùng chung một Redis, mỗi worker phải sở hữu một tập shard riêng: worker.shard_count và worker.shard_index (env AHC_WORKER_SHARD_COUNT, AHC_WORKER_SHARD_INDEX) chia shard theo s mod shard_count == shard_index, hoặc liệt kê worker.shards tường minh (không dùng cùng lúc). Không cấu hình gì thì worker đọc cả 64 shard, chỉ an toàn khi có một worker duy nhất, và collector ghi cảnh báo khi bật động cơ luật. Phân chia là tĩnh: worker chết thì các shard của nó chờ trong Redis đến khi worker đó chạy lại, không có failover tự động (đã chấp nhận, xem docs/08).
3. Chuỗi thời gian (VictoriaMetrics)
Quy tắc đặt tên
- Tên chỉ số ghi vào TSDB có tiền tố
ah_: agent gửicpu_usage_percent, TSDB lưuah_cpu_usage_percent. - Nhãn hệ thống do collector gắn:
company_id,server_id. Không dùngagent_idvàhostnamelàm nhãn vì đổi khi enroll lại hoặc đổi tên, gây bùng nổ cardinality. - Nhãn do agent gửi chỉ thuộc danh sách trắng theo chỉ số (ví dụ
mount,fstypecho đĩa;ifacecho mạng;namecho dịch vụ;port,protocho port). Nhãn lạ bị bỏ.
Danh mục chỉ số chuẩn và danh sách nhãn cho phép nằm trong bản máy đọc được internal/catalog/metrics.yaml của Collector, đồng bộ với danh mục mà Agent triển khai theo hợp đồng docs/03-protocol.md.
Nhóm chỉ số phục vụ biểu đồ máy chủ (thêm cùng Agent, đều là gauge, không có nhãn trừ khi ghi khác):
| Chỉ số | Đơn vị | Nhãn | Ý nghĩa |
|---|---|---|---|
mem_free_bytes, mem_cached_bytes, mem_buffers_bytes, mem_slab_bytes, mem_page_tables_bytes | byte | không | Phân rã bộ nhớ từ /proc/meminfo (MemFree, Cached, Buffers, Slab, PageTables) |
swap_cached_bytes | byte | không | SwapCached |
net_rx_packets_per_sec, net_tx_packets_per_sec | gói/giây | iface | Tốc độ gói nhận và gửi theo giao diện mạng |
psi_cpu_some_percent, psi_memory_some_percent, psi_io_some_percent | phần trăm | không | Áp lực tài nguyên (PSI, some avg10) của CPU, bộ nhớ, I/O |
Collector chỉ kiểm tên và nhãn theo catalog. Khoảng giá trị (phần trăm 0 đến 100, byte và tốc độ không âm) do Agent bảo đảm khi đo. Luật rollup 5 phút và 1 giờ cho chỉ số mới được sinh từ catalog bằng collector -rollup-rules (bản dev: deploy/dev/gen-vmalert-rules.sh), không cần sửa tay. Mỗi giao diện mạng thêm 2 series (gói nhận và gửi) vào hạn mức max_series.
Kiểm soát cardinality
- Agent lọc mount và interface ngay từ nguồn (bỏ tmpfs, overlay, docker, loopback, veth).
- Collector áp
max_seriesmỗi agent (mặc định 500) và bỏ series mới vượt mức. - Cảnh báo nội bộ khi số series tổng tăng đột biến (xem 10-observability).
- Giá trị nhãn bị cắt 128 byte. Không cho nhãn có giá trị tự do do người dùng nhập ngoài
namecủa dịch vụ đã khai báo.
Ghi
Collector ghi bằng API import dạng JSON line của VictoriaMetrics (POST /api/v1/import, mỗi dòng một series với values và timestamps, trả 204), thay cho remote-write để tránh phụ thuộc thư viện nén snappy và protobuf của Prometheus (lệch so với bản thiết kế đầu, không đổi ngữ nghĩa). Nhãn company_id, server_id luôn được đặt sau cùng và thắng mọi nhãn do agent gửi. Worker gom lô theo max_batch=5000 mẫu hoặc 500 ms (cấu hình tsdb.max_batch_samples, tsdb.max_batch_wait). Lỗi tạm thời (5xx, 408, 429, timeout, lỗi mạng): thử lại có backoff từ 200 ms đến tsdb.retry_max, giữ lô, bộ đệm quá 4 lần cỡ lô thì worker dừng nhận từ bus để hàng đợi bus đầy và ingest trả 503. Lỗi vĩnh viễn (4xx khác): bỏ lô, tăng ahc_tsdb_batches_dropped_total, ghi log có mẫu. Khi tắt, worker rút cạn bus rồi flush lần cuối với thời hạn worker.shutdown_flush. Khi tsdb.url trống, collector dùng kho trong bộ nhớ (chỉ dev và kiểm thử).
Bus in-process (bus.queue_per_shard, mặc định 256 lô mỗi shard) chỉ nối ingest và worker trong cùng một tiến trình. Tách vai trò ingest và worker sang tiến trình khác dùng bus.backend: redis (Redis Streams, ADR 0012); cấu hình tách vai trò với inproc bị từ chối khi nạp. JetStream chưa làm, chỉ cân nhắc khi Redis không đủ.
Retention và rollup
Bản OSS của VictoriaMetrics áp một retention duy nhất cho mỗi instance và không có downsampling (đây là tính năng bản trả phí, cần kiểm tra lại khi triển khai). Vì vậy dùng hai instance:
| Instance | Nội dung | Retention mặc định | Nguồn |
|---|---|---|---|
vm-raw | Mẫu thô 30 giây | 30 ngày | Worker ghi trực tiếp |
vm-long | Rollup 5 phút và 1 giờ, dạng avg, min, max, last | 400 ngày | vmalert chạy recording rules từ vm-raw rồi remote-write sang vm-long |
Tên rollup: ah_cpu_usage_percent:5m_avg, :5m_min, :5m_max, :5m_last, :1h_avg, ... Luật sinh bằng collector -rollup-rules từ catalog nhúng trong binary (cùng mã mà API series dùng để đọc rollup, nên tên không thể lệch), 8 luật cho mỗi chỉ số: nhóm rollup_5m chạy mỗi 5 phút, rollup_1h mỗi 1 giờ, biểu thức <agg>_over_time(ah_<metric>[5m|1h]). vmalert căn thời điểm đánh giá theo chu kỳ nhóm và mặc định lùi 30 giây (-rule.evalDelay) để bù -search.latencyOffset của vm-raw, nên mẫu mới nhất không bị bỏ sót. Thêm chỉ số vào catalog thì sinh lại luật và nạp lại vmalert (xem 14 mục 5).
API truy vấn tự chọn nguồn (COL-10):
| Điều kiện | Nguồn | Biểu thức |
|---|---|---|
Không có vm-long, hoặc from cách hiện tại không quá 7 ngày | vm-raw | <agg>_over_time(ah_m{company_id,server_id}[step]) |
from xa hơn 7 ngày, step dưới 1 giờ | vm-long, ô 5 phút | <agg>_over_time(ah_m:5m_<agg>{...}[step]), step làm tròn lên bội 5 phút |
from xa hơn 7 ngày, step từ 1 giờ | vm-long, ô 1 giờ | <agg>_over_time(ah_m:1h_<agg>{...}[step]), step làm tròn lên bội 1 giờ |
Gộp lại các ô bằng cùng hàm là đúng với min, max, last; với avg là trung bình các ô bằng trọng số nhau (ô thiếu mẫu có trọng số như ô đủ, chấp nhận). Rollup trống trong khi khoảng còn chạm retention thô thì collector đọc lại vm-raw (bảo vệ khi vm-long mới thêm hoặc vmalert dừng). Phần cuối của một khoảng dài có thể thiếu tối đa một ô (5 phút hoặc 1 giờ) vì rollup chưa được ghi.
Retention vật lý toàn cụm là trần: vm-raw 30 ngày, vm-long 400 ngày. Retention theo gói (Q17, ADR 0016) áp bên dưới trần đó:
- Nhãn ngày. Mỗi mẫu được ghi kèm
ah_day=YYYYMMDD(ngày UTC của mẫu, collector tự đặt, nhãn cùng tên từ agent bị bỏ), nên mỗi ngày của một series là một series riêng; rollup của vmalert giữ nguyên nhãn. Mọi truy vấn gộp nhãn này (without(ah_day),avgbằng tổng chia số đếm,lastghép theo thời điểm mẫu cuối), nhãn không bao giờ ra khỏi collector. Chi phí đo được: chỉ mục cộng khoảng 40 phần trăm, ghi chậm khoảng 15 phần trăm, truy vấn gần như không đổi. - Ân hạn là thời gian để khách tự export dữ liệu của mình qua Access Hub (đọc API
series). Hết ân hạn dữ liệu bị xóa vật lý, không có xóa mềm, không có khôi phục từ nền tảng. - Đọc bị cắt theo giới hạn đọc của công ty (retention cũ khi còn ân hạn 30 ngày sau lần giảm, retention hiện tại sau đó), ghi không bao giờ vào vùng cũ hơn retention hiện tại (vùng khóa chỉ đọc).
- Xóa vật lý do janitor ở vai trò admin chạy mỗi giờ: hỏi Access Hub chính sách mới nhất, xóa cả ngày nằm ngoài cửa sổ bằng
delete_series{company_id, ah_day}ở cả hai kho, rồiforce_mergephân vùng tháng bị ảnh hưởng (tối đa 1 lần mỗi ngày) để dữ liệu rời đĩa. Lưới an toàn ở03-protocol.mdmục 5.1. - Dữ liệu trước khi có nhãn ngày: ở
vm-rawhết hạn tự nhiên trong 30 ngày; ởvm-longjanitor gắn nhãn ngày (xuất, nhập lại, xóa bản cũ) khi series cũ đã ngừng nhận mẫu 3 giờ.
Dung lượng ước tính
Xem bảng ở 08-scaling-ha. Cơ sở tính: khoảng 1 byte mỗi mẫu sau nén, 70 series mỗi máy ở cấu hình chuẩn, 2.880 mẫu mỗi series mỗi ngày.
4. Outbox sự kiện
Mọi sự kiện gửi Access Hub đi qua outbox bền vững: worker ghi sự kiện vào outbox trước, bộ gửi đọc từ outbox, gửi lô tối đa 200 sự kiện hoặc 1 giây, xác nhận thành công mới xóa.
- Khóa idempotency:
event_id(UUIDv7). - Thứ tự: mỗi
(server_id, rule_id)cóseqtăng dần. Access Hub bỏ sự kiện cóseqnhỏ hơn hoặc bằngseqđã xử lý. - Access Hub lỗi hoặc không liên lạc được: thử lại backoff tối đa 5 phút, outbox giữ tối thiểu 24 giờ (giới hạn bằng dung lượng, có cảnh báo khi vượt 50%).
- Access Hub trả 5xx liên tiếp 3 lần cho một lô nhiều sự kiện: bộ gửi giảm một nửa cỡ lô để một lô quá lớn hoặc có sự kiện độc không chặn cả hàng đợi, rồi tăng gấp đôi trở lại sau mỗi lần được nhận đến khi về
outbox.batch_max. - Sự kiện quá hạn 24 giờ mà chưa gửi được: chuyển sang kho
dead-lettervà tăng metric để người vận hành xử lý.
5. Bảng trong Access Hub (MySQL)
Không nằm trong collector, ghi ở đây để đối chiếu. Đầy đủ cột ở 06-access-hub-integration. Nguyên tắc: không có bảng nào chứa mẫu số liệu.