7 điểm bởi GN⁺ 2025-12-27 | 2 bình luận | Chia sẻ qua WhatsApp
  • Nhiều trình quản lý gói đã dùng Git như một cơ sở dữ liệu nhờ sự tiện lợi trong quản lý phiên bản và cộng tác, nhưng khi quy mô tăng lên thì vấp phải các vấn đề về hiệu năng và bảo trì
  • Cargo, Homebrew, CocoaPods và các công cụ khác cuối cùng đã chuyển sang chỉ mục dựa trên HTTP hoặc CDN do kích thước chỉ mục Git ngày càng phình to, tốc độ cập nhật chậm và sự kém hiệu quả trong môi trường CI
  • vcpkg vẫn hoạt động dựa trên hash cây Git, và trong môi trường clone nông (shallow clone) thì xảy ra lỗi build cùng các cách lách phức tạp
  • Hệ thống module của Go đã đưa vào GOPROXYcơ sở dữ liệu checksum (sumdb) để loại bỏ phụ thuộc vào Git và cải thiện bảo mật cũng như tốc độ
  • Git rất xuất sắc cho cộng tác mã nguồn, nhưng liên tục cho thấy rằng nó không phù hợp cho việc truy vấn metadata gói hoặc quản lý registry quy mô lớn

Sự thất bại lặp đi lặp lại của các nỗ lực dùng Git như cơ sở dữ liệu

  • Git hấp dẫn nhờ các ưu điểm như lịch sử phiên bản, cấu trúc phân tán, hosting miễn phí, nhưng khi dùng như cơ sở dữ liệu thì sẽ chạm tới giới hạn mở rộng
  • Nhiều trình quản lý gói đã chọn Git làm chỉ mục, nhưng theo thời gian suy giảm hiệu năng và gánh nặng hạ tầng ngày càng nghiêm trọng

Cargo

  • Chỉ mục crates.io khởi đầu là một kho Git, và mọi client đều thực hiện clone toàn bộ
    • Khi kho ngày càng lớn, ở giai đoạn delta resolution xuất hiện nút thắt hiệu năng của libgit2
    • Trong môi trường CI, mỗi lần build lại tải xuống toàn bộ chỉ mục nên cực kỳ lãng phí
  • Thông qua RFC 2789, giao thức sparse HTTP được đưa vào để chỉ lấy metadata cần thiết qua HTTPS
    • Tính đến tháng 4 năm 2025, 99% request sử dụng chế độ sparse
    • Chỉ mục Git vẫn còn tồn tại nhưng đa số người dùng không còn truy cập tới nó

Homebrew

  • GitHub đã yêu cầu Homebrew ngừng dùng shallow clone, và việc cập nhật bị chỉ ra là “một phép toán rất tốn kém”
    • Thư mục .git của homebrew-core gần chạm mốc 1GB, và khi cập nhật thì bị chậm do delta resolution
  • Vào tháng 2 năm 2023, trong Homebrew 4.0.0, việc cập nhật tap đã chuyển sang cách tải xuống JSON
    • Việc loại bỏ Git fetch giúp tăng tốc cập nhật, và chu kỳ tự động cập nhật cũng đổi từ 5 phút sang 24 giờ

CocoaPods

  • Trình quản lý gói cho iOS/macOS CocoaPodskho Specs gồm hàng trăm nghìn podspec đã trở nên quá lớn
    • Việc clone và cập nhật mất nhiều phút, và phần lớn thời gian CI bị tiêu tốn cho các thao tác Git
  • GitHub áp dụng CPU rate limit, và shallow clone bị xác định là nguyên nhân gây tải cho máy chủ
  • Nhóm đã áp dụng các biện pháp tạm thời như dừng fetch tự động, chuyển sang clone đầy đủ, sharding kho
  • Từ phiên bản 1.8, công cụ này chuyển sang phân phối HTTP dựa trên CDN, giúp người dùng tiết kiệm khoảng 1GB dung lượng đĩa và tăng tốc cài đặt đáng kể

Nixpkgs

  • Ở phía client, Nix vốn đã dùng channel dựa trên tarball để tránh clone Git
    • Biểu thức gói được cung cấp qua HTTP từ S3 và CDN
  • Tuy vậy, hạ tầng của GitHub vẫn chịu áp lực từ kho 83GB và 20.000 fork
    • Tháng 11 năm 2025, GitHub báo cáo lỗi thất bại đồng thuận giữa các bản sao và lỗi trong công việc bảo trì
    • Clone cục bộ chỉ 2.5GB, nhưng toàn bộ mạng fork gây áp lực lên dung lượng lưu trữ của GitHub

