3 điểm bởi GN⁺ 4 giờ trước | 1 bình luận | Chia sẻ qua WhatsApp
  • 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_run cho 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
  • 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ặc df.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

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)
  • 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())
  • 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())
  • 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())
  • 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)
  • 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)
  • 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
  • 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())

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

    • Có thể cách làm này khó hơn hoặc đơn giản là khác biệt, nhưng có vẻ đang thiếu tài liệu, bài viết có thể tìm kiếm, kinh nghiệm và công cụ
      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”
    • Tôi đồng ý với ý “muốn giữ logic hàng đợi trong code”. So với bản thân dữ liệu, những hành động cần thực hiện trên dữ liệu thay đổi thường xuyên hơn nhiều, nên việc phải migration mỗi lần thay đổi hành vi thật khó hiểu
      Đó 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
    • Tôi là fan từ thời Postgres 7 và từng thử nghiệm nhét càng nhiều thứ càng tốt vào PostgreSQL, nhưng ít nhất thì trải nghiệm lập trình viên và khả năng quan sát vẫn còn thiế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
    • Trong các dự án có DB trigger, chúng tôi cũng đưa code SQL vào Git để quản lý
      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

    • Stored procedure nếu dùng đúng cách thì cũng rất tuyệt. Quản lý phiên bản bằng ID tăng đơn điệu gắn vào cuối tên là được; khi cần thay đổi phá vỡ tương thích thì tăng ID lên, còn phiên bản cũ cứ để lại cho tới khi không còn được dùng nữa
      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

    • Sửa nhẹ một chút thôi, absurd có vẻ là dự án gốc của earendil từ trước khi Mario Zechner gia nhập earendil, và tôi cũng không thấy anh ấy trong lịch sử commit
      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

    • Đồng ý. Nhìn vào ví dụ https://github.com/microsoft/pg_durable/blob/main/examples/i..., tôi không thực sự hiểu giá trị của dự án này nằm ở đâu
      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ó

    • ParadeDB dùng AGPL, nên nhìn chung khó được các nhà cung cấp cloud lớn cung cấp như một tính năng tiêu chuẩn. Tuy vậy, trên Azure HorizonDB có thể dùng https://github.com/timescale/pg_textsearch, và nhiều khả năng cũng sẽ sớm có trên Flex
      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
    • Có vẻ Azure Cosmos DB sẽ phù hợp hơn cho tìm kiếm vector?
    • Có lẽ bạn đã cân nhắc rồi, nhưng tôi vẫn tò mò vì sao không просто tạo một VM trần và cài Postgres mới nhất lên đó
    • Tôi là PM phụ trách các tính năng AI của Postgres trong đội Azure PG. Những tính năng bạn yêu cầu thực tế đều đang có, và trong 3–6 tháng vừa qua đã có rất nhiều tiến triển
      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

    • Ở Microsoft có framework Durable Task[1] cho đúng mục đích này; nó có thể chạy như một dịch vụ độc lập tự host kiểu Temporal, và cũng có thể chạy serverless trong Azure Functions. Nếu tôi nhớ không nhầm thì nó còn ra đời trước cả Airflow lẫn Temporal
      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/...
    • Các công cụ bên ngoài như Airflow không thể biết được mức tải của cơ sở dữ liệu. Nếu lập trình viên đẩy 200 worker đồng thời vào cơ sở dữ liệu thì các workload khác có thể bị ảnh hưởng
      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_out trong 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...

    • Khi gọ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ọi df.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õ

    • Nếu dùng snapshot PITR của cơ sở dữ liệu thì toàn bộ các tác vụ bền vững tại một thời điểm cụ thể cũng sẽ được khôi phục cùng nhau.
      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ế
    • Từ góc nhìn của người đóng góp, khách hàng Postgres của Microsoft chia khá đều thành hai nhóm: nhóm muốn làm càng nhiều càng tốt bên trong cơ sở dữ liệu, và nhóm muốn giữ ứng dụng cùng phần tính toán ở ngoài cơ sở dữ liệu
    • Sẽ có lúc điều này tiện lợi nếu trong kiến trúc, cơ sở dữ liệu là thành phần lưu trạng thái duy nhất.
      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
    • Có thể tích hợp tốt với workflow của ứng dụng. Ví dụ có thể hiển thị tiến độ từ một permalink trong ứng dụng frontend, tạo workflow vẫn tiếp tục chạy sau khi ứng dụng khởi động lại, mà không cần thêm một thành phần hạ tầng mới.
      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 đó

    • Chạy tác vụ dài hạn trong Postgres hoàn toàn không phải chuyện mới. Một ví dụ là pg_cron.
      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