Chương 62. Multi-tenancy
Ở chương trước, ta nói về AI Agent App Architecture.
Agent có thể đọc dữ liệu, gọi tool và hành động.
Điều đó làm một câu hỏi trở nên cực kỳ quan trọng:
Agent đang hành động trong phạm vi của ai?
Nếu hệ thống phục vụ nhiều khách hàng, nhiều tổ chức, nhiều trường, nhiều công ty, ta bước vào bài toán multi-tenancy.
Multi-tenancy nghe đơn giản:
Một hệ thống phục vụ nhiều tenant.
Nhưng trong production, nó phức tạp hơn rất nhiều so với thêm một cột:
tenant_id
Vì tenant không chỉ ảnh hưởng database.
Tenant ảnh hưởng:
Dữ liệu.
Quyền truy cập.
Billing.
Quota.
Rate limit.
Audit log.
Backup.
Restore.
Export.
Analytics.
AI context.
Cache.
Search index.
Observability.
Support tooling.
Thông điệp chính của chương:
> Multi-tenancy không phải chỉ là mô hình database. Multi-tenancy là cách toàn bộ hệ thống hiểu ranh giới giữa các khách hàng, bảo vệ dữ liệu của họ, chia sẻ tài nguyên công bằng, và vận hành từng tenant một cách có kiểm soát.
---
62.1. Một tình huống: AI Judge phục vụ nhiều trường
Hãy tưởng tượng AI Judge ban đầu chỉ dùng cho một trường.
Ta có:
Users.
Classes.
Assignments.
Submissions.
Grading results.
Rubrics.
Files.
Mọi thứ khá dễ.
Sau đó sản phẩm phát triển.
AI Judge bắt đầu phục vụ nhiều trường:
Trường A.
Trường B.
Trường C.
Mỗi trường có:
Học sinh riêng.
Giáo viên riêng.
Lớp riêng.
Assignment riêng.
Rubric riêng.
File bài làm riêng.
Chính sách chấm riêng.
Gói trả phí riêng.
Quota AI riêng.
Admin riêng.
Bây giờ câu hỏi không còn là:
User này có đăng nhập không?
Mà là:
User này thuộc tenant nào?
User này đang thao tác trong tenant nào?
User có quyền với dữ liệu tenant này không?
Request này có bị tính vào quota tenant nào?
Cache này có lẫn dữ liệu tenant khác không?
Backup có thể restore riêng một tenant không?
AI retrieval có lấy nhầm rubric trường khác không?
Đây là multi-tenancy.
Nó làm hệ thống giống một tòa nhà có nhiều căn hộ.
Mọi người dùng chung hạ tầng.
Nhưng cửa, khóa, hóa đơn, dữ liệu và quyền riêng phải tách rõ.
---
62.2. Tenant là gì?
Tenant là một đơn vị khách hàng hoặc tổ chức được phục vụ bởi hệ thống.
Tùy sản phẩm, tenant có thể là:
Một công ty.
Một trường học.
Một tổ chức.
Một workspace.
Một team.
Một shop.
Một khách hàng enterprise.
Trong AI Judge:
Tenant có thể là một trường.
Ví dụ:
tenant_id = school_a
tenant_id = school_b
Mỗi tenant có dữ liệu và cấu hình riêng:
Tên trường.
Domain đăng nhập.
Danh sách user.
Plan.
Quota.
Rubric templates.
Policy.
Feature flags.
Billing information.
Tenant không giống user.
Một tenant có nhiều user.
Một user đôi khi có thể thuộc nhiều tenant.
Ví dụ:
Một giáo viên dạy ở hai trường.
Một consultant hỗ trợ nhiều khách hàng.
Một admin platform có quyền support nhiều tenant.
Vì vậy model quyền phải rõ:
User là ai?
User đang ở tenant nào?
Trong tenant đó user có role gì?
---
62.3. Single-tenant và multi-tenant
Single-tenant nghĩa là mỗi khách hàng có một instance riêng.
Ví dụ:
Trường A có deployment riêng.
Trường B có deployment riêng.
Database riêng.
Config riêng.
Hạ tầng riêng.
Ưu điểm:
Cách ly mạnh.
Dễ tùy biến sâu.
Sự cố tenant này ít ảnh hưởng tenant khác.
Phù hợp khách hàng enterprise yêu cầu riêng.
Nhược điểm:
Tốn chi phí.
Vận hành nhiều instance phức tạp.
Deploy/update khó đồng bộ.
Khó tối ưu tài nguyên.
Multi-tenant nghĩa là nhiều tenant dùng chung một hệ thống.
Ưu điểm:
Tiết kiệm hạ tầng.
Dễ rollout tính năng chung.
Dễ vận hành ở scale lớn.
Tài nguyên dùng hiệu quả hơn.
Nhược điểm:
Cần isolation tốt.
Một tenant nặng có thể ảnh hưởng tenant khác.
Bug tenant_id có thể gây lộ dữ liệu.
Backup/restore/export phức tạp hơn.
Billing/quota phải chính xác.
Không có lựa chọn nào luôn đúng.
Nó phụ thuộc vào sản phẩm, khách hàng, compliance, chi phí và đội vận hành.
---
62.4. Ba mô hình dữ liệu multi-tenant phổ biến
Có ba mô hình hay gặp:
Shared database, shared schema.
Shared database, separate schema.
Database per tenant.
Hiểu đơn giản:
Shared schema: nhiều tenant chung bảng, mỗi row có tenant_id.
Separate schema: chung database, mỗi tenant có schema riêng.
Database per tenant: mỗi tenant có database riêng.
Mỗi mô hình là một trade-off.
Không nên chọn theo cảm giác.
Nên hỏi:
Có bao nhiêu tenant?
Mỗi tenant lớn cỡ nào?
Yêu cầu cách ly dữ liệu cao đến đâu?
Có cần restore riêng tenant không?
Có khách enterprise cần database riêng không?
Đội vận hành có đủ sức quản lý nhiều database không?
Với SaaS nhỏ và vừa, shared schema rất phổ biến.
Với enterprise hoặc dữ liệu nhạy cảm, separate database có thể đáng cân nhắc.
---
62.5. Shared database, shared schema
Đây là mô hình nhiều tenant dùng chung database và chung bảng.
Ví dụ bảng submissions:
id
tenant_id
assignment_id
student_id
content
status
created_at
Mọi query phải filter theo tenant:
SELECT *
FROM submissions
WHERE tenant_id = 'school_a'
AND assignment_id = 'ass_123';
Ưu điểm:
Đơn giản khi bắt đầu.
Dễ thêm tenant mới.
Tối ưu tài nguyên tốt.
Migration schema một lần cho toàn hệ thống.
Analytics toàn hệ thống dễ hơn.
Nhược điểm:
Bug thiếu tenant filter rất nguy hiểm.
Tenant lớn có thể làm bảng phình cho mọi người.
Restore riêng một tenant khó hơn.
Xóa/export dữ liệu tenant phức tạp hơn.
Noisy neighbor dễ xảy ra.
Shared schema không xấu.
Nhưng nó đòi hỏi kỷ luật rất cao.
Nếu dùng shared schema, tenant_id phải là một phần của thiết kế từ đầu.
Không phải thêm sau như miếng vá.
---
62.6. Shared database, separate schema
Mô hình này dùng chung database server, nhưng mỗi tenant có schema riêng.
Ví dụ:
school_a.submissions
school_b.submissions
school_c.submissions
Ưu điểm:
Cách ly tốt hơn shared schema.
Query thiếu tenant_id khó lộ dữ liệu tenant khác hơn.
Có thể backup/export theo schema dễ hơn.
Một số tùy biến schema theo tenant có thể làm được.
Nhược điểm:
Migration phải chạy trên nhiều schema.
Số tenant lớn làm quản lý schema phức tạp.
Connection/query tooling khó hơn.
Analytics cross-tenant khó hơn.
Vẫn dùng chung database server nên noisy neighbor vẫn có thể xảy ra.
Separate schema phù hợp khi:
Số tenant không quá lớn.
Cần isolation mạnh hơn row-level tenant_id.
Cần restore/export theo tenant dễ hơn.
Đội vận hành chấp nhận complexity migration.
Nó là điểm giữa.
Không đơn giản như shared schema.
Nhưng cũng chưa nặng như database riêng cho từng tenant.
---
62.7. Database per tenant
Mỗi tenant có database riêng.
Ví dụ:
db_school_a
db_school_b
db_school_c
Ưu điểm:
Cách ly dữ liệu mạnh.
Backup/restore riêng tenant dễ hơn.
Tenant lớn có thể scale riêng.
Phù hợp enterprise/compliance.
Một tenant gặp vấn đề ít ảnh hưởng dữ liệu tenant khác.
Nhược điểm:
Vận hành nhiều database phức tạp.
Migration phải chạy nhiều nơi.
Connection management khó hơn.
Chi phí cao hơn.
Analytics toàn hệ thống khó hơn.
Provision tenant mới phức tạp hơn.
Database per tenant thường đáng cân nhắc khi:
Tenant ít nhưng lớn.
Khách hàng yêu cầu isolation cao.
Cần restore riêng thường xuyên.
Cần data residency riêng.
Mỗi tenant có workload rất khác nhau.
Nếu có hàng trăm nghìn tenant nhỏ, database per tenant có thể rất nặng.
Nếu có vài chục khách enterprise lớn, nó có thể hợp lý.
---
62.8. Bảng so sánh nhanh mô hình dữ liệu
| Mô hình | Ưu điểm | Nhược điểm | Hợp khi | |---|---|---|---| | Shared schema | Rẻ, đơn giản, dễ scale số tenant | Dễ lỗi tenant filter, restore riêng khó | Nhiều tenant nhỏ/vừa | | Separate schema | Cách ly tốt hơn, export/restore dễ hơn | Migration nhiều schema, vận hành khó hơn | Số tenant vừa, cần isolation hơn | | Database per tenant | Cách ly mạnh, scale/restore riêng tốt | Đắt, vận hành phức tạp | Tenant lớn, enterprise, compliance cao |
Không cần chọn một mô hình duy nhất cho mọi tenant.
Nhiều hệ thống dùng hybrid:
Tenant nhỏ dùng shared schema.
Tenant enterprise dùng database riêng.
Tenant đặc biệt có cluster riêng.
Hybrid mạnh nhưng làm routing, migration và vận hành phức tạp hơn.
Chỉ nên làm khi lợi ích thật sự rõ.
---
62.9. Tenant isolation trong dữ liệu
Tenant isolation nghĩa là dữ liệu tenant này không bị lẫn với tenant khác.
Trong shared schema, isolation chủ yếu dựa vào:
tenant_id.
Query filter.
Permission check.
Index.
Row-level security nếu dùng.
Test.
Code review.
Một lỗi rất nguy hiểm:
SELECT *
FROM submissions
WHERE assignment_id = 'ass_123';
Nếu assignment_id không globally unique hoặc bị đoán sai, query có thể lấy dữ liệu tenant khác.
Query đúng phải có tenant scope:
SELECT *
FROM submissions
WHERE tenant_id = current_tenant_id
AND assignment_id = 'ass_123';
Một nguyên tắc tốt:
Không query dữ liệu nghiệp vụ multi-tenant mà thiếu tenant context.
Tenant context nên đi từ request vào service, repository, event, job, log và metric.
Nếu một background job không biết tenant nào, nó là rủi ro.
---
62.10. Tenant context
Tenant context là thông tin cho biết request/task đang thuộc tenant nào.
Ví dụ:
tenant_id = school_a
user_id = teacher_123
role = teacher
Tenant context có thể lấy từ:
Subdomain.
URL.
JWT/session.
Organization switcher.
API key.
Request header nội bộ.
Job payload.
Event payload.
Điểm quan trọng:
Tenant context phải rõ và đáng tin.
Không nên để user tự gửi:
tenant_id = school_b
rồi hệ thống tin ngay.
Hệ thống phải kiểm tra user có thuộc tenant đó không.
Với job async, tenant context phải được lưu trong job:
grading_job {
tenant_id,
submission_id,
assignment_id
}
Nếu không, worker có thể xử lý thiếu phạm vi tenant.
---
62.11. Tenant-aware permission
Permission trong multi-tenancy phải gắn với tenant.
Không đủ để hỏi:
User này có role teacher không?
Phải hỏi:
User này có role teacher trong tenant nào?
Với lớp nào?
Với assignment nào?
Ví dụ:
User U là teacher ở school_a.
User U là student ở school_b.
Nếu hệ thống chỉ lưu:
role = teacher
thì sẽ sai.
Role phải có scope:
tenant_id.
class_id.
assignment_id nếu cần.
Ví dụ permission đúng:
Teacher school_a được xem submission của lớp mình trong school_a.
Teacher school_a không được xem submission school_b.
Platform support chỉ được xem tenant nếu có support session/audit.
Tenant-aware permission là lớp bảo vệ quan trọng nhất.
Không thể thay bằng UI ẩn nút.
Backend phải enforce.
---
62.12. User thuộc nhiều tenant
Nhiều hệ thống ban đầu giả định:
Một user thuộc một tenant.
Sau đó thực tế xuất hiện:
Một giáo viên dạy ở nhiều trường.
Một phụ huynh có con ở hai trường.
Một agency quản lý nhiều khách hàng.
Một support staff hỗ trợ nhiều tenant.
Một admin platform có quyền toàn hệ thống.
Nếu data model ban đầu quá cứng, sửa sẽ đau.
Thiết kế linh hoạt hơn:
users
tenants
memberships
roles
Membership nói:
User này thuộc tenant này với role này.
Khi user đăng nhập, họ có thể chọn workspace/tenant hiện tại.
Mọi request sau đó phải chạy trong tenant context đó.
Đây là pattern rất phổ biến trong SaaS.
---
62.13. Cache phải tenant-aware
Cache là nơi rất dễ lộ dữ liệu nếu key thiếu tenant.
Ví dụ key sai:
assignment:ass_123
Nếu ass_123 không unique toàn hệ thống, tenant khác có thể lấy nhầm.
Key tốt hơn:
tenant:school_a:assignment:ass_123
Với AI/RAG, cache còn nguy hiểm hơn.
Ví dụ:
Cache retrieval result cho câu hỏi "rubric bài 1".
Nếu key không có tenant/user/permission scope, tenant B có thể nhận rubric tenant A.
Cache AI response cũng cần tenant-aware:
tenant_id.
prompt_version.
model_id.
context_hash.
permission scope.
Cache sai không crash.
Nó trả dữ liệu sai một cách rất thuyết phục.
Đó là loại bug đáng sợ.
---
62.14. Search index và vector index cũng phải tenant-aware
Multi-tenancy không dừng ở database.
Nếu dùng search index:
Elasticsearch.
OpenSearch.
Meilisearch.
Algolia.
hoặc vector database:
Pinecone.
Weaviate.
Qdrant.
pgvector.
thì index cũng phải hiểu tenant.
Mỗi document nên có metadata:
tenant_id.
resource_type.
resource_id.
permission scope.
version.
Retrieval phải filter theo tenant trước hoặc trong lúc search.
Không nên:
Search toàn bộ index rồi nhờ model tự bỏ qua dữ liệu không được phép.
Với AI app, đây là ranh giới cực kỳ quan trọng.
Nếu RAG lấy nhầm tài liệu tenant khác, model có thể vô tình lộ dữ liệu.
Tenant-aware retrieval là yêu cầu kiến trúc, không phải chi tiết implementation.
---
62.15. Noisy neighbor là gì?
Noisy neighbor là khi một tenant dùng quá nhiều tài nguyên, làm tenant khác bị chậm hoặc lỗi.
Ví dụ:
Trường A có kỳ thi lớn.
50.000 học sinh nộp bài cùng lúc.
AI grading jobs tăng mạnh.
Worker bận.
Queue backlog.
Database chậm.
Trường B cũng bị chấm chậm dù traffic bình thường.
Nếu nhiều tenant dùng chung hạ tầng, noisy neighbor là rủi ro thật.
Nó có thể xảy ra ở:
Database.
Queue.
Worker.
Cache.
AI provider quota.
Search index.
Object storage.
Network.
Giải pháp không chỉ là "tăng server".
Cần thiết kế fairness:
Rate limit theo tenant.
Quota theo tenant.
Queue priority hoặc partition theo tenant.
Worker pool riêng cho tenant lớn.
Connection pool giới hạn.
Cost budget.
Alert theo tenant.
Nếu không, một tenant lớn có thể làm cả hệ thống xấu đi.
---
62.16. Quota và rate limit theo tenant
Quota là giới hạn tổng lượng sử dụng trong một khoảng thời gian.
Ví dụ:
Tenant A có 100.000 AI grading credits/tháng.
Tenant B có 10.000 credits/tháng.
Rate limit là giới hạn tốc độ.
Ví dụ:
Tenant A tối đa 100 submissions/phút.
Tenant B tối đa 20 submissions/phút.
Cả hai đều cần.
Quota bảo vệ chi phí dài hạn.
Rate limit bảo vệ hệ thống tại một thời điểm.
Với AI Judge, có thể giới hạn:
Số bài chấm mỗi phút.
Số AI calls đồng thời.
Số regrade hàng loạt.
Số export báo cáo.
Token budget mỗi ngày.
Shadow/evaluation usage.
Khi vượt giới hạn, hệ thống nên nói rõ:
Tenant đang vượt quota.
Yêu cầu được xếp hàng.
Vui lòng thử lại sau.
Liên hệ admin để nâng plan.
Không nên im lặng làm user bấm lại nhiều hơn.
---
62.17. Billing và plan theo tenant
Trong SaaS, billing thường gắn với tenant chứ không chỉ user.
Ví dụ plan:
Free.
Pro.
School.
Enterprise.
Mỗi plan có thể khác:
Số user.
Số bài chấm.
Số AI tokens.
Số lớp.
Tính năng export.
Retention log.
Priority support.
Model AI được dùng.
SLO.
Hệ thống phải biết:
Tenant này đang plan nào?
Feature nào bật?
Quota bao nhiêu?
Usage hiện tại bao nhiêu?
Billing cycle là gì?
Overage xử lý thế nào?
Feature flag cũng có thể tenant-aware:
Tenant enterprise được bật advanced analytics.
Tenant free không có bulk regrade.
Tenant thử nghiệm được bật model mới.
Billing không nên là thứ gắn sau cùng.
Nếu sản phẩm có plan/quota, kiến trúc phải lưu usage theo tenant từ sớm.
Nếu không, về sau tính tiền hoặc giới hạn sẽ rất khó.
---
62.18. Usage tracking theo tenant
Muốn billing và quota đúng, phải đo usage đúng.
Với AI Judge, usage có thể gồm:
Submission count.
Grading jobs.
Input tokens.
Output tokens.
Model cost.
Storage used.
File upload count.
Export jobs.
Active users.
Regrade count.
Mỗi usage record nên có:
tenant_id.
user_id nếu cần.
feature.
resource_id.
quantity.
timestamp.
idempotency key.
Idempotency rất quan trọng.
Nếu job retry, không được tính tiền hai lần cho cùng một việc nếu business không cho phép.
Ví dụ:
Cùng grading_result_id retry ba lần do timeout.
Cost provider có thể phát sinh ba lần.
Nhưng billing user có tính ba lần không?
Đó là quyết định business.
Nhưng hệ thống phải ghi dữ liệu đủ để quyết định rõ.
---
62.19. Audit log theo tenant
Audit log trả lời:
Ai đã làm gì, với dữ liệu nào, trong tenant nào, lúc nào?
Với multi-tenancy, audit log phải có tenant context.
Ví dụ:
teacher_123 viewed submission sub_456 in tenant school_a.
admin_999 changed rubric rub_1 in tenant school_b.
support_1 accessed tenant school_c under support session sup_77.
agent_5 changed feature flag for tenant school_a with approval app_88.
Audit log rất quan trọng cho:
Security.
Compliance.
Support.
Incident investigation.
Customer trust.
Không nên có audit log kiểu:
User viewed submission.
mà thiếu tenant, resource, role, reason.
Đặc biệt với platform admin hoặc support staff, mọi truy cập cross-tenant phải được audit kỹ.
Vì họ có quyền mạnh hơn user thường.
---
62.20. Support tooling cũng phải tenant-aware
Nhiều vụ lộ dữ liệu không đến từ app user-facing.
Mà đến từ admin/support tooling.
Ví dụ support dashboard cho phép tìm user bằng email.
Nếu support chọn nhầm tenant hoặc tool không hiện rõ tenant, họ có thể xem/sửa nhầm dữ liệu.
Support tooling nên có:
Tenant selector rõ.
Tenant banner rõ.
Permission theo support role.
Just-in-time access nếu cần.
Reason for access.
Audit log.
Read-only mặc định.
Approval cho hành động mạnh.
Với AI Agent cho support, càng cần cẩn thận:
Agent đang hỗ trợ tenant nào?
Tool calls thuộc tenant nào?
Có được đọc dữ liệu học sinh không?
Có support session hợp lệ không?
Đừng chỉ bảo vệ đường user.
Đường admin/support thường nguy hiểm hơn vì quyền rộng hơn.
---
62.21. Backup và restore theo tenant
Backup toàn hệ thống là một chuyện.
Restore riêng một tenant là chuyện khác.
Ví dụ:
Trường A xóa nhầm assignment.
Chỉ muốn restore dữ liệu của trường A.
Không được ảnh hưởng trường B và C.
Nếu dùng database per tenant, restore riêng dễ hơn.
Nếu dùng shared schema, restore riêng khó hơn:
Phải lọc dữ liệu tenant A từ backup.
Phải giữ quan hệ giữa assignments, submissions, files, results.
Không được ghi đè dữ liệu mới của tenant A sau thời điểm backup.
Không được chạm tenant khác.
Với AI Judge, restore tenant có thể liên quan:
Database records.
Object storage files.
Search index.
Vector index.
Queue jobs.
Audit log.
Report/read models.
Multi-tenant backup không chỉ là:
Backup database xong.
Phải có kịch bản restore theo tenant nếu business cần.
---
62.22. Export dữ liệu theo tenant
Nhiều khách hàng sẽ cần export dữ liệu của tenant.
Ví dụ:
Trường muốn tải toàn bộ submissions.
Trường muốn export điểm cuối kỳ.
Khách hàng rời hệ thống và cần lấy dữ liệu.
Compliance yêu cầu data portability.
Export theo tenant phải:
Chỉ chứa dữ liệu tenant đó.
Giữ liên kết dữ liệu đúng.
Không lẫn dữ liệu tenant khác.
Có permission rõ ai được export.
Có audit log.
Có giới hạn dung lượng/tần suất.
Có xử lý dữ liệu nhạy cảm.
Export lớn cũng có thể gây noisy neighbor.
Nếu một tenant export toàn bộ lịch sử 5 năm vào giờ cao điểm, database có thể chậm.
Vì vậy export nên chạy async:
Tạo export job.
Chạy theo batch.
Lưu file tạm có thời hạn.
Thông báo khi xong.
Rate limit theo tenant.
Export là tính năng sản phẩm.
Nhưng cũng là bài toán kiến trúc.
---
62.23. Delete tenant
Khi một tenant rời hệ thống, có thể cần xóa dữ liệu.
Delete tenant khó hơn tưởng tượng.
Dữ liệu tenant có thể nằm ở:
Database.
Object storage.
Cache.
Search index.
Vector index.
Analytics warehouse.
Logs.
Audit logs.
Backups.
Queue.
Third-party tools.
Không thể chỉ:
DELETE FROM tenants WHERE id = ...
Delete tenant cần quy trình:
Disable tenant.
Ngăn login mới.
Export nếu cần.
Xóa hoặc anonymize dữ liệu theo policy.
Xóa file.
Xóa index.
Hủy job.
Cập nhật billing.
Ghi audit.
Xử lý retention backup theo policy.
Một số dữ liệu có thể phải giữ vì lý do pháp lý hoặc audit.
Một số dữ liệu phải xóa.
Đây là nơi multi-tenancy gặp privacy và compliance.
Chương sau sẽ đi sâu hơn.
---
62.24. Tenant configuration
Mỗi tenant có thể có cấu hình riêng.
Ví dụ:
Feature flags.
Plan.
Quota.
Timezone.
Language.
Grading policy.
Allowed AI models.
Data retention policy.
SSO settings.
Webhook endpoints.
Branding.
Tenant config nên có quản lý rõ:
Ai được sửa?
Sửa lúc nào?
Config có version không?
Config có audit không?
Config sai có rollback được không?
Config theo tenant rất dễ tạo bug vì số tổ hợp lớn.
Ví dụ:
Tenant A bật model mới + SSO + custom rubric.
Tenant B dùng legacy grading + no webhook.
Tenant C enterprise có database riêng.
Test mọi tổ hợp là không thể.
Vì vậy cần giảm tùy biến không cần thiết và có config validation.
Tùy biến là sức mạnh bán hàng.
Nhưng tùy biến quá nhiều là gánh nặng vận hành.
---
62.25. Multi-tenant event và queue
Event và queue cũng phải mang tenant context.
Ví dụ event:
{
"event_type": "submission.created",
"tenant_id": "school_a",
"submission_id": "sub_123",
"assignment_id": "ass_456"
}
Nếu event thiếu tenant_id, consumer phải đoán hoặc query thêm.
Đoán là nguy hiểm.
Queue cũng có thể cần tách theo tenant hoặc theo nhóm tenant:
Queue chung cho tenant nhỏ.
Queue riêng cho tenant enterprise.
Priority queue cho deadline gần.
Worker pool riêng cho tenant lớn.
Nếu tất cả tenant dùng chung một queue, noisy neighbor dễ xảy ra.
Nhưng queue riêng cho từng tenant cũng tăng vận hành.
Cách chọn phụ thuộc vào:
Số tenant.
Traffic mỗi tenant.
SLO.
Plan.
Yêu cầu isolation.
Không có một mẫu đúng cho mọi hệ thống.
---
62.26. Observability theo tenant
Nếu hệ thống multi-tenant chỉ có metric tổng, ta sẽ bỏ sót nhiều vấn đề.
Ví dụ:
Overall error rate = 0.5%.
Nghe ổn.
Nhưng có thể:
Tenant school_a error rate = 20%.
Các tenant khác gần 0%.
Với tenant school_a, đó là sự cố lớn.
Vì vậy cần nhìn theo tenant cho các metric quan trọng:
Error rate.
Latency.
Queue age.
AI cost.
Token usage.
Storage.
Rate limit hits.
Quota usage.
Export job duration.
Support incidents.
Nhưng phải cẩn thận cardinality.
Nếu có hàng triệu tenant, không thể label mọi metric chi tiết theo tenant một cách bừa bãi.
Cách thực dụng:
Metric theo top tenants.
Metric theo plan/tier.
Tenant-level dashboard khi điều tra.
Logs/traces có tenant_id.
Sampling hợp lý.
Alert cho tenant enterprise hoặc SLO riêng.
Observability multi-tenant là cân bằng giữa nhìn đủ và không làm hệ thống monitoring quá tải.
---
62.27. Security boundary theo tenant
Tenant là security boundary.
Điều đó nghĩa là:
Dữ liệu tenant này không được lộ sang tenant khác.
Action của tenant này không được ảnh hưởng trái phép tenant khác.
Admin tenant này không được quản lý tenant khác.
Key/token của tenant này không được dùng cho tenant khác.
Một vài điểm cần chú ý:
API keys gắn với tenant.
Webhook secrets gắn với tenant.
SSO config gắn với tenant.
Encryption keys có thể tách theo tenant nếu cần.
Object storage path có tenant prefix.
Logs không lẫn dữ liệu nhạy cảm giữa tenant.
Với khách enterprise, có thể cần:
Customer-managed keys.
Dedicated environment.
Data residency.
Private networking.
Custom retention.
Không phải mọi SaaS cần mức này.
Nhưng nếu bán cho enterprise, tenant boundary sẽ trở thành câu hỏi lớn rất sớm.
---
62.28. Testing multi-tenancy
Multi-tenancy phải được test.
Không chỉ test happy path.
Các test quan trọng:
User tenant A không đọc được dữ liệu tenant B.
Teacher tenant A không sửa được assignment tenant B.
API thiếu tenant context bị reject.
Query repository luôn filter tenant.
Cache key có tenant.
Event có tenant_id.
Worker xử lý job đúng tenant.
Export chỉ chứa dữ liệu đúng tenant.
Support access được audit.
Rate limit theo tenant hoạt động.
Quota không bị vượt do retry.
Một test rất đáng có:
Tạo hai tenant có dữ liệu giống id cục bộ.
Đảm bảo mọi API chỉ trả dữ liệu tenant hiện tại.
Ví dụ:
school_a có assignment local_id = 1.
school_b cũng có assignment local_id = 1.
User school_a gọi API assignment 1.
Không bao giờ được thấy school_b.
Test kiểu này bắt được nhiều lỗi tenant filter.
---
62.29. AI và agent trong multi-tenant system
AI làm multi-tenancy nhạy hơn.
Vì AI thường dùng context rộng:
RAG documents.
Conversation history.
Memory.
Prompt logs.
Tool results.
Mỗi phần phải tenant-aware.
Với AI Judge:
Rubric retrieval phải filter tenant.
Agent memory phải scope theo tenant.
Prompt log phải có tenant_id và quyền truy cập.
Evaluation data không được lẫn tenant nếu chưa được phép.
Model gateway phải đo cost theo tenant.
Tool calling phải enforce tenant permission.
Một lỗi RAG hoặc memory có thể làm lộ dữ liệu rất tự nhiên.
Model có thể trả lời:
Theo rubric của trường B...
cho user trường A.
Đó là lỗi kiến trúc, không phải lỗi model.
AI không được dùng như lý do để nới lỏng tenant isolation.
Ngược lại, AI đòi hỏi isolation chặt hơn.
---
62.30. Khi nào multi-tenancy làm hệ thống phức tạp hơn tưởng tượng?
Multi-tenancy bắt đầu đơn giản khi:
Thêm tenant_id vào vài bảng.
Nó trở nên phức tạp khi:
Tenant có plan khác nhau.
Tenant có quota khác nhau.
Tenant có feature flag khác nhau.
Tenant lớn cần SLO riêng.
Tenant muốn export/restore riêng.
Tenant muốn SSO riêng.
Tenant muốn data residency.
Tenant có billing riêng.
Support cần truy cập tenant có audit.
AI/RAG cần filter theo tenant.
Một user thuộc nhiều tenant.
Một tenant quá lớn ảnh hưởng tenant khác.
Lúc này multi-tenancy không còn là một cột.
Nó là một trục xuyên qua toàn bộ hệ thống.
Nếu thiết kế từ đầu không tính, sửa sau rất đau.
Nhưng nếu thiết kế quá phức tạp từ đầu, ta cũng tự làm chậm mình.
Cách tốt là chọn mức phù hợp với giai đoạn sản phẩm.
Nhưng phải biết đường tiến hóa.
---
62.31. Một chiến lược thực dụng cho AI Judge
Nếu xây AI Judge dạng SaaS cho nhiều trường, một chiến lược thực dụng có thể là:
Giai đoạn đầu:
Shared database, shared schema.
Mọi bảng nghiệp vụ có tenant_id.
Membership model rõ.
Tenant-aware permission ở backend.
Cache/search/vector index có tenant filter.
Usage tracking theo tenant.
Quota AI cơ bản theo tenant.
Audit log có tenant_id.
Khi có tenant lớn:
Queue priority hoặc pool riêng.
Rate limit riêng.
Dashboard theo tenant.
Plan/quota chi tiết hơn.
Export async theo tenant.
Support tooling có audit.
Khi có enterprise/compliance:
Database riêng hoặc cluster riêng cho một số tenant.
SSO riêng.
Customer-specific retention.
Restore/export quy trình rõ.
Data residency nếu cần.
Encryption/key strategy nâng cao.
Điểm quan trọng:
Không over-engineer từ ngày đầu.
Nhưng đừng viết code như thể chỉ có một tenant mãi mãi.
Một khi dữ liệu đã lớn và code đã lan khắp nơi, thêm tenant isolation sau sẽ rất đắt.
---
62.32. Những sai lầm phổ biến
Sai lầm thứ nhất:
Nghĩ multi-tenancy chỉ là thêm tenant_id.
Thực tế, tenant ảnh hưởng quyền, cache, queue, billing, audit, backup và AI context.
Sai lầm thứ hai:
Filter tenant ở frontend.
Frontend chỉ là giao diện.
Backend/database/service mới là nơi phải enforce.
Sai lầm thứ ba:
Cache key thiếu tenant.
Đây là lỗi lộ dữ liệu rất dễ gặp.
Sai lầm thứ tư:
Support tooling không audit.
Người có quyền rộng truy cập dữ liệu tenant mà không có dấu vết.
Sai lầm thứ năm:
Không đo usage theo tenant.
Sau này billing/quota/cost control sẽ rất khó.
Sai lầm thứ sáu:
Không có chiến lược noisy neighbor.
Một tenant lớn làm mọi tenant khác chậm.
Sai lầm thứ bảy:
RAG/vector search không filter tenant.
AI có thể lộ dữ liệu tenant khác qua context.
Sai lầm thứ tám:
Không nghĩ đến restore/export/delete theo tenant.
Đến lúc khách hàng yêu cầu mới phát hiện dữ liệu nằm rải rác khắp nơi.
---
62.33. Checklist multi-tenancy
Khi thiết kế hệ thống multi-tenant, hãy hỏi:
- Tenant trong sản phẩm này là gì?
- User có thể thuộc nhiều tenant không?
- Tenant context lấy từ đâu?
- Backend có enforce tenant permission không?
- Mọi bảng nghiệp vụ có tenant scope không?
- Query có bắt buộc tenant filter không?
- Cache key có tenant không?
- Search/vector index có tenant filter không?
- Event/job có tenant_id không?
- Queue có nguy cơ noisy neighbor không?
- Rate limit/quota có theo tenant không?
- Usage có đo theo tenant không?
- Billing plan có gắn tenant không?
- Feature flag có tenant-aware không?
- Audit log có tenant_id không?
- Support access có reason và audit không?
- Backup/restore có kịch bản theo tenant không?
- Export tenant có an toàn không?
- Delete tenant xử lý dữ liệu ở những nơi nào?
- Observability có nhìn được vấn đề theo tenant không?
- AI/RAG/agent memory có tenant isolation không?
Nếu nhiều câu trả lời là "chưa biết", multi-tenancy của hệ thống vẫn còn nhiều lỗ hổng thiết kế.
---
62.34. Bảng nhìn nhanh
| Khái niệm | Hiểu đơn giản | |---|---| | Tenant | Khách hàng/tổ chức/workspace được phục vụ | | Tenant context | Request/job đang thuộc tenant nào | | Shared schema | Nhiều tenant chung bảng, phân biệt bằng tenant_id | | Separate schema | Mỗi tenant có schema riêng trong cùng database | | Database per tenant | Mỗi tenant có database riêng | | Tenant isolation | Không để dữ liệu/hành động lẫn giữa tenant | | Tenant-aware permission | Quyền truy cập có scope theo tenant | | Noisy neighbor | Một tenant dùng nhiều tài nguyên làm tenant khác bị ảnh hưởng | | Quota | Giới hạn tổng usage theo tenant | | Rate limit | Giới hạn tốc độ dùng tài nguyên | | Tenant-aware audit | Audit log ghi rõ tenant của hành động | | Tenant export/restore | Xuất/khôi phục dữ liệu riêng một tenant |
---
62.35. Kết luận của chương
Multi-tenancy là một trong những ranh giới kiến trúc quan trọng nhất của SaaS.
Nó bắt đầu bằng câu hỏi:
Ai là tenant?
Nhưng nó không dừng ở đó.
Tenant phải đi qua:
Database.
Permission.
Cache.
Queue.
Search.
RAG.
Billing.
Quota.
Audit.
Backup.
Restore.
Export.
Observability.
Support tooling.
Shared schema, separate schema và database per tenant đều có chỗ dùng.
Không có mô hình nào thắng tuyệt đối.
Điều quan trọng là hiểu trade-off và chọn theo giai đoạn, khách hàng, compliance và năng lực vận hành.
Thông điệp cần nhớ:
> Multi-tenancy không phải là thêm tenant_id vào database. Multi-tenancy là làm cho toàn bộ hệ thống luôn biết mình đang phục vụ tenant nào, được phép thấy gì, được dùng bao nhiêu tài nguyên, và phải bảo vệ ranh giới giữa các tenant ra sao.
Ở chương tiếp theo, ta sẽ nói về Privacy, Compliance và Data Governance: dữ liệu nhạy cảm, PII, consent, retention, quyền xóa dữ liệu, data export, audit trail, data residency, masking, encryption và governance cho analytics/AI training data.