vcpkg

  • vcpkg, trình quản lý gói C++ của Microsoft, quản lý phiên bản bằng hash cây Git
    • Để tái hiện các port tại một thời điểm commit cụ thể thông qua builtin-baseline, cần toàn bộ lịch sử
  • Trong môi trường shallow clone (GitHub Actions, DevContainers), sẽ xảy ra lỗi build
    • Cách khắc phục là cần đặt fetch-depth: 0, tức phải tải xuống toàn bộ lịch sử
  • Do cấu trúc hash cây Git nên không thể truy vết commit, và đây là giới hạn mang tính cấu trúc không thể sửa được
  • Công cụ này vẫn chỉ hỗ trợ registry dựa trên kho Git, không có phương án thay thế bằng HTTP hay CDN

Hệ thống module của Go

  • Đội ngũ kỹ thuật của Grab cho biết sau khi đưa vào module proxy, thời gian go get đã giảm từ 18 phút xuống 12 giây
  • Cách làm cũ yêu cầu phải clone toàn bộ kho của từng dependency mới có thể đọc được go.mod
  • Nhóm Go lo ngại về sự phụ thuộc vào công cụ VCS và các lỗ hổng bảo mật
  • Từ Go 1.13, GOPROXY trở thành mặc định, cung cấp mã nguồn module và go.mod qua HTTP
    • sumdb (cơ sở dữ liệu checksum) đảm bảo tính toàn vẹn và tính bền vững của module

Các vấn đề phổ biến khi dùng Git như cơ sở dữ liệu

  • Wiki dựa trên Git (Gollum) trở nên chậm trong việc duyệt thư mục và tải trang khi kho quá lớn
    • GitLab có kế hoạch ngừng dùng Gollum
  • CMS dựa trên Git (Decap) vướng giới hạn request GitHub API (5.000 lần)
    • Hiệu năng suy giảm từ khoảng 10.000 mục trở lên, và người dùng mới với cache trống có thể gây bùng nổ request
  • Công cụ GitOps (ArgoCD) gặp vấn đề vượt quá dung lượng đĩa khi clone kho
    • Một commit đơn lẻ có thể làm mất hiệu lực toàn bộ cache, còn monorepo lớn cần cơ chế mở rộng riêng

Những lý do mang tính cấu trúc khiến Git không phù hợp làm cơ sở dữ liệu

  • Giới hạn thư mục: số lượng file càng nhiều thì càng chậm
    • CocoaPods từng tạo ra đối tượng cây khổng lồ do có 16.000 thư mục, và đã giải quyết bằng sharding theo hash
  • Vấn đề phân biệt chữ hoa chữ thường: Git có phân biệt, nhưng macOS và Windows thì không
    • Azure DevOps đã thêm chức năng chặn ở phía máy chủ để ngăn xung đột
  • Giới hạn độ dài đường dẫn: giới hạn 260 ký tự của Windows gây lỗi git status
  • Thiếu các chức năng của cơ sở dữ liệu:
    • Không có CHECK/UNIQUE constraint, khóa, chỉ mục hay chức năng migration
    • Mỗi trình quản lý gói đều phải tự xây dựng hệ thống kiểm tra và lập chỉ mục riêng

Kết luận

  • Git rất xuất sắc cho cộng tác mã nguồn, nhưng không phù hợp cho truy vấn metadata gói hay quản lý registry quy mô lớn
  • Phần lớn các trình quản lý gói cuối cùng đều chuyển sang chỉ mục dựa trên HTTP hoặc cơ sở dữ liệu
  • Các ưu điểm của Git như lịch sử phiên bản, quy trình PR rất hấp dẫn, nhưng nó thất bại khi được dùng để thay thế cơ sở dữ liệu
  • Khi thiết kế một trình quản lý gói mới, dù chỉ mục Git có vẻ hấp dẫn đến đâu thì vẫn sẽ chạm tới cùng những giới hạn như các trường hợp Cargo, Homebrew, CocoaPods, vcpkg và Go

2 bình luận

 
lamanus 2025-12-28

