KB-6356

S175 Why Round 2 - Gemini RCA Report

2 min read Revision 1

WHY-1: Event sync miss

  • Root cause 1 dòng: Sync logic là "fire-and-forget" async, thiếu cơ chế retry hoặc lock khi có nhiều update dồn dập (race condition), dẫn đến việc Directus PATCH có thể nhận data từ một state trung gian của AD thông qua API GET /kb/get/.
  • Evidence: agent_data/event_system.py:418 (emit_fire_and_forget), agent_data/directus_sync.py:143 (_fetch_document), log AD cho thấy chuỗi PUT/PATCH/PATCH dồn dập cho nd-36-01 trong cùng 1 giây.
  • Mức độ chắc chắn: CAO

WHY-2: Schema cho phép duplicate

  • Root cause 1 dòng: Bảng knowledge_documents thiếu UNIQUE constraint trên source_id hoặc file_path, trong khi logic directus_sync.py chỉ tìm và update record ĐẦU TIÊN nó thấy (limit: 1).
  • Evidence: SQL \d knowledge_documents chỉ có UNIQUE (slug), không có unique cho source_id. agent_data/directus_sync.py:128 sử dụng limit: 1 khi lookup.
  • Mức độ chắc chắn: CAO

WHY-3: limit=500 hardcode

  • Root cause 1 dòng: Các script vận hành (DOT) và Nuxt composables hardcode limit: 500 để "tối ưu" thay vì dùng pagination, dẫn đến việc miss dữ liệu khi collection vượt ngưỡng này (hiện tại 618 rows).
  • Evidence: dot/bin/dot-knowledge-sync-agentdata:87, web/composables/useKnowledgeTree.ts:101. Directus SDK default limit là 100.
  • Mức độ chắc chắn: CAO

Observations phụ

  • Risk ẩn: is_current_version đang bị hardcode True trong mọi payload push từ AD (directus_sync.py:186), dẫn đến tình trạng nhiều row cùng là "current" cho một document nếu AD push doc mới mà không revoke row cũ.
  • Pattern: Hệ thống đang lạm dụng "upsert by code" (find-then-patch) thay vì "upsert by database constraint", gây ra race condition và duplicates khi logic tìm kiếm bị giới hạn (limit 1).

KHÔNG đề xuất fix trong báo cáo này.