pgflow Workflow Engine

repository·main·Indexed 43 days ago

https://github.com/pgflow-dev/pgflow

A workflow engine built for Supabase that enables complex, multi-step AI and data pipelines using TypeScript. It leverages Postgres primitives like pgmq and pg_cron for reliable execution, automatic retries, and observability without requiring external infrastructure like Redis or Temporal.

Tokens
127.8K
Snippets
379
Records
586
Agent score
49%

What's inside pgflow

  1. Build RAG pipelines with pgflow and Postgres

    main

    pgflow allows you to move Retrieval-Augmented Generation (RAG) orchestration directly into Postgres. This approach addresses the complexities of vector embedding pipelines, such as detecting content changes, chunking content, calling embedding APIs, storing results, and handling retries without blocking database writes.

    Key capabilities covered in the RAG series include:

    • Automatic Embeddings: Ingesting, chunking, and embedding data automatically upon INSERT.
    • Keeping Data Fresh: Handling updates and deletes to maintain synchronization.
    • Advanced Retrieval: Implementing techniques like Hypothetical Document Embeddings (HyDE), reranking, and LLM-augmented retrieval.
    • Conversational RAG: Implementing chat functionality with conversation history.
  2. Key features of the Edge Worker

    main

    The Edge Worker wraps Supabase Background Tasks to provide enterprise-grade queue management features:

    • Retries with delays: Automatically retries failed tasks after a specified delay.
    • Concurrency control: Manages how many tasks are processed simultaneously.
    • Observability: Provides visibility through heartbeats and logging.
    • Horizontal scaling: You can scale throughput by deploying multiple edge functions to listen to the same queue.
  3. Core benefits of using pgflow

    main

    pgflow is an orchestration engine designed to run entirely within Supabase, providing several advantages over manual queue management:

    • No manual wiring: Eliminates the need to manually set up pg_cron, pgmq, and multiple Edge Functions for state tracking and task coordination.
    • Runs entirely in Supabase: Uses Postgres and Edge Functions, requiring no external infrastructure like Redis, Temporal, or Bull.
    • Full observability in SQL: All workflow state, execution history, and step outputs are stored in Postgres tables, allowing you to debug using standard SQL.
    • Automatic retries: Built-in exponential backoff for individual steps. If an API call fails, only that specific step retries without restarting the entire workflow.
    • Parallel array processing: Supports processing arrays in parallel with independent retries per item.
    • Flexible triggers: Workflows can be started from database triggers, scheduled pg_cron jobs, browser clients, or RPC calls.
  4. Compare pgflow vs Trigger.dev

    main

    pgflow and Trigger.dev are both workflow orchestration systems, but they differ fundamentally in their architectural models and infrastructure requirements.

    Key Differences

    FeaturepgflowTrigger.dev
    ArchitectureDatabase-centric orchestrationBackground job execution and orchestration
    InfrastructureZero additional (just Supabase)Hosted service or self-hosted
    Workflow DefinitionTypeScript DSL with explicit dependenciesTypeScript functions with tasks and subtasks
    Supabase IntegrationBuilt-inRequires manual configuration
    MonitoringSQL queries for visibilityComprehensive web dashboard
    Execution StructureDatabase-driven DAGTask/subtask hierarchy
    SchedulingNative step delays + Supabase pg_cronBuilt-in cron scheduler

    When to Choose pgflow

    • Committed to Supabase: You want a solution that lives entirely inside your Supabase project.
    • DAG-based workflows: You need to express complex data dependencies as a directed acyclic graph.
    • SQL visibility: You prefer monitoring workflow state via direct SQL queries.
    • Lightweight deployment: You want minimal infrastructure with no additional services to maintain.
    • PostgreSQL transactions: You want to leverage database transactions throughout workflows.
    • Edge Function execution: You want to run workflow steps as Supabase Edge Functions.
    • Explicit data flow: You prefer declarative data dependencies over imperative control flow.

    When to Choose Trigger.dev

    • User-friendly dashboard: You need a UI for monitoring and managing runs.
    • Task relationships: You prefer parent-child task relationships over explicit dependency graphs.
    • Advanced concurrency: You need sophisticated queuing and per-tenant controls.
    • Rich lifecycle hooks: You want init, cleanup, onSuccess, and onFailure hooks.
    • Dynamic delays: You need complex conditional delays or waiting for external events.
    • Immediate production use: You need a mature, battle-tested solution ready for production.
  5. Production readiness and limitations of pgflow

    main

    pgflow is currently in public beta. While core functionality is considered stable and reliable, users should be aware of the following limitations:

    • Performance: While core operations use batching and indexing, extremely large-scale workloads may require additional optimization.
    • Fair Processing: There is currently no built-in fair processing across flows. A single flow with many tasks (particularly those using map steps to process large arrays) can saturate the queue and delay other flows. For multi-tenant scenarios requiring fairness, consult the community for workarounds.
    • Permissions: pgflow does not include built-in access controls. If using the TypeScript Client or Supabase RPC from client applications, you must manually configure Database permissions.
  6. What is the Context Object in pgflow?

    main

    The context object is pgflow's mechanism for providing handlers with access to platform resources like database connections, environment variables, and service clients. Instead of handlers creating their own connections (which leads to resource leaks and boilerplate), pgflow manages these resources centrally and injects them through a standardized second parameter in handler functions.

    When pgflow executes a handler, it passes two parameters:

    1. The input object.
    2. The context object.

    The context parameter is optional; handlers that do not require platform resources can omit it.

  7. Available step methods for flow orchestration

    main

    When defining a flow, you use different methods to specify how steps behave and how they process data:

    • .step(): Defines a Regular Step. This is the standard building block for sequential or dependent tasks.
    • .array(): Defines an Array-Returning Step. Use this when a step is designed to return a collection of items rather than a single value.
    • .map(): Defines Parallel Array Processing. This method is used to iterate over an array and execute a step for each element in parallel.

    By using these methods, pgflow automatically manages dependencies and parallel execution of independent steps.

  8. How Two-Phase Polling prevents race conditions

    main

    To prevent a race condition where a worker attempts to process a message before the corresponding step_tasks record exists in the database, pgflow uses a Two-Phase Polling mechanism involving the Worker and SQL Core.

    The Process:

    1. Phase 1: The Worker calls read_with_poll(). This reserves messages and returns a set of msg_ids.
    2. Phase 2: The Worker calls start_tasks(flow_slug, msg_ids, worker_id, queue_name). This creates the necessary step_tasks records and returns the task details.

    This ensures that by the time a worker is actively executing a task, the orchestration state is fully persisted in the SQL Core.

  9. How FlowRun and FlowStep work

    main

    The client uses two primary abstractions to manage workflow execution:

    • FlowRun: Represents a single execution instance of a workflow. It tracks the overall status (started, completed, failed), input, and final output. It also acts as a container for individual steps.
    • FlowStep: Represents an individual step within a FlowRun. Each step has its own lifecycle (created, started, completed, failed), timing metadata (started_at, completed_at), and specific output or error messages.
  10. Use the DSL layer to express user intent

    main

    The TypeScript DSL allows you to define complex workflows using type-safe method chaining. It translates high-level concepts (like mapping over an array) into Directed Acyclic Graph (DAG) primitives that the SQL Core can understand.

    Key Features:

    • Type-safe method chaining using .step(), .array(), and .map().
    • Full type inference across step dependencies.
    • Pattern recognition for complex workflows.

    Note: The DSL layer does not handle task execution, database state, or queue mechanics; it only defines the structure and intent of the flow.

    new Flow<Input>({ slug: "process_users" })
      .step({ slug: "fetch_users" }, fetchUsers)
      .array({ slug: "users_array" }, (input) => input.fetch_users)
      .map({ slug: "send_email" }, sendEmail)
  11. How layer interactions and contracts work

    main

    The layers interact through strict, one-way contracts to prevent abstraction leakage:

    1. DSL $\rightarrow$ SQL Core: The DSL translates TypeScript code into step definitions. The contract consists of step definitions containing step_type, dependencies, and options.
    2. SQL Core $\rightarrow$ Worker: The SQL Core spawns tasks when dependencies are met. The contract is a task object containing a handler function, input data, and retry configuration.
    3. Worker $\rightarrow$ SQL Core: The Worker executes the handler and reports the outcome. The contract is a task completion message containing either the output or an error.
  12. Compare pgflow vs Inngest for workflow orchestration

    main

    Choosing between pgflow and Inngest depends on your architectural requirements and existing infrastructure.

    pgflow

    • Best for: Supabase-native applications, minimal infrastructure requirements, and workflows where state needs to be directly queryable via SQL. It is ideal for parallel data processing and workflows with explicit input/output dependencies.
    • Architecture: Database-centric orchestration where PostgreSQL manages the execution DAG (Directed Acyclic Graph) based on dependencies.
    • Infrastructure: Zero additional infrastructure required if using Supabase.

    Inngest

    • Best for: Event-driven architectures, complex temporal workflows (using sleep or waitForEvent), and scenarios requiring a dedicated monitoring dashboard.
    • Architecture: Durable execution engine that orchestrates linear steps with state memoization.
    • Infrastructure: Requires a hosted service or a self-hosted instance.