Họ dùng git vì nó tiện hơn là phải xây riêng một hệ thống để nhận đóng góp từ cộng đồng. Bảo đó là giới hạn thì tôi không thấy đồng tình lắm, mà cũng chẳng thấy phương án thay thế nào cho những vấn đề thực tế cả.

 
GN⁺ 2025-12-27
Ý kiến trên Hacker News
  • Việc này trông giống một dạng bi kịch của tài sản chung. GitHub thì miễn phí và có nhiều tính năng tuyệt vời nên ai cũng muốn dùng. Nhưng kiểu quyết định này luôn xảy ra khi có ngoại tác
    Ngoại tác mà tôi coi là quan trọng nhất là thời gian của người dùng. Phần lớn các công ty phần mềm chỉ quan tâm chi phí thời gian kỹ sư, còn thời gian của người dùng thì bị bỏ qua. Họ tập trung phát triển tính năng nhưng không tối ưu thời gian tương tác của người dùng. Ví dụ, nếu tôi bỏ 1 giờ để làm ứng dụng nhanh hơn 1 giây, thì một triệu người dùng sẽ tiết kiệm được 277 giờ mỗi năm. Nhưng vì thời gian người dùng là ngoại tác nên kiểu tối ưu này hiếm khi được thực hiện
    Cuối cùng người dùng phải tải thêm dữ liệu vô ích và chờ đợi lâu hơn, còn lập trình viên thì không phải chịu trách nhiệm cho sự lãng phí đó

    • Tôi không rõ chính xác “software house” nghĩa là gì, nhưng hầu hết sản phẩm phần mềm tiêu dùng mà tôi từng làm đều theo dõi chặt các chỉ số như tốc độ khởi động hay độ trễ. Đây đã là kiến thức phổ biến từ hàng chục năm trước. Ví dụ, người ta vẫn thường nói Amazon có thể mất hàng triệu đô la chỉ vì chênh lệch vài mili giây trong thời gian tải trang
    • Điều này cùng mạch với câu “tốc độ cũng là một tính năng(feature)”. Tuy nhiên, thời gian của người dùng không chỉ bị chi phối bởi hiệu năng đơn thuần mà còn chịu ảnh hưởng lớn từ thiết kế UI
    • Tôi không nghĩ đây là “bi kịch của tài sản chung”. GitHub là tài sản của Microsoft, nên điều đó chỉ có nghĩa là họ cho rằng mình gánh được. Tài sản chung thực sự phải là thứ không ai sở hữu nhưng mọi người đều được hưởng lợi
    • Càng nghĩ sâu về vấn đề này lại càng nhớ đến câu nói của Alan Kay — “nếu thực sự coi trọng phần mềm thì bạn cũng phải tự làm phần cứng”. Việc tải qua mạng về bản chất là trải nghiệm người dùng tệ. Nếu thật sự tôn trọng người dùng thì phải làm ứng dụng ưu tiên cục bộ(native-first). Nhưng rất hiếm công ty tôn trọng trải nghiệm người dùng đến mức đó
    • Bài “Saving Lives” của Andy Hertzfeld khá thú vị — có một giai thoại kiểu “Macintosh khởi động quá chậm. Phải làm nó nhanh hơn!”
  • Tôi đang làm Cargo/UV cho C. Bài viết rất hay và tôi đồng cảm sâu sắc.
    Lúc mới bắt đầu thì vận hành registry thật sự rất khó. Không chỉ phải viết mã, đảm bảo chất lượng công cụ, mở rộng cộng đồng, mà còn phải tính đến hạ tầng có thể chịu được lưu lượng toàn cầu. Trong tình huống đó, giải pháp dựa trên git rất hấp dẫn
    Nhưng vấn đề là sparse checkout. Tôi muốn quản lý phiên bản manifest gói bằng git, nhưng lại phải theo dõi các commit tùy ý nên rất kém hiệu quả. Cuối cùng thành ra phải push hai commit, nên thực tế là không khả thi
    Tôi nghĩ cách tiếp cận của Conan là thực dụng nhất. Thay vì khả năng tái lập hoàn hảo, họ đưa logic có điều kiện vào manifest. Cũng có thể ánh xạ manifest theo từng khoảng phiên bản. Không hoàn hảo, nhưng là một thỏa hiệp thực dụng và hữu ích.
    Tất nhiên giải pháp thật sự là dùng cơ sở dữ liệu, nhưng cũng chẳng có ai trả hộ chi phí máy chủ và bảo trì nên ngoài thực tế rất khó

    • Nếu đổi góc nhìn thì phần lớn các package manager thành công ban đầu đều dựa trên Git, rồi khi cần mới chuyển sang cấu trúc hiệu quả hơn
    • Cách của Arch Linux AUR cũng đáng cân nhắc. Mỗi gói có một kho git độc lập và chỉ chứa manifest. Như vậy có thể tránh vấn đề monorepo hay cơn ác mộng về tham chiếu
    • Vận hành registry bằng một backend HTTP đơn giản như S3 cũng hấp dẫn. Ban đầu có thể chạy bằng một máy chủ duy nhất, rồi khi nổi tiếng thì tìm nhà tài trợ và chuyển lên cloud.
      Nếu vấn đề là tiền bạc và tính độc lập thì cũng có thể dùng P2P. Tuy nhiên nếu không có cache CI thì lưu lượng có thể tăng vọt
    • Nếu người dùng còn chưa nhiều thì chuẩn bị sẵn hạ tầng phục vụ toàn cầu là quá sớm
    • Cũng không nhất thiết phải cho người dùng thấy toàn bộ dữ liệu lịch sử. Có thể dùng post-commit hook để render riêng trạng thái HEAD thành file tĩnh rồi phục vụ kiểu GitHub Pages.
      Cấu trúc mirror của các bản phân phối Linux như Debian, Fedora, openSUSE cũng đáng tham khảo
  • Bài này đang trộn lẫn hai vấn đề. Một là dùng git làm cơ sở dữ liệu chỉ mục gói, hai là lấy mã của từng gói bằng git. Hai chuyện đó tách biệt.
    Chỉ mục có thể ở git còn gói thì ở zip/tar, hoặc ngược lại. Trường hợp của Go thì thậm chí còn không có chỉ mục

    • Có vẻ tác giả hơi bị lẫn. Tôi đồng ý với ý “đừng để mọi người dùng phải sao chép toàn bộ cơ sở dữ liệu”, nhưng điều đó không có nghĩa là không thể dùng đồ thị git để mã hóa dữ liệu.
      Chuyện backend của GitHub hay 20.000 fork đều không liên quan đến bản chất. Ngay cả không có working tree của git thì vẫn có thể tra cứu key-value hiệu quả.
      Lập luận “rewrite lịch sử git giống như migration DB” cũng kỳ lạ. Chẳng phải chạy một Postgres còn tốt hơn sao?
    • Trọng tâm của bài nằm ở quá trình lấy chính file go.mod, chứ không phải bản thân mã nguồn. Vì vậy giải pháp là host riêng go.mod
    • Trong git cũng có thể chỉ lấy đúng một file cần thiết, nhưng về mặt cấu trúc thì vẫn hơi gượng ép
  • Cách tiếp cận kiểu “lúc còn chạy được thì dùng cách dễ, khi nào không ổn thì sửa” là thực tế.
    Julia cũng làm như vậy, và số package của họ mới khoảng 1/7 của Rust nên chưa có vấn đề gì.
    Có thể cải thiện bằng cách chỉ tải file Registry.toml cấp cao nhất rồi mới tải package cần thiết. Không phải vấn đề lớn

    • Julia chỉ dùng registry git như một sổ cái chính thức(ledger), còn client thực tế dùng Pkg Protocol
    • Có thể gọi cách tiếp cận này là tinh thần “FAFO(thử đi rồi vỡ mặt)”. Thực dụng đấy, nhưng cá nhân tôi không thích
    • Tôi cho rằng thái độ này là phi đạo đức. Cách nghĩ “cứ làm cho dễ trước rồi sửa sau” cuối cùng chỉ làm tăng nợ kỹ thuật.
      Văn hóa “Move fast and break things” đã tạo ra đống phần mềm chậm chạp và đầy lỗi như hiện nay
    • Nếu để sau mới sửa thì chi phí sẽ tăng theo cấp số nhân. Cuối cùng kết luận sẽ là “thôi cứ dùng tạm khi nó còn hơi lỗi”. Trường hợp vcpkg trong bài chính là ví dụ đó
    • Có một ví dụ trông như ai đó đã cố tình thao túng UUID, và việc đó có thể làm được khiến tôi hơi lo
  • Tôi đồng ý với kết luận rằng “Git là một cơ sở dữ liệu tuyệt vời để khởi đầu package manager”

    • Tuy nhiên, phía client tốt hơn là đừng nhận toàn bộ repository mà nên có lớp cache hoặc DB. Với môi trường CI/CD thì hiệu quả lại càng quan trọng
    • Nixpkgs cũng thành công nhờ Git. Vấn đề scale là kiểu lo xa sang chảnh để tính sau
    • Nhưng trước khi ca ngợi Git thì tốt hơn nên đọc qua một chút về nghiên cứu cơ sở dữ liệu
    • Git cũng có thể gây ra ác mộng chuỗi cung ứng. Thảm họa Leftpad có thể lặp lại hằng tuần
    • Git là một cơ sở dữ liệu tệ hại cho package manager. Chỉ là GitHub host miễn phí nên ai cũng dùng thôi
  • Tôi đứng về phía “cuối cùng vẫn ổn mà”. Nó đã giúp rất nhiều cho giai đoạn vận hành ban đầu, và vấn đề scale có thể xử lý sau

    • Nhưng một số dự án đang không thoát ra được khỏi git vì giới hạn ở cấp kiến trúc
    • Nếu bắt đầu bằng một kho lưu trữ kiểu hệ thống tệp như git thì về sau gần như không thể đổi giao thức. Ngay từ đầu phải đi theo thiết kế lấy API làm trung tâm
    • Thực ra vẫn còn cơ hội khai thác git hiệu quả hơn. Bảo bỏ git mà không đưa ra phương án thay thế thì là kết luận nửa vời
    • Cũng có câu đùa mỉa mai kiểu “không scale được từ 0 đến 1 nghìn tỷ người dùng thì thành rác à”
  • Ở đây có survivorship bias. Cargo thành công nên chỉ mục git của nó mới phình ra và gây vấn đề.
    Phần lớn dự án nhỏ vẫn đang dùng git rất ổn như một giao thức phân phối dữ liệu.
    Ở giai đoạn đầu khi chưa rõ có scale hay không, tận dụng git và GitHub để tập trung vào vấn đề cốt lõi là hợp lý

    • Cần cảnh giác với việc tối ưu quá sớm. Cargo hay Homebrew cũng chọn con đường dễ đi để phát triển, còn vấn đề scale về sau là kiểu “bài toán tốt”
  • Mỗi khi thấy một bài trên trang đầu HN kiểu “thứ bạn đang làm hiện giờ là sai”, tôi lại thấy khiêm tốn hơn.
    Tôi cũng từng vài lần rơi vào tình huống đó. Lần này là bài về PG Notify.
    Nhưng hiện giờ tôi đang phát triển một mình, còn chẳng biết dự án có thành công hay không, nên phân phối plugin bằng git là phương án thực tế nhất.
    Dù vậy nếu sau này gặp vấn đề scale thì tôi sẽ tham khảo bài này

    • Ngay từ bây giờ vẫn có thể tránh được vài cái bẫy. Những thứ như vendor lock-in vào GitHub thậm chí có thể là vấn đề lớn hơn
  • Cá nhân tôi host code bằng Forgejo. Nó được bảo vệ bằng mTLS mà không phơi ra ngoài.
    Nhưng Go module lại yêu cầu chứng chỉ nên không nhận diện được instance Forgejo của tôi.
    Dù dùng SSH thì nó vẫn đòi truy cập HTTPS, nên cuối cùng tôi phải dùng replace directive với bản sao cục bộ. Khá phiền

    • Nếu thêm .git vào cuối đường dẫn module và đặt $GOPRIVATE thì có thể dùng xác thực lệnh git mà không cần yêu cầu HTTPS. Xem tài liệu chính thức
    • Nếu thêm chứng chỉ TLS(CA) của instance vào trust store thì cũng có thể tải qua HTTPS
    • Nói rằng “cần truy cập HTTP” thực ra không đúng. Có thể giải quyết bằng proxy cục bộ
    • Dùng DNS và chứng chỉ của Tailscale thì có thể lấy chứng chỉ Let’s Encrypt mà không cần phơi ra ngoài
  • Không chỉ package manager, rất nhiều dự án nhỏ cũng crowdsource dữ liệu vào repository git.
    Phần lớn quy mô nhỏ nên chưa chạm phải giới hạn kỹ thuật.
    Tuy vậy cấu trúc như vậy lại làm tăng rào cản tham gia của người không phải lập trình viên. Package manager là ngoại lệ, còn với dự án thông thường thì đây là vấn đề
    Tôi đã tạo một thư viện mã nguồn mở tên là Datatig để giúp xử lý vấn đề này.
    Tài liệu thuyết trình liên quan ở đây. Sau này tôi định tham khảo bài viết này để bổ sung thêm nội dung về mở rộng quy mô