01Legal constraint như ràng buộc kiến trúc
Ràng buộc pháp lý không chỉ trả lời "có được crawl không". Nó quyết định nguồn dữ liệu nào được dùng, schema phải lưu gì, storage được immutable đến đâu, API trả trường nào, và cách dữ liệu bị xóa như thế nào.
Storage không thể thuần immutable
Kiến trúc scraping mặc định nghiêng về append-only (raw HTML → immutable blob, mọi phiên bản giữ lại). Quyền xóa (right-to-erasure) phá thẳng vào giả định đó. Xóa "một người" không phải xóa một row — nó là một deletion workflow xuyên toàn bộ lineage của subject: raw snapshot, bản parse, canonical profile, search/vector/feature/cache, và backup.
- Derived stores (search, index, cache): xóa vật lý hoặc tombstone ngay, rồi reindex/compaction — vì tái tạo được từ raw.
- Raw store: mã hóa theo từng subject hoặc partition nhỏ. Không dùng một khóa chung cho cả kho — hủy một khóa sẽ xóa quá nhiều người. Khi cần xóa: xóa object nếu rẻ; nếu object chứa nhiều subject, crypto-shred bằng cách hủy khóa riêng của partition rồi ghi tombstone.
Backup — chỗ khó nhất
Không thể hứa "xóa tức thì khỏi mọi backup" nếu backup immutable. Mô hình kết hợp ba cơ chế: derived data xóa vật lý; raw data mã hóa theo partition nhỏ; backup giữ tombstone và replay deletion khi khôi phục — không đưa PII raw vào backup dài hạn nếu không cần thiết cho mục đích tái tạo.
Special category data — API trả trường nào
Một số trường trong LinkedIn profile (tổ chức tôn giáo, hội nhóm, ảnh chân dung dùng cho nhận diện) có thể rơi vào nhóm dữ liệu đặc biệt (GDPR Điều 9), đòi cơ sở pháp lý chặt hơn dữ liệu nghề nghiệp thông thường — cần phân loại trường theo mức nhạy cảm, không trả mặc định toàn bộ payload cho mọi API caller.
02Deletion / erasure workflow
Nghịch lý kiến trúc: muốn xóa dữ liệu của một người, hệ thống phải nhận diện họ chính xác hơn cả lúc lập chỉ mục. Nếu entity resolution sai, chính cơ chế dùng để bảo vệ quyền xóa có thể xóa nhầm người khác hoặc bỏ sót người cần xóa. Vì vậy entity resolution không chỉ là pipeline tối ưu search — nó là hạ tầng quyền riêng tư.
Xác minh người yêu cầu
Ít nhất hai tín hiệu độc lập, tùy nguồn: đăng nhập tài khoản nguồn; email/domain đã xác minh; mã xác nhận gửi tới địa chỉ công khai chứng minh được họ kiểm soát; source-specific profile URL; hoặc quy trình thủ công cho trường hợp rủi ro cao. Không yêu cầu người dùng gửi thêm dữ liệu nhạy cảm nếu không cần.
Ba mức hành động, theo độ chắc chắn của danh tính
| Trạng thái yêu cầu | Hành động với hồ sơ |
|---|---|
| Chưa có bằng chứng | Không suppress; chỉ mở case. Không thông báo cho khách hàng rằng subject đã yêu cầu xóa; không để trạng thái yêu cầu thành metadata tìm kiếm được; đặt thời hạn xử lý nội bộ. |
| Đạt ngưỡng xác minh tối thiểu | Soft suppression có phạm vi: ẩn candidate đã xác minh khỏi bulk export; chặn agent dùng nó để tự động outreach/ra quyết định; vẫn cho privacy review truy cập; record chưa xác nhận trong cluster không bị ảnh hưởng. |
| Xác minh đầy đủ | Tạo deletion manifest → xóa derived data → xử lý raw theo chính sách → purge cache/index → thêm suppression fingerprint tối thiểu chống tái-ingest → trả biên nhận hoàn tất. |
Suppression fingerprint — chống tái-ingest mà không giữ lại dữ liệu gốc
Chỉ lưu HMAC của một định danh đã xác minh, dùng khóa riêng có kiểm soát — không lưu email, URL hay identifier gốc. HMAC phù hợp hơn hash thường vì không thể dễ dàng dò ngược bằng danh sách email phổ biến. Đây vẫn là dữ liệu phục vụ một mục đích hẹp, có retention và access policy riêng — không gọi nó là "không còn dữ liệu cá nhân" một cách tuyệt đối.
Phòng chống lạm dụng quy trình
Hệ thống phát hiện: một người gửi claim hàng loạt profile không liên quan; nhiều claim dùng chung contact channel; tốc độ gửi bất thường; identifier không khớp lịch sử profile; claim nhắm vào profile có lượng truy vấn cao bất thường, nhiều candidate trùng tên, hoặc hậu quả business lớn nếu suppress nhầm (tiêu chí đo được, không phải nhãn cảm tính "người nổi tiếng"). Các case này chuyển sang manual review — không dùng "nghi ngờ gian lận" làm lý do trì hoãn vô thời hạn.
03Entity resolution ở quy mô 1 tỷ
1 · Giảm 10¹⁸ cặp xuống mức chạy được — multi-key blocking
Không dùng một blocking key duy nhất. Mỗi record được đưa vào nhiều nhóm; hai record trở thành candidate nếu trùng ít nhất một nhóm — giảm bỏ sót mà vẫn tránh so sánh toàn bộ.
| Key | Ví dụ | Bỏ sót ai |
|---|---|---|
| Tên chuẩn hóa + công ty | nguyen-van-an | example-corp | Người đổi việc giữa hai lần thu thập; freelancer; công ty ghi khác tên chưa được nối |
| Tên chuẩn hóa + trường + khoảng năm | nguyen-van-an | hanoi-university | 2012–2014 | Profile không có education; năm học bị ẩn/sai; học nhiều trường mà hai nguồn lấy khác nhau |
Khi một nhóm vượt ngưỡng kích thước (tên phổ biến có thể tạo nhóm hàng chục nghìn record — so sánh mọi cặp trong nhóm 10.000 record đã là ~50 triệu cặp), không xử lý như block hợp lệ — chia tiếp bằng tín hiệu khác: quốc gia/vùng, ngành nghề, employer/education trong lịch sử, khoảng thời gian hoạt động.
Blocking chỉ trả lời "hai record này đáng được đưa ra so sánh hay không?" — chưa trả lời "hai record này chắc chắn là cùng một người."
2 · Tín hiệu để merge
Tín hiệu gần định danh (có thể tạo confirmed nếu không mâu thuẫn): cùng source profile ID/URL ổn định; cùng email đã xác minh (so sánh bằng HMAC); cùng số điện thoại chuẩn hóa được phép xử lý; stable person ID từ nguồn cấp phép. Vẫn cần điều kiện: email công ty có thể tái cấp cho người khác; URL/ID có thể bị nguồn tái sử dụng; profile có thể bị takeover.
Tín hiệu kết hợp (nhiều tín hiệu đồng thuận → probable): tên tương đồng + employer timeline nối tiếp hợp lý + cùng trường/khoảng năm + location tương thích + độ tương đồng headline/skills + cùng URL cá nhân ngoài nguồn chính.
Tín hiệu phản bác (chặn merge dù điểm khớp cao): hai công việc toàn thời gian không thể cùng tồn tại theo timeline/địa điểm; hai lịch sử học tập khác hẳn; hai identifier đã xác minh thuộc hai người khác nhau.
Chỉ confirmed mới hợp nhất thành một canonical identity; probable là một cạnh có
trọng số, không phải sự thật đã ghi vào dữ liệu. Transitive merge (A~B, B~C) chỉ thực hiện khi từng
cạnh đáp ứng ngưỡng confirmed và toàn cluster không có mâu thuẫn — chặn lỗi lan truyền qua bắc cầu.
Mâu thuẫn dựa trên dữ liệu có thể đổi (employer, location) có TTL, xét lại khi có tín hiệu mới. Mâu thuẫn dựa trên hai identifier đã xác minh giữ lâu hơn, nhưng vẫn có đường manual override.
3 · Lưu evidence để tách cluster được sau này
Chỉ lưu "lý do merge" là không đủ. Hệ thống giữ nguyên từng source record; canonical profile là một view được dựng từ các record, không phải kết quả ghi đè phá hủy bản gốc.
Khi phát hiện merge sai: (1) đánh dấu edge gây lỗi revoked; (2) tính lại connected components từ edge còn hợp lệ; (3) tạo hai canonical profile mới; (4) dựng lại field values từ source records nguyên bản; (5) rebuild search/vector/feature/cache; (6) kiểm tra lại deletion/suppression manifest thuộc component nào.
4 · Đo lỗi khi không có ground truth cho 1 tỷ người
Tập mẫu review thủ công, lấy mẫu phân tầng (không ngẫu nhiên toàn hệ thống — phần lớn cặp rất dễ và sẽ che khuất lỗi ở vùng biên): theo trạng thái merge, tên phổ biến/hiếm, block nhỏ/lớn, quốc gia/ngôn ngữ, nguồn dữ liệu, score gần/xa threshold. Mỗi cặp ≥2 reviewer độc lập, bất đồng chuyển reviewer thứ ba.
- False merge rate — trong cặp đã merge, tỷ lệ thực ra là hai người.
- Missed merge rate — trong cặp cùng người đã biết, tỷ lệ không nối được.
- Cluster contamination — tỷ lệ cluster chứa record của người khác.
- Reversal rate — tỷ lệ edge
confirmedphải revoke sau này.
confirmed tối ưu cho precision rất cao, chấp
nhận missed merge nhiều hơn.
Kiểm tra qua tác vụ thực tế, không chỉ pair metrics: chạy simulation gửi deletion claim trên tập đã biết, đo số record đúng được tìm thấy, số record người khác bị kéo vào, và việc revoke một edge có tách lại cluster chính xác không.
04Crawling & politeness
Phần này chỉ cover crawl lịch sự (tôn trọng robots.txt, tự rate-limit, backoff) — không đi vào kỹ thuật né phát hiện của một target cụ thể, phần đó thuộc phạm vi (A), không phải (B).
Frontier priority
priority = source_permission + business_value + freshness_need + change_probability + query_demand - recent_failure_penalty - estimated_fetch_cost
Thứ tự: nguồn đã cấp quyền và còn quota → profile mới có nhu cầu sử dụng thực tế → profile cũ có tín hiệu thay đổi hoặc sắp hết freshness SLA → profile ít thay đổi/ít truy vấn → URL lỗi lặp lại bị đẩy xuống dead-letter queue. Không crawl tuần tự theo ID vì vừa tạo burst lên một nguồn, vừa dùng tài nguyên cho profile giá trị thấp trước profile đang cần.
change_probability dựa trên lịch sử thay đổi quan sát được: profile đổi employer/headline hai lần trong sáu tháng có xác suất đổi tiếp cao hơn profile không đổi trong ba năm — cộng tín hiệu query demand và SLA sắp hết.
Politeness — hard ceiling, không phải gợi ý
max_rps · max_concurrency · daily_quota · robots_policy · retry_after · allowed_paths
Mặc định bảo thủ khi không có hợp đồng/hướng dẫn khác: 1 request/giây/origin, tối đa 2 request đồng thời. Gặp 429/503 đọc Retry-After; không có thì backoff tăng dần. Không tự tăng vượt mức mặc định chỉ vì server trả 200 — chỉ tăng khi có quyền/thỏa thuận rõ ràng.
Discovery vs refresh — hai frontier tách biệt
| Discovery crawl | Refresh crawl | |
|---|---|---|
| Mục tiêu | Tìm record/URL mới | Kiểm tra record đã biết còn đúng không |
| Ưu tiên | Breadth & coverage | Tuổi dữ liệu, xác suất thay đổi, query demand |
| Cơ chế | Dedup URL trước khi enqueue; lưu first_seen/source | Conditional request (ETag/Last-Modified) nếu nguồn hỗ trợ; không đổi thì chỉ cập nhật last_checked |
Tách hai frontier giúp discovery không làm profile đang dùng bị cũ, và refresh không chiếm hết ngân sách khiến hệ thống ngừng mở rộng coverage. Hai luồng dùng chung quota nguồn, nhưng có ngân sách và SLA riêng.
05Storage tiering
| Tầng | Nội dung | Đặc tính & chi phí |
|---|---|---|
| Hot cache | Kết quả query/enrich gần đây | Đắt nhất mỗi GB, latency thấp, TTL ngắn |
| Search/index | Inverted index, blocking index, vector nếu cần | Đắt do replication, RAM/SSD, write amplification |
| Canonical + identity graph | Canonical view, provenance, merge edges, deletion state | Hot/warm, cần consistency và truy cập theo ID |
| Recent raw | Snapshot mới, parse artifacts, lịch sử gần | Object storage tiêu chuẩn, rẻ hơn database |
| Cold raw/history | Snapshot cũ phục vụ audit/replay | Archive storage rẻ, restore chậm |
Chính sách theo tầng: cache purge ngay khi profile bị suppress/xóa; search/index xóa document và rebuild khi split cluster; canonical/graph giữ current state cùng evidence để giải thích và đảo merge; recent raw giữ đủ lâu để replay parser và điều tra lỗi; cold raw chỉ giữ khi license/retention/lawful purpose cho phép, hết hạn thì xóa hoặc crypto-shred.
06Agent-ready API
Hai endpoint chính: POST /v1/profiles/search và POST /v1/profiles/enrich.
Search — response không chỉ trả profile, mà trả trạng thái nhận dạng
POST /v1/profiles/search
{
"query": { "title": ["staff engineer"], "skills": ["distributed systems"], "location": ["Singapore"] },
"purpose": "recruiting_search",
"limit": 20
}
{
"results": [{
"profile_id": "cp_123",
"identity_status": "probable",
"confidence": 0.87,
"profile": { "name": "Example Person", "headline": "Staff Engineer" },
"provenance": [{ "source": "licensed_partner_a", "observed_at": "2026-07-20" }],
"allowed_actions": ["display_for_review"],
"blocked_actions": ["automated_outreach", "employment_decision"],
"next_step": "request_human_review"
}]
}
Enrich
POST /v1/profiles/enrich
{
"identifiers": { "verified_email_hmac": "hmac:...", "company_domain": "example.com" },
"requested_fields": ["current_title", "employer"],
"purpose": "crm_enrichment"
}
Response status: confirmed · probable · ambiguous · not_found.
probable bị agent hiểu thành confirmed, nó có thể gửi outreach sai người, cập
nhật CRM sai, trộn lịch sử nghề nghiệp, hoặc góp phần vào quyết định tuyển dụng sai. API phải trả
allowed_actions/blocked_actions — chỉ confirmed mới đi tiếp vào
automation được policy cho phép; probable/ambiguous bắt buộc dừng, review, hoặc yêu cầu thêm
identifier.
Rate limit cho agent — khác người dùng tay
Agent có thể gọi lặp tốc độ cao và tự mở rộng truy vấn. Giới hạn theo: service account, agent run, tenant, purpose, endpoint, records returned, daily cost budget.
Interactive search: 60 request/phút, tối đa 100 record/request Enrich: 1.000 identity/ngày/agent Batch jobs: async, quota riêng, cần phê duyệt
- Cursor pagination, không offset sâu.
- Idempotency key cho enrich.
- Cost estimate trước batch; hard budget + kill switch cho agent loop.
- Audit log ghi prompt purpose, query và profile IDs đã truy cập.
- Không bulk export mặc định.
429kèm thời điểm được thử lại. - Agent không được tự đổi
purposeđể né quota.
07Capacity & cost — ước lượng bậc độ lớn
Không phải báo giá. Giả định 1 tỷ profile, trung bình 200KB/payload, nạp ban đầu trong 30 ngày, refresh ~10%/tháng.
| Hạng mục | Quy mô | Chi phí ước tính |
|---|---|---|
| Raw payload ban đầu | ~200 TB | ~$2k–5k/tháng hot storage; thấp hơn khi chuyển cold |
| Canonical + provenance + identity graph | ~2–10 TB | ~$5k–30k/tháng tùy database, replication, throughput |
| Blocking indexes | ~300–600 GB | ~$1k–5k/tháng; chi phí build/update đáng kể hơn số byte |
| Search indexes | ~5–20 TB logical, lớn hơn sau replication | ~$20k–80k/tháng |
| Cache | ~0.5–2 TB | ~$3k–15k/tháng |
| Parse + normalize ban đầu | 1.000–1.500 worker-slot/tháng | ~$20k–80k |
| Entity resolution + index build | Nhiều blocking pass + candidate comparison | ~$30k–150k ban đầu |
| Compliance operations | Deletion, reindex, split cluster, audit | ~$5k–30k/tháng, phụ thuộc volume |
| Network cho 200 TB | Theo nguồn được cấp phép | ~$10k–20k ở $0.05–0.10/GB |
| Data license/partner feed | Phụ thuộc hợp đồng | ~$100k tới hàng triệu USD |
Tổng storage logical
Raw ~200 TB Canonical/graph 2–10 TB Search 5–20 TB Blocking indexes 0.3–0.6 TB Cache 0.5–2 TB History/headroom 20–80 TB ──────────────────────────────────── Tổng logical ~230–310 TB
Raw chiếm nhiều byte nhất, nhưng search/index chiếm nhiều tiền vận hành nhất vì cần SSD/RAM, nhiều replica, shard management và cập nhật liên tục.
Bandwidth — hai mô hình khác hẳn nhau
Câu "proxy áp đảo mọi thứ" đúng với kiến trúc scraping né chặn; với lựa chọn (B), nó được thay bằng licensed data acquisition — vẫn là khoản chi phí lớn nhất, nhưng có quyền sử dụng rõ ràng.
Compute
Deletion và split cluster tạo write amplification: một edge bị revoke có thể yêu cầu dựng lại canonical profile, search document, vector, feature và cache.
Infrastructure ban đầu (chưa gồm phí dữ liệu và nhân sự): ~$100k–500k. Recurring platform cost: ~$40k–160k/tháng. Biên độ rộng vì search throughput, replication, SLA và số lần refresh ảnh hưởng lớn hơn dung lượng raw đơn thuần.
Một lần vs vĩnh viễn
| Chi phí một lần | Chi phí vĩnh viễn |
|---|---|
| Ingest 1 tỷ profile; parse/normalize; build blocking indexes; entity resolution ban đầu; build search index lần đầu | Refresh thích ứng; cập nhật blocking/search index; entity-resolution lại khi có dữ liệu mới; license/partner feed; deletion/suppression/DSAR; reindex khi split cluster; audit, cache, API serving |
08Giới hạn & điều tôi không hứa
- Đây là design exercise, không phải kế hoạch triển khai. Số liệu cost là ước lượng bậc độ lớn, không phải báo giá — biên độ rộng vì phụ thuộc nhà cung cấp, hợp đồng license, và throughput thực tế.
- Bài chọn lập trường (B): thiết kế đầy đủ nhưng dựa trên nguồn dữ liệu được cấp phép, không phải kỹ thuật né bot-detection của một nền tảng cụ thể — phần "anti-blocking" trong bài chỉ cover crawl lịch sự.
- Crypto-shredding là biện pháp giảm khả năng truy cập, không phải xóa vật lý tuyệt đối trong mọi chế độ pháp lý.
- Entity resolution không thể chứng minh đúng trên 1 tỷ người — chỉ đo được bằng mẫu phân tầng, theo dõi reversal rate, và đặt ngưỡng theo chi phí lỗi không đối xứng.
- Legal/ToS constraints được phân tích ở mức nguyên lý (right-to-erasure, Điều 14, special category data); các viện dẫn án lệ hoặc điều khoản cụ thể cần được kiểm chứng lại trước khi dùng làm căn cứ thật, không lấy từ suy luận của bài này.