- pgmock là máy chủ giả lập PostgreSQL in-memory cho kiểm thử đơn vị và kiểm thử E2E, chạy bằng WebAssembly trên Node.js và trình duyệt mà không cần phụ thuộc bên ngoài
- Người dùng
node-postgres có thể nhận một đối tượng cấu hình để kết nối mà không cần mở cổng, và cách này cũng hoạt động trong trình duyệt
- Trên trình duyệt, ứng dụng web không thể mở cổng TCP nhưng vẫn có thể dùng
PostgresMock.createSocket và cấu hình node-postgres; nếu bundler phân tích static import thì có thể xuất hiện cảnh báo về các mô-đun Node.js tùy chọn
- Hiện tại, phần triển khai chạy máy chủ PostgreSQL bên trong trình giả lập x86, ưu tiên tránh khác biệt hành vi giữa môi trường kiểm thử và production hơn là hiệu năng
- Về lâu dài, khi nhánh fork PostgreSQL WASM native trở nên hoàn thiện hơn, dự án sẽ cung cấp cả hai cách tiếp cận và sau đó chuyển WASM native thành mặc định
Những gì pgmock cung cấp
- pgmock là máy chủ giả lập PostgreSQL in-memory cho kiểm thử đơn vị và kiểm thử E2E
- Không cần phụ thuộc bên ngoài và chạy trong WebAssembly trên cả Node.js lẫn trình duyệt
- Có thể cài đặt bằng npm
npm install pgmock
Luồng sử dụng cơ bản
- Máy chủ in-memory được tạo bằng
PostgresMock.create() và có thể nhận chuỗi kết nối qua listen(5432)
import { PostgresMock } from "pgmock";
const mock = await PostgresMock.create();
const connectionString = await mock.listen(5432);
- Nếu dùng
node-postgres, mock.getNodePostgresConfig() sẽ cung cấp đối tượng cấu hình để kết nối mà không cần lắng nghe cổng
- Sau khi xong việc, nên gọi
mock.destroy() để giải phóng tài nguyên
mock.destroy();
Hỗ trợ trình duyệt và khác biệt với pglite
pgmock hỗ trợ đầy đủ môi trường trình duyệt
- Ứng dụng web không thể mở cổng TCP, nhưng vẫn có thể dùng
PostgresMock.createSocket và cấu hình node-postgres
- Nếu bundler phân tích import theo cách tĩnh, có thể xuất hiện cảnh báo thiếu các mô-đun Node.js tùy chọn; ví dụ cấu hình Webpack nằm tại
examples/web-demo/next.config.mjs
- Nếu chỉ muốn chạy cơ sở dữ liệu trong trình duyệt, có thể cân nhắc pglite
- pglite nhanh hơn và nhẹ hơn, nhưng tập tính năng bị giới hạn
pgmock được thiết kế với mục tiêu đạt mức tương đương tính năng với PostgreSQL production trong môi trường kiểm thử
Cách chạy PostgreSQL trong WebAssembly
- Có hai cách để chạy PostgreSQL trong WebAssembly
- Cách dùng fork WASM native nhanh hơn và dùng ít bộ nhớ hơn rất nhiều, nhưng chỉ hỗ trợ chế độ một người dùng và không hỗ trợ kết nối hay extension
- Hiện tại
pgmock dùng cách trình giả lập x86
- Mục tiêu là ngăn sự không khớp giữa kiểm thử và production
- Vì trong kiểm thử, hiệu năng thường không phải vấn đề lớn
- Trong trung hạn, khi fork PostgreSQL WASM native trưởng thành hơn, dự án dự định cung cấp cả hai tùy chọn
- Sau đó, dự án có kế hoạch chuyển WASM native thành mặc định và dự kiến sẽ không có nhiều breaking change lớn ngoài API nội bộ
PostgresMock.subtle
Khác biệt với các dự án PostgreSQL trên trình duyệt hiện có
pgmock cung cấp khả năng tương thích đầy đủ về tính năng bên trong runtime JavaScript và không phụ thuộc vào proxy mạng để giao tiếp
- Dự án mô phỏng ngăn xếp mạng bằng JavaScript để hoạt động như mạng thực, nhờ đó có thể mô phỏng kết nối TCP ngay cả trên các nền tảng không cho phép truy cập raw socket
Khả năng mở rộng và các dự án liên quan
- Về mặt lý thuyết, cũng có thể chạy các image Docker hoặc cơ sở dữ liệu khác, nhưng chưa được kiểm thử
- Các triển khai và dự án nền tảng liên quan được nhắc đến gồm
- v86: trình giả lập x86
- Supabase & Snaplet: nền tảng cho cách tiếp cận chạy PostgreSQL trong WebAssembly
- Stackframe: công ty được nhắc đến là đã trả lương trong quá trình phát triển
pgmock
1 bình luận
Ý kiến trên Hacker News
Trong vài tháng qua, chúng tôi đã xây dựng một phiên bản Postgres in-memory tại công ty, và nó có mức tương đương về tính năng với cơ sở dữ liệu production
Ưu điểm là không cần tiến trình bên ngoài hay proxy. Nếu nền tảng có thể chạy WASM, bạn có thể chạy pgmock cả trong Node.js hoặc trình duyệt; việc tạo một cơ sở dữ liệu mới có dữ liệu mock cũng đơn giản như tạo một object JavaScript
Nó hơi khác với pglite, thứ đã khiến chúng tôi quyết định mở mã nguồn pgmock. pgmock chạy Postgres gốc bên trong một trình giả lập x86, còn pglite biên dịch trực tiếp một fork của Postgres sang WASM native nên nhanh và nhẹ hơn
Tuy nhiên pglite chỉ hỗ trợ chế độ một người dùng và một số extension, nên không thể kết nối bằng các client Postgres thông thường; điều này khá quan trọng trong kiểm thử E2E
Về lý thuyết, có thể sửa để chạy bất kỳ Docker image nào trên nền tảng WebAssembly; tôi tò mò liệu có đối tượng cụ thể nào mọi người muốn thấy không
Chúng tôi đang nghĩ đến vài cách để thêm chế độ đa kết nối, nhưng có lẽ sẽ mất chút thời gian. PGlite cũng có các hạn chế khác liên quan đến chế độ một người dùng; ví dụ hiện chưa hỗ trợ pg_notify, và chúng tôi cũng có kế hoạch sửa điều này
Ngược lại, dự án này gần với Postgres thật hơn nhiều nên khả năng cao là cứ thế chạy được. Những dự án Postgres in-memory như thế này có vẻ có thể giảm thời gian chạy test xuống dưới một phần tư, và có tiềm năng lớn trong lĩnh vực kiểm thử
Đây là suy nghĩ từ góc nhìn của người làm PGlite
Gần đây tôi muốn chạy pipeline FFMPEG/SoX ở phía client, nhưng có quá nhiều dependency nên khó biên dịch lại dễ dàng bằng Emscripten. Tôi tự hỏi cách tiếp cận này có thể giúp ích trong những trường hợp như vậy không
Nhờ các tính năng quan hệ, có thể thêm và truy vấn cùng lúc lượng metadata phong phú, đặc thù theo domain, vốn thường nằm trong cơ sở dữ liệu quan hệ
Khi chạy
select foo();thì xuất hiệnError.captureStackTrace is not a function, xảy ra trên Firefox 124.0.2 trên LinuxTôi tự hỏi liệu chỉ cần đặt file Postgres lên ramdisk rồi chạy có được không
Cập nhật: Có vẻ nó có thể chạy trong môi trường trình duyệt/Node, nên test có thể tạo, cập nhật và xóa. Tôi quá là backend developer nên chưa rõ lợi thế so với môi trường phát triển thông thường. Sẽ rất hay nếu ai đó giải thích được nó tốt hơn ở đâu, khi nào và như thế nào
Lý do dùng WebAssembly là để hành vi có thể được điều chỉnh theo hướng portable hơn trên nhiều nền tảng, kiến trúc, cả trình duyệt hoặc môi trường edge, và tạo thành cấu hình không có dependency bên ngoài, thậm chí không cần Docker
Vì trình giả lập cho phép boot trực tiếp vào trạng thái đã chạy sẵn, việc khởi động cơ sở dữ liệu được giả lập nhanh hơn so với dựng một cơ sở dữ liệu thật hoặc container Docker. Tuy vậy, đây giống một hiệu ứng may mắn có được hơn là mục tiêu thiết kế
Tôi tự hỏi sao không dùng thứ như https://testcontainers.com/. Việc container engine là dependency bên ngoài có tệ đến thế không
Ngay khi nhét mock vào trong đó, nó trở thành unit test. Có hiệu quả, nhưng không phải cùng một thứ. Một trong những điểm cốt lõi của E2E là vì không có mock nên bạn biết test là chính xác. Đây không phải là kiểm thử Postgres, mà là kiểm thử hệ thống này mỗi lần
Nếu bạn đang xây PG cho hệ thống embedded, nhẹ, hiệu năng thấp, thì dùng làm test xác minh trước các bài E2E thật chậm hơn là hợp lý. Tôi cũng có nhu cầu như vậy
Ngoài ra thì đây là một dự án thú vị, và có vẻ là công cụ có thể dùng khi cần PG shim
Với Postgres tôi đang dùng savepoint, nhưng ngay cả trên ramdisk cũng không nhanh lắm
Trước đây trong test chúng tôi chạy đủ loại server in-memory giả tùy biến. Giờ thì dùng https://testcontainers.com để chạy đối tượng thật
Nếu developer Prisma/Node.js chỉ muốn một “Postgres đóng hộp” cho phát triển local, bản server hóa gần đây của PGlite là pglite-server có thể phù hợp hơn: https://github.com/kamilogorek/pglite-server
Nó nhanh hơn và có thể giữ dữ liệu trên hệ thống tệp, nhưng khi tải cao thì kém ổn định hơn so với server kiểm thử E2E dựa trên toàn bộ trình giả lập x86. pglite-server chỉ dùng 150MB bộ nhớ, trong khi pgmock-server dùng 830MB
Chỉ cần checkout một
.env.localmới bằng dotenv và đổiDATABASE_URLtrong tất cả các script chạypackage.jsoncủa nextjs/prismaDATABASE_URL="postgresql://postgres@localhost:5432/awesomeproject""db:pushlocal": "dotenv -e .env.local -- pnpm prisma db push"Có thể gắn vào bất kỳ dự án nào rất dễ dàng, và tôi cũng hiểu vì sao Neon tài trợ cho mảng này
Không muốn dội gáo nước lạnh, nhưng tôi không định dùng cái này
Với một ứng dụng đơn giản thì có thể hoạt động, nhưng khi độ phức tạp tăng lên — chẳng hạn có nguy cơ deadlock hoặc phụ thuộc vào dạng/thực thể của cơ sở dữ liệu — thì chỉ một khác biệt nhỏ về hành vi cũng có thể lan thành vấn đề nghiêm trọng, khiến giá trị của nó giảm đi
Dạo này tôi thích môi trường E2E bị giới hạn tài nguyên hơn. Vì trình chạy test cục bộ sẽ có cơ hội bị vỡ khi ai đó viết đoạn code cực kỳ kém hiệu quả
Ngoài ra, cách tạo snapshot cơ sở dữ liệu sau vài giây rồi phân phối snapshot đó cho các partition test rất nhanh, và đã nhiều lần giúp giảm vài phút trong test suite
Đây là một ý tưởng thú vị và là trải nghiệm học hỏi tốt, nhưng tôi cho rằng nhóm người dùng mục tiêu khá hạn chế
Tiêu đề hơi gây nhầm lẫn. Nếu “làm ở công ty” thì với giả định đã dùng tài nguyên của công ty, quyền sở hữu trí tuệ của dự án này chẳng phải thuộc về chủ lao động sao
Nếu vậy tôi thắc mắc về mặt kỹ thuật có được công bố mã nguồn mở hay không
Copyright 2024 Stackframe.. Có vẻ tác giả làm việc tại StackframeTôi tò mò cái này so với chế độ tương thích Postgres của H2 thì thế nào
Khá hay. Nếu có thể trả lời thì tôi có vài điều muốn hỏi
Tôi tò mò điều gì đã khiến công ty tạo ra dự án này, có phải chạy Postgres trong Docker container quá chậm không
Tôi cũng muốn biết cấu hình CI cho E2E test đã thay đổi thế nào trước và sau khi tích hợp pgmock vào luồng
Và cũng tò mò quá trình chuyển sang giải pháp này có khó không
Dump dữ liệu production, loại bỏ toàn bộ dữ liệu nhạy cảm, rồi truncate những bảng không cần thiết như bảng log, sẽ tạo được một bản sao tốt cho môi trường phát triển
Chỉ cần sao chép nó sang dev, QA, E2E, v.v. Thứ E2E cần chính là các extension, trigger, function, view, index, dữ liệu đó
Tôi tự hỏi sao không cứ dùng Docker và có một cơ sở dữ liệu test riêng
Elixir làm như vậy, và test framework bọc mỗi test trong một transaction rồi rollback để đảm bảo cách ly. Sẽ thú vị nếu biết ưu điểm của cách tiếp cận này là gì