GitHub tái cấu trúc API lịch sử sao: Từ 2.500 yêu cầu xuống còn 24
GitHub ra mắt API mới trả về số lượng sao theo tuần thay vì danh sách người dùng, giúp giảm số lượng yêu cầu từ 2.511 xuống chỉ còn 24 cho các kho lưu trữ phổ biến. Thiết kế mới vừa bảo vệ quyền riêng tư của người dùng vừa giảm tải đáng kể cho cơ sở dữ liệu.

GitHub tái cấu trúc API lịch sử sao: Từ 2.500 yêu cầu xuống còn 24
GitHub vừa công bố thiết kế lại API lịch sử sao (star history) theo hướng trả về số liệu tổng hợp thay vì danh sách người dùng. Thay đổi này giúp các nhà phát triển lấy được biểu đồ tăng trưởng sao chỉ với 24 yêu cầu thay vì hàng nghìn như trước đây, đồng thời loại bỏ hoàn toàn việc thu thập danh tính người dùng.
Vấn đề mà các nhà bảo trì thực sự cần
Những người quản lý dự án mã nguồn mở thường xem lịch sử sao để trả lời một câu hỏi thực tế: liệu một bản phát hành, sự kiện hay thông báo có làm thay đổi quỹ đạo của dự án hay không? Để dựng lại đường cong đó từ API GitHub, các công cụ trước đây phải yêu cầu từng trang bản ghi stargazer — tức mỗi người đang đánh dấu sao kho lưu trữ cùng thời điểm họ thực hiện — giữ lại dấu thời gian rồi vứt bỏ danh tính.
Cách làm này về lâu dài tạo ra vấn đề ở cả hai phía. Danh tính người dùng bị phơi bày cho mục đích thu thập hàng loạt, khiến kẻ xấu bắt đầu thu thập danh sách stargazer công khai để phục vụ các chiến dịch spam và lừa đảo. Với những kho lưu trữ được đánh dấu sao nhiều, điểm cuối API này còn buộc cơ sở dữ liệu phải xử lý hàng nghìn bản ghi chỉ để trả về một trang duy nhất.
Giải pháp tưởng chừng rõ ràng: trả về số lượng thay vì con người. Nhưng trả về ít dữ liệu hơn không thực sự làm cho truy vấn rẻ hơn.
Từ 2.511 yêu cầu xuống còn 24
Điểm cuối cũ GET /repos/{owner}/{repo}/stargazers được thiết kế để trả lời câu hỏi đơn giản: ai đã đánh dấu sao kho lưu trữ này? Nó trả về danh sách người dùng có phân trang, và nếu yêu cầu chỉ định media type tùy chỉnh, mỗi mục còn kèm theo thời điểm đánh dấu sao.
Mô hình dữ liệu sao cũng được xây dựng cho cùng mục đích đó: nó ghi lại tập hợp những người hiện đang đánh dấu sao kho lưu trữ. Khi ai đó bỏ sao, bản ghi đó bị xóa. Nó không được thiết kế như một nhật ký sự kiện lịch sử. Vì mỗi trang chỉ chứa tối đa 100 người, khối lượng công việc tăng theo độ phổ biến của kho lưu trữ: dựng toàn bộ đường cong cho react/react với khoảng 251.000 sao cần tới khoảng 2.511 yêu cầu, khiến các công cụ thường xuyên gặp giới hạn tốc độ hoặc phải lấy mẫu thay vì đọc hết.
Giao diện GitHub với tính năng lịch sử sao
Tệ hơn, do một lỗi trong triển khai phân trang, mỗi yêu cầu còn phải đếm toàn bộ số sao để xác định có bao nhiêu trang — dù người gọi chỉ nhận một trang tối đa 100 người. Trong tuần trước khi GitHub hạn chế quyền truy cập, gần 37.000 truy vấn đếm chạy chậm đến mức bị ghi log, trung bình gần 3 giây mỗi truy vấn, và các cơ chế bảo vệ cơ sở dữ liệu đã buộc dừng hơn 1.000 truy vấn, góp phần gây ra tình trạng hết thời gian chờ.
Sau khi hạn chế quyền truy cập, cơ sở dữ liệu phục vụ ít hơn khoảng 60% yêu cầu stargazer, và tổng thời gian dành cho truy vấn đếm giảm khoảng 80%.
Tại sao trả về ít hơn không có nghĩa là rẻ hơn
GitHub đã cân nhắc nhiều hướng tiếp cận trước khi chốt giải pháp cuối cùng.
Giữ sự kiện nhưng bỏ danh tính là phương án đầu tiên: vẫn lưu một bản ghi cho mỗi sao nhưng trả về giá trị starred_at mà không kèm danh tính cho những người gọi thiếu quyền cộng tác viên. Cách này xử lý được yêu cầu về quyền riêng tư với thay đổi tương đối nhỏ, nhưng người dùng API vẫn phải phân trang và xử lý hàng nghìn bản ghi riêng lẻ để dựng lịch sử tổng hợp.
Tổng hợp rồi phân trang là bước tiếp theo hợp lý. Thay vì trả về một dòng cho mỗi sao, truy vấn có thể nhóm sao theo ngày hoặc tuần, sắp xếp các nhóm đó rồi phân trang kết quả nhỏ hơn nhiều. Vấn đề nằm ở chỗ phân trang diễn ra sau khi nhóm và sắp xếp:
tất cả sao khớp → nhóm mọi dòng theo ngày/tuần → sắp xếp mọi nhóm → OFFSET/LIMIT → phản hồi
Với một kho lưu trữ phổ biến, yêu cầu ẩn danh vẫn có thể phải quét và tổng hợp hàng nghìn mục chỉ mục để trả về vài nhóm. Với react/react, đọc toàn bộ lịch sử theo cách này đồng nghĩa với việc xem xét khoảng 6 triệu dòng, bởi mỗi trong số 24 yêu cầu đều xử lý lại toàn bộ số sao.
Lọc theo thời gian trước khi nhóm
Thiết kế cuối cùng mà GitHub triển khai phân trang lịch sử sao theo tuần dương lịch. Với mỗi yêu cầu, hệ thống chuyển đổi page và per_page thành một khoảng created_at đã được đánh chỉ mục, lọc sao theo đúng các tuần đó, rồi nhóm các bản ghi khớp thành số lượng theo ngày:
khoảng ngày đã đánh chỉ mục → nhóm dòng trong khoảng đó → nhóm theo ngày → phản hồi
Khi áp dụng khoảng ngày trước tiên, mỗi yêu cầu chỉ xử lý các tuần được trả về thay vì toàn bộ số sao hiện tại của kho lưu trữ. Với các công cụ, điều này có nghĩa một yêu cầu giờ đây bao phủ tới 30 tuần lịch sử, bất kể kho lưu trữ có bao nhiêu sao. Với react/react, đó là khoảng 24 yêu cầu thay vì 2.511, và cơ sở dữ liệu chỉ xem xét mỗi sao khoảng một lần trên toàn bộ lịch sử — tương đương gần 251.000 dòng.
Cấu trúc phản hồi cũng được thiết kế đồng bộ với điểm cuối hoạt động commit theo tuần GET /repos/{owner}/{repo}/stats/commit_activity, giúp các công cụ dễ dàng tích hợp hơn.
Những bài học rút ra
Quá trình tái thiết kế này mang lại vài bài học quan trọng cho cộng đồng kỹ thuật:
- Phản hồi nhỏ hơn không luôn đồng nghĩa truy vấn rẻ hơn.
LIMITvàOFFSETgiảm số kết quả trả về nhưng không giảm công việc nhóm và sắp xếp mà cơ sở dữ liệu đã thực hiện. Cần nhìn vào thứ tự thực thi công việc. - Bắt đầu từ điều người dùng thực sự muốn làm. Các nhà bảo trì cần so sánh xu hướng. Danh sách danh tính chỉ là cách điểm cuối cũ tình cờ cung cấp dữ liệu nền tảng.
- Chỉ trả về những gì trường hợp sử dụng cần. Bất cứ thứ gì thừa đều là dữ liệu mà người khác có thể lạm dụng.
- Nói rõ về các đánh đổi còn lại. Điểm cuối mới an toàn và dễ dự đoán hơn, nhưng ranh giới tuần dương lịch, các sao đã bị xóa và những tuần có mật độ sao bất thường vẫn ảnh hưởng tới kết quả.
Trong quá trình so sánh hiệu năng giữa đường dẫn API cũ và mới, GitHub còn phát hiện thêm những chỗ khác cũng đang đếm sao theo yêu cầu. Họ phát hiện cùng một mẫu vấn đề trên trang stargazers của GitHub.com và đã cập nhật để dùng số sao đã lưu trong bộ đệm. Các truy vấn chậm từ trang này — trước đây lên tới hàng trăm mỗi tuần — đã giảm về không. Gần đây, GitHub cũng triển khai bản sửa tương tự cho các điểm cuối API stargazer khác.
Việc hạn chế điểm cuối gốc đã giải quyết vấn đề trước mắt. Nhưng chính việc lắng nghe các nhà bảo trì đã mang lại một giải pháp tốt hơn những gì GitHub bắt đầu: lịch sử sao dễ lấy hơn, rẻ hơn để phục vụ và an toàn hơn cho tất cả những ai đánh dấu sao cho một dự án.


