pg_durable - Hàm SQL bền vững cho PostgreSQL
(github.com/microsoft)- Phần mở rộng durable function xử lý thử lại, lập lịch, fan-out song song, phân nhánh có điều kiện ngay bên trong PostgreSQL chỉ bằng một DSL SQL nhỏ gọn
- Hoạt động chỉ với Postgres và background worker, không cần container hay dịch vụ bên ngoài
- Mọi bước đều ghi trạng thái checkpoint vào PostgreSQL để có thể tiếp tục từ điểm dừng ngay cả khi crash, khởi động lại hoặc mất kết nối
- Không cần tự triển khai quản lý hàng đợi, theo dõi trạng thái, khôi phục sau crash, điều phối bước hay thử lại, chỉ cần viết SQL là engine điều phối sẽ xử lý
- Thay thế công việc cần hơn 300 dòng boilerplate nếu tự xây bằng một lệnh gọi DSL duy nhất, có thể dùng ngay dưới dạng mã nguồn mở trên PostgreSQL 17
Tổng quan và giá trị cốt lõi
- Durable function chống crash được tích hợp trong Postgres, điều phối retry, scheduling, fan-out song song và phân nhánh có điều kiện bằng một DSL SQL nhỏ gọn
- Chạy chỉ với Postgres + background worker, không cần hạ tầng bổ sung, container riêng hay dịch vụ bên ngoài
- Đóng vai trò engine điều phối phụ trách toàn bộ quản lý queue, theo dõi trạng thái, khôi phục sau crash, điều phối bước và retry; người dùng chỉ cần viết SQL
Nếu triển khai mà không có pg_durable
- Để chạy song song 3 tác vụ tổng hợp rồi cập nhật dashboard kèm retry và khôi phục sau crash sẽ cần hơn 300 dòng boilerplate
- Các phần phải tự xây gồm: thiết lập và cấu hình queue, quản lý worker và polling, xử lý message và theo dõi trạng thái, xử lý lỗi và retry, điều phối bước thủ công
- Mã ví dụ bao gồm nhiều bảng trạng thái như
job_queue,job_results,job_state,workflow_steps,step_variables,scheduled_jobs, cùng worker polling, tiến trình workflow, khôi phục sau crash, bộ điều phối chạy song song, truyền biến, scheduling và hàm dọn dẹp - Việc tính
next_runcho scheduling còn cần thêm thư viện parser cron bên ngoài
Khi triển khai với pg_durable
- Cùng workflow tổng hợp song song + cập nhật dashboard có thể được biểu diễn bằng một lệnh gọi
df.start(), dùng toán tử&để fan-out rồi~>để join- Ví dụ: 3 truy vấn tách nhánh chạy song song rồi hợp lại ở bước
refresh dashboardđể tạo kết quả - Trong ví dụ chạy trực tiếp, sau 3 bước chạy song song rồi join, trạng thái dashboard ready được hoàn thành bền vững chỉ trong 1,9 giây
- Ví dụ: 3 truy vấn tách nhánh chạy song song rồi hợp lại ở bước
- Toàn bộ quản lý queue, theo dõi trạng thái, khôi phục sau crash, điều phối bước và retry đều do pg_durable xử lý
Tính năng chính
-
Durable by default
- Mọi bước đều ghi trạng thái checkpoint vào PostgreSQL, giúp workflow sống sót qua crash, khởi động lại và mất kết nối
- Tiếp tục chính xác từ đúng điểm đã dừng
-
Automatic retries
- Logic retry tích hợp sẵn cho các tác vụ dễ lỗi tạm thời; khi một bước thất bại chỉ bước đó được thử lại, phần còn lại của workflow vẫn tiếp tục
- Không cần mã xử lý lỗi thủ công
-
Full observability in SQL
- Toàn bộ trạng thái workflow được lưu trong các bảng Postgres, có thể truy vấn lịch sử chạy, kiểm tra output từng bước và debug lỗi bằng SQL tiêu chuẩn
- Không cần dashboard bên ngoài
-
Parallel execution
- Dùng toán tử
&hoặcdf.join()để fan-out các tác vụ độc lập, chạy đồng thời các bước tổng hợp, gọi API hoặc ETL kèm điều phối tự động
- Dùng toán tử
Các mẫu có thể xây dựng
-
ETL Pipelines
- Nối cleanup → transform → load với bảo đảm thứ tự tuần tự; mỗi bước chờ bước trước đó và dừng pipeline gọn gàng nếu lỗi (
~> sequence,|=> variables)
- Nối cleanup → transform → load với bảo đảm thứ tự tuần tự; mỗi bước chờ bước trước đó và dừng pipeline gọn gàng nếu lỗi (
-
Parallel Aggregation
- Đồng thời tổng hợp số người dùng + cộng doanh thu + kiểm tra tồn kho, fan-out thành nhiều truy vấn rồi chờ tất cả hoàn tất (
&,df.join())
- Đồng thời tổng hợp số người dùng + cộng doanh thu + kiểm tra tồn kho, fan-out thành nhiều truy vấn rồi chờ tất cả hoàn tất (
-
Order Processing
- Ghi nhận ID đơn hàng rồi truyền qua các bước xác thực, xử lý và hoàn tất; biến được tự động luân chuyển giữa các bước (
|=> capture,$var substitution,df.sleep())
- Ghi nhận ID đơn hàng rồi truyền qua các bước xác thực, xử lý và hoàn tất; biến được tự động luân chuyển giữa các bước (
-
Scheduled Jobs
- Polling API, lưu trữ bản ghi và đồng bộ dữ liệu theo lịch cron; vòng lặp chạy lâu dài và vẫn tồn tại sau khi khởi động lại (
@> loop,df.wait_for_schedule())
- Polling API, lưu trữ bản ghi và đồng bộ dữ liệu theo lịch cron; vòng lặp chạy lâu dài và vẫn tồn tại sau khi khởi động lại (
-
Conditional Branching
- Kiểm tra tác vụ chờ xử lý, số hàng hoặc cờ để rẽ nhánh xử lý hay bỏ qua; logic phân nhánh nằm trong SQL thay vì ứng dụng (
df.if(),?> conditional)
- Kiểm tra tác vụ chờ xử lý, số hàng hoặc cờ để rẽ nhánh xử lý hay bỏ qua; logic phân nhánh nằm trong SQL thay vì ứng dụng (
-
Multi-step Validation
- Lấy dữ liệu → kiểm tra schema → kiểm tra quy tắc nghiệp vụ → phê duyệt/từ chối; mỗi bước đều được checkpoint nên không mất tiến độ dù có lỗi
-
Database Maintenance
- Phát hiện yếu tố chặn autovacuum, table bloat và rủi ro wraparound để đưa ra xem xét; sau khi chờ phê duyệt có thể khắc phục một cách bền vững ngay cả sau khi khởi động lại (
?> conditional,df.wait_for_signal(),@> loop)
- Phát hiện yếu tố chặn autovacuum, table bloat và rủi ro wraparound để đưa ra xem xét; sau khi chờ phê duyệt có thể khắc phục một cách bền vững ngay cả sau khi khởi động lại (
-
Azure Functions & HTTP
- Dùng
df.http()để gọi trực tiếp Azure Functions hoặc các endpoint HTTPS được cho phép từ SQL, xử lý inline việc chia nhỏ tài liệu, làm giàu hàng dữ liệu và phân loại bản ghi
- Dùng
-
Human-in-the-Loop Approval
- Tự động phê duyệt các tác vụ thường nhật, còn các tác vụ rủi ro cao (hóa đơn lớn, thao tác phá hủy) sẽ tạm dừng cho đến khi có tín hiệu phê duyệt từ con người (
df.wait_for_signal(),df.if())
- Tự động phê duyệt các tác vụ thường nhật, còn các tác vụ rủi ro cao (hóa đơn lớn, thao tác phá hủy) sẽ tạm dừng cho đến khi có tín hiệu phê duyệt từ con người (
Hỗ trợ soạn thảo bằng AI
- Chỉ cần mô tả workflow bằng tiếng Anh thông thường, Copilot sẽ tạo ra durable-function SQL đúng cú pháp, không cần học cú pháp mà chỉ cần mô tả điều mình muốn
- Kho mã có kèm kỹ năng tác nhân tái sử dụng
pg-durable-sql, giúp GitHub Copilot và các tác nhân khác học cách tạo SQL đúng cho toán tử, thay biến, loop, parallel join và hơn thế nữa
Phát hành mã nguồn mở
- Được cung cấp hoàn toàn dưới dạng mã nguồn mở không danh sách chờ, không lock-in, có thể clone, build và chạy ngay trên PostgreSQL của riêng mình
- Có thể áp dụng durable orchestration trên laptop, máy chủ hoặc đám mây
Tùy chọn managed Azure HorizonDB
- Azure HorizonDB là dịch vụ PostgreSQL đám mây mới của Microsoft, tích hợp sẵn pg_durable, giữ nguyên durable function đã viết đồng thời bổ sung khả năng mở rộng cấp doanh nghiệp, bảo mật và AI
- Hiệu năng nhanh hơn tới 3×, tự động mở rộng lưu trữ tới 128 TB, scale-out compute tới 3.072 vCore
- Microsoft Defender phát hiện mối đe dọa theo thời gian thực, Microsoft Entra ID quản lý danh tính
- Tìm kiếm vector Filtered DiskANN, semantic ranking, tuyển chọn mô hình AI ngay trong cơ sở dữ liệu
- Microsoft Fabric mirroring gần thời gian thực, tích hợp VS Code, liên kết với GitHub Copilot
-
Pipeline AI tích hợp
- HorizonDB xếp chồng pipeline AI managed end-to-end lên trên khả năng thực thi bền vững của pg_durable, với mỗi bước đều có checkpoint, retry và an toàn trước crash
- Luồng các bước: Ingest (nạp tài liệu/dữ liệu) → Chunk (chia nhỏ nội dung) → Embed (vector hóa) → Index (lưu vào DiskANN) → Serve (tìm kiếm/xếp hạng)
1 bình luận
Ý kiến trên Hacker News
Có vẻ năm 2026 sẽ là năm của hàng đợi Postgres: có các hướng đi như DBOS[0], pgQue[1], và thật tuyệt khi cộng đồng tạo ra những lựa chọn như thế
Tuy vậy, với tư cách là một cựu kỹ sư ứng dụng, tôi vẫn thích logic hàng đợi nằm trong code và Git hơn. Có thể tôi sẽ đổi ý nếu có công cụ phù hợp
[0]: https://www.dbos.dev/
[1]: https://github.com/NikolayS/pgque
Tôi khá tò mò việc quản lý phiên bản, debug, test và phát hành sẽ được làm thế nào. Việc đặt mọi thứ vào một chỗ để tận dụng tính cục bộ của dữ liệu và đơn giản hóa stack trông có vẻ hay, nhưng tôi có cảm giác đang đánh mất khá nhiều tri thức hữu ích về cách làm cho “đúng”
Đó cũng là lý do tôi cực ghét việc ở Supabase, chỉ cần muốn làm gì hơi phức tạp một chút là đã phải tạo hàm Postgres. Dù vậy, ở startup trước tôi từng tự xây một hàng đợi tác vụ đơn giản trên Postgres, và nếu khi đó có thứ như pgQue thì có lẽ nó đã chỉn chu hơn nhiều
Các mở rộng multi-master cũng không phải kiểu có thể dùng ngay lập tức và hoàn toàn an toàn, nên tôi vẫn dè chừng khi đưa vào các tác vụ phức tạp thiên về ghi, vốn sẽ đẩy sớm nhu cầu mở rộng cơ sở dữ liệu
Có khi còn nhét chúng vào Django migration để khi thiết lập cục bộ, các trigger sẽ được đưa vào cơ sở dữ liệu local
Cái này có mùi stored procedure. Khó unit test, khó quản lý phiên bản, và logic nghiệp vụ bị giấu trong cơ sở dữ liệu thành một “bộ não ẩn”
Cũng khó cô lập workload ồn ào, không có khả năng quan sát, và toàn bộ áp lực mở rộng đều dồn vào Postgres. Đặc biệt là rất thiếu I/O như gọi API. Với các tác vụ chỉ chạy bên trong cơ sở dữ liệu local thì ổn, nhưng có vẻ phạm vi dùng khá hẹp
Tất nhiên, cần có quy trình nâng cấp cơ sở dữ liệu đàng hoàng. Nếu đồng đội cứ chạy các migration SQL tùy tiện bằng root thì sẽ rất khổ
Unit test cũng làm được giống hệt các bài test SQL khác, chỉ là phải dựng cơ sở dữ liệu lên. Nếu không test được stored procedure thì cũng đồng nghĩa là không có cách test chính SQL, và đó mới là vấn đề thật sự
Phương án thay thế stored procedure thường không phải là hoàn toàn không để logic nghiệp vụ trong cơ sở dữ liệu, mà là SQL bị rải rác khắp codebase, khó test, quản lý phiên bản và đóng gói kém, lại còn chậm một cách không cần thiết
Về khả năng quan sát thì phần nào đúng, vì soi vấn đề SQL thường vất vả hơn đa số ngôn ngữ lập trình. Nhưng nếu stored procedure gây ra vấn đề I/O và mở rộng thì tức là bạn đang dùng sai; nếu dùng đúng, chúng thường còn giúp giảm mạnh I/O và cải thiện khả năng mở rộng
Nếu tôi hiểu đúng, Absurd do các nhà phát triển Pi LLM harness tạo ra dường như đi theo hướng giảm tối đa việc truy cập cơ sở dữ liệu thuần túy. Tôi mới chỉ bắt đầu tìm hiểu chủ đề này thôi
https://github.com/earendil-works/absurd
Tất nhiên tôi không biết hết mọi chi tiết, nên thật lòng cũng khá tò mò
Ở phần “khi không nên dùng”, bài viết ghi là “khi workflow chủ yếu nằm ngoài Postgres và trải dài trên nhiều hệ thống dị chủng”, nếu vậy thì tôi không rõ dự án này có thể được so sánh với thứ như Temporal theo cách nào
Tôi đang tự hỏi liệu mình có hiểu sai giới hạn mà khuyến nghị này đang hàm ý hay không
Xét về mặt kỹ thuật thì có thể đây là một thành tựu thú vị, nhưng đọc kiểu SQL này khá là kỳ quặc
SELECT df.start(@> (($$SELECT ... FROM demo.invoices WHERE status = 'pending'$$ |=> 'inv')~> df.if_rows('inv',$$UPDATE ... SET status = 'processing'$$~> (df.http(...) |=> 'resp')~> df.if($$SELECT $r.ok$$,-- classify, branch, wait for signal ...),df.sleep(5))),'invoice-approval-pipeline');Công ty tôi đang bị khóa vào Azure, và vẫn đang phải chờ Azure PostgreSQL bắt kịp các tính năng hiện đại
Ví dụ không thể dùng cái này: https://www.paradedb.com/blog/hybrid-search-in-postgresql-th...
Cũng không hỗ trợ vector siêu rộng, số chiều cao. Việc phát hành pg_durable dưới dạng mã nguồn mở thì tốt đấy, nhưng tôi nghĩ trước hết nên bổ sung các tính năng cơ bản mà trên AWS đương nhiên đã có
Nói rõ luôn thì tôi là người bảo trì pg_textsearch và hiện đang làm ở Azure. Tôi chưa hiểu chính xác ý bạn về hỗ trợ vector, nên muốn hỏi là bạn đang cần thứ gì đó vượt quá pgvector + diskann mà Azure đang cung cấp phải không
Với tìm kiếm lai (BM25 + vector), pg_search của ParadeDB cũng không phải tính năng native của AWS mà phải tự host trên EC2. Trên Azure PostgreSQL, chúng tôi đã native hóa pg_textsearch, cung cấp cùng mô hình xếp hạng BM25, và người đóng góp chính hiện đang ở đội Azure Postgres
Tài liệu: https://learn.microsoft.com/en-us/azure/horizondb/ai/full-te...
Vector số chiều cao thậm chí còn là mảng mà chúng tôi đang đi trước. pgvector dùng HNSW có giới hạn 2.000 chiều, nhưng Azure hỗ trợ pgvector cho lưu trữ và tìm kiếm vector, còn với workload quy mô lớn, số chiều cao thì cung cấp pg_diskann, chỉ mục vector dựa trên đồ thị của Microsoft. Nó hỗ trợ tối đa 16.000 chiều, đồng thời còn có tính năng lọc ngay trong chỉ mục để đánh giá điều kiện WHERE trong lúc duyệt đồ thị, nên không làm giảm recall ở các điều kiện chọn lọc
pgvector: https://learn.microsoft.com/en-us/azure/horizondb/ai/vector-...
Hỗ trợ số chiều cao của DiskANN: https://learn.microsoft.com/en-us/azure/horizondb/ai/vector-...
Các tính năng này hiện đã dùng được trên Azure PostgreSQL, đặc biệt là Azure HorizonDB Preview. Nếu bạn có workload cụ thể, chúng tôi có thể xem chi tiết hơn
Cảm giác đây là một lời giải sai cho một bài toán cũ mà các trình lập lịch DAG như Apache Airflow đã giải quyết từ lâu
Tôi thấy khá lạ khi muốn lưu control flow trong cơ sở dữ liệu thay vì trong code. Không phải tôi muốn chê dự án, chỉ là tôi vẫn chưa thực sự hiểu
Dự án này có vẻ hướng nhiều hơn tới các use case đặc thù cho cơ sở dữ liệu. Ưu điểm có lẽ là có thể theo dõi trạng thái chính xác của tác vụ ngay trong cơ sở dữ liệu, thay vì phải lần theo từng dòng bằng cách đối chiếu log workflow với codebase. Tải và độ trễ cũng có thể thấp hơn, và về mặt vận hành thì cũng bớt đi một thành phần phải dựng và quản lý
[1] https://learn.microsoft.com/en-us/azure/durable-task/common/...
Ngược lại, dù có vẻ hiện giờ cách này chưa hoạt động như vậy, nhưng nó có khả năng tự điều tiết dựa trên phản hồi hiệu năng gần như theo thời gian thực mà không phải chịu chi phí độ trễ do round-trip
Ngay cả khi đã đọc tài liệu và ví dụ, vẫn có vài điểm chưa rõ. Tôi thắc mắc
df.wait_for_schedule()hoạt động như thế nào.Nếu gọi từ ứng dụng thì nó có idempotent không, nếu chạy hai lần với cùng tham số thì có tạo ra hai tick không, hay đây là thứ chỉ được gọi thủ công một lần từ console truy vấn, hoặc được chạy như một phần của script migration?
Tôi cũng thắc mắc liệu
timed_outtrong ví dụ[0] có phải là một hằng số cố định được trả về khi hết thời gian chờ hay không. Cũng chưa thấy rõ cách xử lý lỗi hay ngoại lệ hoạt động thế nào.[0] https://github.com/microsoft/pg_durable/blob/main/examples/i...
df.start(), nó tạo một durable function và đồng thời bắt đầu thực thi. Lời gọi này trả về một instance ID đại diện cho lần chạy đó, và có thể dùng sau này để tham chiếu đến lần chạy ấy.Bên trong durable function này,
df.wait_for_signal()được gọi, và lời gọi đó chỉ được thực thi đúng một lần trong instance của hàm đó nên không thể bị trùng lặp. Bản thân lời gọidf.start()có thể bị trùng nếu nó bị timeout rồi được chạy lại, nhưng trong trường hợp đó một instance hàm khác sẽ được tạo ra.Nếu có lỗi chưa được xử lý trong lúc thực thi SQL, instance hàm sẽ thất bại và trạng thái sẽ phản ánh nguyên chính xác lỗi đã xảy ra
Có thể giải thích vì sao nên dùng cái này thay vì một công cụ điều phối ở ngoài cơ sở dữ liệu không. Dù đã đọc README và ví dụ tôi vẫn chưa hiểu rõ
Không cần phải đồng bộ bản sao lưu với các thành phần khác thuộc cùng kho dữ liệu, nên khá phù hợp cho pipeline ETL hoặc các tác vụ kiểu state machine. Nếu ETL chủ yếu là SQL thì việc tác vụ thực sự chạy trên cùng máy chủ cũng là một lợi thế
Khi mọi trạng thái đều ở trong một cơ sở dữ liệu, khả năng có được bản sao lưu nhất quán cũng cao hơn
Tại https://transport.data.gouv.fr, Postgres được dùng cho mục đích tương tự và điều đó hữu ích trong một ứng dụng Elixir xử lý khá nhiều việc. Tôi chưa rõ lắm về pg_durable, nhưng tôi đồng cảm vì đã từng dùng hoặc tự triển khai các giải pháp tương tự
Cơ sở dữ liệu vốn đã là một trong những thành phần hạ tầng khó mở rộng nhất rồi phải không. Tôi không hiểu vì sao lại muốn chất thêm cả tác vụ chạy lâu lên đó
Suy cho cùng, kiểu workload này sẽ là các tác vụ chạy đối với cơ sở dữ liệu dù được thành phần bên ngoài kích hoạt hay không. Trong các pipeline dữ liệu hoặc AI, việc gửi truy vấn HTTP từ cơ sở dữ liệu cũng ngày càng phổ biến hơn để tránh độ trễ qua lại và các điểm lỗi do thêm thành phần bên ngoài. Tuy vậy, việc nên đưa phần tính toán đến gần dữ liệu hay đưa dữ liệu đến nơi tính toán vẫn là một lựa chọn thiết kế gây nhiều tranh luận
Nó tạo cảm giác như lại là một https://en.wikipedia.org/wiki/Inner-platform_effect khác, thứ vốn sẽ không cần thiết nếu các ngôn ngữ lập trình hay máy ảo phổ biến đã hỗ trợ sẵn tính quyết định, thực thi từng bước có thể đo lường và kiểm soát, tạm dừng trạng thái runtime, tuần tự hóa/giải tuần tự hóa và tiếp tục thực thi