Vận hành
1. Deployment
| Thuộc tính | Giá trị |
|---|---|
| Image | registry/nx-seller-inventory:<tag> |
| Container Port | 3000 |
| External Port | 31050 |
| Snowflake ID | 5 |
| Replicas (mặc định) | 1 (dev) / 2+ (staging+) |
| Resources (req/lim) | 100m / 500m CPU, 256Mi / 1Gi memory |
| HPA target | CPU 70% (khi scale) |
| Migration mode | Job RUN_MODE=migrate trước khi rollout; on-boot cho dev |
| Live probe | GET /v1/api/inventory/healthz |
| Ready probe | GET /v1/api/inventory/readyz |
| Graceful shutdown | SIGTERM/SIGINT → đóng Kafka producer + consumer (isForce=false) |
Label routing Traefik
yaml
labels:
- "traefik.enable=true"
- "traefik.http.routers.inventory.rule=PathPrefix(`/v1/api/inventory`)"
- "traefik.http.services.inventory.loadbalancer.server.port=3000"Hạ tầng bắt buộc
| Phụ thuộc | Lý do |
|---|---|
| PostgreSQL | Datastore chính (schema inventory) |
| Kafka brokers | Bắt buộc — service từ chối khởi động nếu APP_ENV_KAFKA_BROKERS rỗng |
| Debezium | Bắt buộc cho topic CDC (MERCHANT, PRODUCT_VARIANT) |
| Redis | Tùy chọn — auth cache; service vẫn start được nếu thiếu |
| @nx/identity reachable | Verify JWKS trên mọi JWT |
2. Observability
| Tín hiệu | Nguồn | Tìm ở đâu |
|---|---|---|
| Logs | stdout (structured logger IGNIS, định dạng key: %s) | kubectl logs deploy/inventory / Loki |
| Health | GET /v1/api/inventory/healthz, GET /readyz | Gateway portal |
| OpenAPI live spec | GET /v1/api/inventory/doc/openapi.json | Trình duyệt gateway portal |
| Metrics | Traefik gateway :30800 (Prometheus scrape) | Grafana — dashboard gateway |
| Kafka lag | Kafka cluster broker / Burrow | TBD |
Trường log quan trọng
| Trường | Nguồn | Ghi chú |
|---|---|---|
requestId | header X-Request-Id | Truyền cross-service |
userId | subject của JWT | — |
merchantId | scope của request | — |
topic / partition / offset | Kafka consumer | Log mỗi message |
referenceType / referenceId | inventory tracking writes | Để tương quan audit trail |
Truy vấn log hữu ích
| Câu hỏi | Truy vấn |
|---|---|
| Lỗi trừ stock (oversell-blocked) | note=OVERSELL_BLOCKED |
| Re-deliveries | topic=payment.success AND idempotent skip |
| Lỗi emit PO | Kafka emit failed AND topic=purchase-order.received |
3. Security
| Concern | Biện pháp |
|---|---|
| AuthN | JWT (ES256, JWKS lấy từ identity lúc boot + on-demand) |
| AuthZ | Casbin qua PolicyDefinitionService; permission cache trong Redis (hoặc in-memory nếu Redis tắt) |
| Service-to-service | Strategy BASIC (commerce → inventory direct call trong TX dùng chung) |
| Secrets | K8s Secret mount thành env (APP_ENV_DB_URL, APP_ENV_KAFKA_SASL_PASSWORD, v.v.) — không bao giờ trong code |
| TLS | Terminate ở Nginx → Traefik → service plaintext (intra-cluster) |
| Rate limit | Middleware Traefik (mặc định 100 rps/IP) |
| Network policy | Cilium — chỉ cho phép gateway + Kafka + Postgres + Redis + identity (JWKS) |
| Soft-delete | deletedAt — không hard-delete mặc định; InventoryTracking bất biến |
| Idempotency | Mọi Kafka handler đều idempotent — an toàn dưới delivery at-least-once |
4. Runbook
4.1 Lớp Alert
| Alert | Trigger | Kiểm tra | Sửa | Escalate |
|---|---|---|---|---|
InventoryHighErrorRate | 5xx >5% trong 5m | kubectl logs deploy/inventory | grep level=error | Xác định endpoint lỗi; restart nếu kẹt | on-call backend |
InventoryKafkaLag | Lag consumer group >10k trên topic bất kỳ | Burrow / Kafka admin tools | Scale replicas nếu CPU-bound; kiểm tra lỗi handler | on-call SRE |
InventoryDBConnectionExhausted | Lỗi pool exhausted | pg_stat_activity | Tăng APP_ENV_DB_POOL_MAX; kiểm tra TX chạy lâu | on-call backend |
InventoryStockNegative | Row có quantityOnHand < 0 trong DB | Chạy query reconciliation | Audit InventoryTracking để tìm nguyên nhân; adjustment thủ công | on-call backend + finance |
InventoryOversellSpike | Số lượng note=OVERSELL_BLOCKED tăng | Tìm log | Kiểm tra pattern sale order; cân nhắc bật allowOversell cho item bán nhanh | on-call business + backend |
InventoryPOEmitFailure | Lỗi log emit purchase-order.received | Grep logs | Job replay thủ công; verify sức khỏe Kafka cluster | on-call SRE |
4.2 Thao tác thường gặp
| Thao tác | Lệnh |
|---|---|
| Tail logs | kubectl logs -n <ns> -f deploy/inventory |
| Chạy migration thủ công | kubectl exec -it deploy/inventory -- bun run migrate |
| Reset offset Kafka consumer | Dùng Kafka admin: kafka-consumer-groups --reset-offsets --group SVC-00050-INVENTORY_CONSUMER_GROUP --topic <topic> --to-earliest --execute |
Replay một PAYMENT_SUCCESS cho một order | Re-emit thủ công message với cùng key từ sale; idempotency lookup sẽ skip nếu đã xử lý |
| Force re-seed permission | Tăng version migration + chạy lại migrate |
| Inspect stock cho một item ở các location | SELECT * FROM "InventoryStock" WHERE inventory_item_id = '...' AND deleted_at IS NULL; |
| Audit movement cho một SaleOrder | SELECT * FROM "InventoryTracking" WHERE reference_type = 'SALE_ORDER' AND reference_id = '...'; |
4.3 Kịch bản Recovery
| Kịch bản | Recovery |
|---|---|
| Service crash giữa chừng handler | Kafka offset CHƯA commit → message re-deliver khi restart; idempotency lookup skip các entry đã xử lý |
| DB transaction thành công, Kafka emit lỗi (PO receive) | Log ghi nhận emit lỗi; tool replay thủ công publish từ snapshot PurchaseOrder |
| Phát hiện sai sót đếm stock | (1) Pause traffic bán, (2) chạy cycle count qua InventoryTicket type=CYCLE_COUNT, (3) ghi tracking row ADJUSTMENT_NEUTRAL |
| Mất default location của merchant | Re-emit Merchant CDC event → ensureDefaultLocation tạo lại |
5. Runbook Cross-Service
Cho sự cố trải dài nhiều service, xem central runbook/:
- Sale ↔ Inventory payment-success không trừ stock — TBD
- PO receive nhưng không tạo EXPENSE bên Finance — TBD
6. Trang liên quan
- Cấu hình
- API Sự kiện — hằng số Kafka topic cho lệnh replay
- Inventory Tracking — pattern truy vấn audit
- Quyết định