{"id":33402333,"url":"https://github.com/floxy-project/floxy-pro","last_synced_at":"2026-03-04T15:01:36.454Z","repository":{"id":323562037,"uuid":"1084569410","full_name":"floxy-project/floxy-pro","owner":"floxy-project","description":"A Pro version of Core Floxy library.","archived":false,"fork":false,"pushed_at":"2025-11-26T16:00:43.000Z","size":2314,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-12T05:25:28.024Z","etag":null,"topics":["golang","saga","workflows"],"latest_commit_sha":null,"homepage":"http://floxy-project.github.io","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/floxy-project.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":"CLA.md"}},"created_at":"2025-10-27T21:24:45.000Z","updated_at":"2025-11-26T15:51:41.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/floxy-project/floxy-pro","commit_stats":null,"previous_names":["floxy-project/floxy-pro","rom8726/floxy-pro"],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/floxy-project/floxy-pro","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/floxy-project%2Ffloxy-pro","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/floxy-project%2Ffloxy-pro/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/floxy-project%2Ffloxy-pro/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/floxy-project%2Ffloxy-pro/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/floxy-project","download_url":"https://codeload.github.com/floxy-project/floxy-pro/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/floxy-project%2Ffloxy-pro/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30084685,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-04T13:22:36.021Z","status":"ssl_error","status_checked_at":"2026-03-04T13:20:45.750Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["golang","saga","workflows"],"created_at":"2025-11-23T15:00:28.249Z","updated_at":"2026-03-04T15:01:36.421Z","avatar_url":"https://github.com/floxy-project.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# floxy-pro\n\n![Go Version](https://img.shields.io/badge/Go-%3E=1.25-blue)\n[![Go Reference](https://pkg.go.dev/badge/github.com/rom8726/floxy-pro.svg)](https://pkg.go.dev/github.com/rom8726/floxy-pro)\n[![Go Report Card](https://goreportcard.com/badge/github.com/rom8726/floxy-pro)](https://goreportcard.com/report/github.com/rom8726/floxy-pro)\n[![Coverage Status](https://coveralls.io/repos/github/rom8726/floxy-pro/badge.svg?branch=main)](https://coveralls.io/github/rom8726/floxy-pro?branch=main)\n[![MIT License](https://img.shields.io/badge/license-MIT-green.svg)](./LICENSE)\n\n[![boosty-cozy](https://gideonwhite1029.github.io/badges/cozy-boosty_vector.svg)](https://boosty.to/dev-tools-hacker)\n\nA Go library for creating and executing workflows with a custom DSL. Implements the Saga pattern with orchestrator approach, providing transaction management and compensation capabilities.\n\n**This is the Pro version of the Floxy library**, which includes advanced features:\n\n- **Partitioned Tables**: Database schema redesigned with partitioned tables using PostgreSQL `pg_partman` extension for efficient management of large data volumes\n- **floxyctl**: CLI tool for running workflows with in-memory store or managing workflow instances (start/cancel/abort) stored in PostgreSQL\n- **floxyd**: Ready-to-use runtime daemon for continuous workflow processing with support for bash and HTTP handlers\n\nfloxy means \"flow\" + \"flux\" + \"tiny\".\n\n### ⚠ Disclaimer\n\nFloxy is an early-stage project.\nIt is under active development and not battle-tested yet.\nThe software is provided **“as is”**, without any warranties or guarantees of stability, correctness, or fitness for any particular purpose.\nBy using this project, you acknowledge that **all risks are your own**, and the authors cannot be held responsible for any damages, data loss, or failures resulting from its use.\n \n\u003e Join the discussion if you’re experimenting with Floxy or want to help test the engine in real-world scenarios.\n\n\u003cimg src=\"docs/floxy_logo.png\" width=\"500\"\u003e\n\n## Table of Contents\n\n- [Features](#features)\n- [Pro Version Features](#pro-version-features)\n  - [Partitioned Tables with pg_partman](#partitioned-tables-with-pg_partman)\n  - [floxyctl - CLI Tool](#floxyctl---cli-tool)\n  - [floxyd - Runtime Daemon](#floxyd---runtime-daemon)\n- [Ecosystem](#ecosystem)\n- [Why Floxy?](#why-floxy)\n  - [1. Lightweight, Not Heavyweight](#1-lightweight-not-heavyweight)\n  - [2. Pragmatic by Design](#2-pragmatic-by-design)\n  - [3. Embedded](#3-embedded)\n- [Quick Start](#quick-start)\n- [Examples](#examples)\n- [Integration Tests](#integration-tests)\n- [Database Migrations](#database-migrations)\n- [Dead Letter Queue](#dead-letter-queue-dlq)\n- [Known Issues](#known-issues)\n  - [Condition Steps in Forked Branches](#condition-steps-in-forked-branches)\n  - [Rollback for nested Fork/Join branches](#rollback-for-nested-fork-/-join-branches)\n- [Installation](#installation)\n- [Dependencies](#dependencies)\n\n## Features\n\n- **Workflow DSL**: Declarative workflow definition using Builder pattern\n- **Saga Pattern**: Orchestrator-based saga implementation with compensation\n- **Workflows Versioning**: Safe changing flows using versions\n- **Transaction Management**: Built-in transaction support with rollback capabilities\n- **Parallel Execution**: Fork/Join patterns for concurrent workflow steps with dynamic wait-for detection\n- **Error Handling**: Automatic retry mechanisms and failure compensation\n- **SavePoints**: Rollback to specific points in workflow execution\n- **Conditional branching** with Condition steps. Smart rollback for parallel flows with condition steps\n- **Human-in-the-loop**: Interactive workflow steps that pause execution for human decisions\n- **Cancel\\Abort**: Possibility to cancel workflow with rollback to the root step and immediate abort workflow\n- **Dead Letter Queue (DLQ)**: Two modes for error handling - Classic Saga with rollback/compensation or DLQ Mode with paused workflow and manual recovery\n- **Distributed Mode**: Microservices can register only their handlers; steps without local handlers are returned to queue for other services to process\n- **Priority Aging**: Prevents queue starvation by gradually increasing step priority as waiting time increases\n- **PostgreSQL Storage**: Persistent workflow state and event logging\n- **Migrations**: Embedded database migrations with `go:embed`\n\nPlantUML diagrams of compensations flow: [DIAGRAMS](docs/SAGA_COMPENSATION_DIAGRAMS.md)\n\nEngine specification: [ENGINE](docs/ENGINE_SPEC.md)\n\n## Pro Version Features\n\n### Partitioned Tables with pg_partman\n\nThe Pro version uses partitioned tables managed by PostgreSQL's `pg_partman` extension for efficient data management at scale. All high-volume tables (`workflow_instances`, `workflow_steps`, `workflow_events`, `workflow_dlq`) are partitioned by `created_at` with daily partitions.\n\n**Key benefits:**\n- **Automatic partition management**: `pg_partman` automatically creates new partitions (30 days ahead) and removes old ones (90 days retention)\n- **Improved query performance**: Queries can leverage partition pruning for faster execution\n- **Easier maintenance**: Old data can be dropped by dropping partitions instead of deleting rows\n- **Scalability**: Handles millions of workflow instances and steps efficiently\n\n**Partition configuration:**\n- Partition interval: 1 day\n- Premake: 30 partitions ahead\n- Retention: 90 days (automatic cleanup)\n- Partition key: `created_at` timestamp\n\nThe partitioned schema is defined in `migrations_pro/001_initial.up.sql` and requires the `pg_partman` extension to be installed in PostgreSQL.\n\n### floxyctl - CLI Tool\n\n`floxyctl` is a command-line tool for running and managing workflows. It supports two modes of operation:\n\n#### 1. Run Mode (In-Memory Store)\n\nExecute workflows directly from YAML files using an in-memory store. Perfect for testing, development, and one-off workflow executions.\n\n**Commands:**\n- `floxyctl run -f workflow.yaml [-i input.json]` - Run workflow from YAML file\n\n**Features:**\n- Runs workflow to completion synchronously\n- Uses in-memory store (no database required)\n- Supports bash script handlers (inline or file-based)\n- Configurable worker pool and timeouts\n- Debug mode for inspecting handler input/output\n\n**Example:**\n```bash\n# Run workflow with input file\nfloxyctl run -f workflow.yaml -i input.json\n\n# Run with input from stdin\necho '{\"key\": \"value\"}' | floxyctl run -f workflow.yaml\n\n# Run with custom worker settings\nfloxyctl run -f workflow.yaml -w 5 --worker-interval 50ms --completion-timeout 5m\n```\n\n#### 2. Database Mode (PostgreSQL)\n\nManage workflow instances stored in PostgreSQL database. Requires database connection parameters.\n\n**Commands:**\n- `floxyctl start -o workflow-id [--host HOST --port PORT --user USER --database DB]` - Start new workflow instance\n- `floxyctl cancel -o instance-id [--host HOST --port PORT --user USER --database DB]` - Cancel workflow with rollback\n- `floxyctl abort -o instance-id [--host HOST --port PORT --user USER --database DB]` - Abort workflow without rollback\n\n**Features:**\n- Start workflow instances from registered workflow definitions\n- Cancel workflows with automatic rollback to root step\n- Abort workflows immediately without rollback\n- Password can be provided via `-W` flag (prompt) or `PG_PASSWORD` environment variable\n- Automatically runs database migrations on connection\n\n**Example:**\n```bash\n# Start workflow instance\nfloxyctl start -o my-workflow-v1 \\\n  --host localhost --port 5432 --user floxy --database floxy \\\n  -i input.json -W\n\n# Cancel workflow (with rollback)\nfloxyctl cancel -o 123 \\\n  --host localhost --port 5432 --user floxy --database floxy \\\n  --reason \"User requested cancellation\" -W\n\n# Abort workflow (without rollback)\nfloxyctl abort -o 123 \\\n  --host localhost --port 5432 --user floxy --database floxy \\\n  --reason \"Critical error\" -W\n```\n\n**Handler Support:**\n- Bash script handlers (inline scripts or file paths)\n- HTTP endpoint handlers (automatically detected by `http://` or `https://` prefix)\n- TLS configuration for secure HTTP handlers\n- Debug mode for troubleshooting\n\n### floxyd - Runtime Daemon\n\n`floxyd` is a daemon service that continuously processes workflows stored in PostgreSQL. It's designed to run as a long-running service with multiple workers.\n\n**Key Features:**\n- **Continuous Processing**: Long-running workers poll database for pending steps\n- **YAML Configuration**: Loads handlers and workflow definitions from YAML file\n- **Multiple Handler Types**: Supports both bash scripts and HTTP endpoints\n- **TLS Support**: Configurable TLS for secure HTTP handlers (global and per-handler)\n- **Worker Pool**: Configurable number of workers and polling intervals\n- **Statistics**: Real-time workflow statistics printed every 10 seconds\n- **Tech Server**: Built-in HTTP server with Prometheus metrics and health check endpoints\n- **Graceful Shutdown**: Handles SIGINT/SIGTERM signals cleanly\n\n**Configuration:**\n\nEnvironment variables:\n- `FLOXY_DB_HOST` - Database host (required)\n- `FLOXY_DB_PORT` - Database port (required)\n- `FLOXY_DB_USER` - Database user (required)\n- `FLOXY_DB_PASSWORD` - Database password (optional)\n- `FLOXY_DB_NAME` - Database name (required)\n- `FLOXY_WORKERS` - Number of workers (default: 3)\n- `FLOXY_WORKER_INTERVAL` - Worker polling interval (default: \"100ms\")\n\n**YAML Configuration:**\n\n```yaml\ntls:\n  skip_verify: false\n  cert_file: /path/to/cert.pem\n  key_file: /path/to/key.pem\n  ca_file: /path/to/ca.pem\n\nhandlers:\n  - name: bash_handler\n    exec: |\n      echo \"$INPUT\" | jq '.value * 2'\n  \n  - name: script_handler\n    exec: ./scripts/process.sh\n  \n  - name: http_handler\n    exec: https://api.example.com/process\n    tls:\n      skip_verify: true\n\nflows:\n  - name: my_workflow\n    steps:\n      - name: step1\n        handler: bash_handler\n      - name: step2\n        handler: http_handler\n```\n\n**Usage:**\n```bash\n# Set environment variables\nexport FLOXY_DB_HOST=localhost\nexport FLOXY_DB_PORT=5432\nexport FLOXY_DB_USER=floxy\nexport FLOXY_DB_PASSWORD=password\nexport FLOXY_DB_NAME=floxy\nexport FLOXY_WORKERS=5\n\n# Run floxyd\n./floxyd handlers.yaml\n```\n\n**Tech Server Endpoints:**\n- `http://localhost:8081/metrics` - Prometheus metrics\n- `http://localhost:8081/health` - Health check (checks database connection)\n\n**Workflow Registration:**\nOn startup, `floxyd`:\n1. Parses YAML file for workflow definitions (`flows` section)\n2. Registers each workflow in the database (version 1 by default)\n3. Logs each registered workflow with its ID and version\n\n**Handler Types:**\n\n**Bash Handlers:**\n- Inline scripts or file paths\n- Receive JSON input via `$INPUT` environment variable\n- Input fields available as uppercase environment variables\n- Workflow context via `FLOXY_*` environment variables\n- Must output valid JSON to stdout\n\n**HTTP Handlers:**\n- Automatically detected by `http://` or `https://` prefix\n- POST requests with JSON body containing `metadata` and `data` fields\n- Custom headers: `X-Floxy-Instance-ID`, `X-Floxy-Step-Name`, `X-Floxy-Idempotency-Key`, `X-Floxy-Retry-Count`\n- TLS configuration support (client certificates, CA certificates, skip verify)\n\n**Statistics Output:**\nEvery 10 seconds, `floxyd` prints:\n- Total, completed, failed, running, pending, and active workflow counts\n- Active workflow instances with details (ID, workflow name, status, current step, progress, runtime)\n\n**Differences from floxyctl:**\n\n| Feature | floxyctl | floxyd |\n|---------|----------|--------|\n| **Mode** | CLI tool | Daemon service |\n| **Storage** | In-memory (run mode) or PostgreSQL | PostgreSQL only |\n| **Execution** | Runs workflow to completion | Continuous processing |\n| **Workers** | Temporary pool | Long-running workers |\n\n## Ecosystem\n\n - **Web UI** for visualizing and managing workflows: [Web UI](https://github.com/rom8726/floxy-ui)\n - **Web Manager**: advanced Web UI with SSO, RBAC, etc.: [Floxy Manager](https://github.com/floxy-project/floxy-manager)\n - **GoLand Plugin**: [plugin](https://github.com/rom8726/floxy-goland-plugin)\n - **VS Code Extension**: [extension](https://github.com/rom8726/floxy-vsc-plugin)\n\n## Why Floxy?\n\n**Floxy** is a lightweight, embeddable workflow engine for Go developers.\nIt was born from the idea that *not every system needs a full-blown workflow platform like Cadence or Temporal.*\n\n### 1. Lightweight, Not Heavyweight\n\nMost workflow engines require you to deploy multiple services, brokers, and databases just to run a single flow.\nFloxy is different — it’s a **Go library**.\nYou import it, initialize an engine, and define your workflow directly in Go code.\n\nNo clusters. No queues.\n\n```go\nwf, _ := floxy.NewBuilder(\"order\", 1).\n    Step(\"reserve_stock\", \"stock.Reserve\").\n    Then(\"charge_payment\", \"payment.Charge\").\n    OnFailure(\"refund\", \"payment.Refund\").\n    Build()\n```\n\nThat’s it — a complete Saga with compensation.\n\n### 2. Pragmatic by Design\n\nFloxy doesn’t try to solve every problem in distributed systems.\nIt focuses on **clear, deterministic workflow execution** with the tools Go developers already use:\n\n* PostgreSQL as durable storage\n* Go’s standard `net/http` for API\n* Structured retries, compensation, and rollback\n\nYou don’t need to learn Cadence’s terminology.\nEverything is plain Go — just like your codebase.\n\n### 3. Embedded\n\nYou can embed Floxy inside any Go service.\n\n\u003e Floxy is for developers who love Go, simplicity, and control.\nNo orchestration clusters. No external DSLs.\nJust workflows — defined in Go, executed anywhere.\n\n## Quick Start\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"encoding/json\"\n    \"log\"\n    \n    \"github.com/jackc/pgx/v5/pgxpool\"\n\tfloxy \"github.com/rom8726/floxy-pro\"\n)\n\nfunc main() {\n    ctx := context.Background()\n    \n    // Connect to PostgreSQL\n    pool, err := pgxpool.New(ctx, \"postgres://floxy:password@localhost:5432/floxy?sslmode=disable\")\n    if err != nil {\n        log.Fatal(err)\n    }\n    defer pool.Close()\n    \n    // Run database migrations\n    if err := floxy.RunMigrations(ctx, pool); err != nil {\n        log.Fatal(err)\n    }\n    \n    // Create engine\n    engine := floxy.NewEngine(pool)\n\tdefer engine.Shutdown()\n    \n    // Register step handlers\n    engine.RegisterHandler(\u0026PaymentHandler{})\n    engine.RegisterHandler(\u0026InventoryHandler{})\n    engine.RegisterHandler(\u0026ShippingHandler{})\n    engine.RegisterHandler(\u0026CompensationHandler{})\n    \n    // Define workflow using Builder DSL\n    workflow, err := floxy.NewBuilder(\"order-processing\", 1, floxy.WithDLQEnabled(true)).\n        Step(\"process-payment\", \"payment\", floxy.WithStepMaxRetries(3)).\n        OnFailure(\"refund-payment\", \"compensation\").\n        SavePoint(\"payment-checkpoint\").\n        Then(\"reserve-inventory\", \"inventory\", floxy.WithStepMaxRetries(2)).\n        OnFailure(\"release-inventory\", \"compensation\").\n        Then(\"ship-order\", \"shipping\").\n        OnFailure(\"cancel-shipment\", \"compensation\").\n        Build()\n    if err != nil {\n        log.Fatal(err)\n    }\n    \n    // Register and start workflow\n    if err := engine.RegisterWorkflow(ctx, workflow); err != nil {\n        log.Fatal(err)\n    }\n    \n    order := map[string]any{\n        \"user_id\": \"user123\",\n        \"amount\":  100.0,\n        \"items\":   []string{\"item1\", \"item2\"},\n    }\n    input, _ := json.Marshal(order)\n    \n    instanceID, err := engine.Start(ctx, \"order-processing-v1\", input)\n    if err != nil {\n        log.Fatal(err)\n    }\n    \n    log.Printf(\"Workflow started: %d\", instanceID)\n    \n    // Process workflow steps\n    for {\n        empty, err := engine.ExecuteNext(ctx, \"worker1\")\n        if err != nil {\n            log.Printf(\"ExecuteNext error: %v\", err)\n        }\n        if empty {\n            break\n        }\n    }\n    \n    // Check final status\n    status, err := engine.GetStatus(ctx, instanceID)\n    if err != nil {\n        log.Fatal(err)\n    }\n    log.Printf(\"Workflow status: %s\", status)\n}\n\n// Step handlers\ntype PaymentHandler struct{}\nfunc (h *PaymentHandler) Name() string { return \"payment\" }\nfunc (h *PaymentHandler) Execute(ctx context.Context, stepCtx floxy.StepContext, input json.RawMessage) (json.RawMessage, error) {\n    // Process payment logic\n    return json.Marshal(map[string]any{\"status\": \"paid\"})\n}\n\ntype InventoryHandler struct{}\nfunc (h *InventoryHandler) Name() string { return \"inventory\" }\nfunc (h *InventoryHandler) Execute(ctx context.Context, stepCtx floxy.StepContext, input json.RawMessage) (json.RawMessage, error) {\n    // Reserve inventory logic\n    return json.Marshal(map[string]any{\"status\": \"reserved\"})\n}\n\ntype ShippingHandler struct{}\nfunc (h *ShippingHandler) Name() string { return \"shipping\" }\nfunc (h *ShippingHandler) Execute(ctx context.Context, stepCtx floxy.StepContext, input json.RawMessage) (json.RawMessage, error) {\n    // Ship order logic\n    return json.Marshal(map[string]any{\"status\": \"shipped\"})\n}\n\ntype CompensationHandler struct{}\nfunc (h *CompensationHandler) Name() string { return \"compensation\" }\nfunc (h *CompensationHandler) Execute(ctx context.Context, stepCtx floxy.StepContext, input json.RawMessage) (json.RawMessage, error) {\n    // Compensation logic\n    return json.Marshal(map[string]any{\"status\": \"compensated\"})\n}\n```\n\n## Examples\n\nSee the `examples/` directory for complete workflow examples:\n\n- **Hello World**: Basic single-step workflow\n- **E-commerce**: Order processing with compensation flows\n- **Data Pipeline**: Parallel data processing with Fork/Join patterns\n- **Microservices**: Complex service orchestration with multiple branches\n- **SavePoint Demo**: Demonstrates SavePoint functionality and rollback\n- **Rollback Demo**: Shows full rollback mechanism with OnFailure handlers\n- **Human-in-the-loop**: Interactive workflows with human decision points\n\nRun examples:\n```bash\n# Start PostgreSQL (required for examples)\nmake dev-up\n\n# Run all examples\ncd examples/hello_world \u0026\u0026 go run main.go\ncd examples/ecommerce \u0026\u0026 go run main.go\ncd examples/data_pipeline \u0026\u0026 go run main.go\ncd examples/microservices \u0026\u0026 go run main.go\ncd examples/savepoint_demo \u0026\u0026 go run main.go\ncd examples/rollback_demo \u0026\u0026 go run main.go\ncd examples/human_in_the_loop__approved \u0026\u0026 go run main.go\ncd examples/human_in_the_loop__rejected \u0026\u0026 go run main.go\n```\n\n## Integration Tests\n\nThe library includes comprehensive integration tests using testcontainers:\n\n```bash\n# Run all tests\ngo test ./...\n\n# Run only integration tests\ngo test -v -run TestIntegration\n\n# Run specific integration test\ngo test -v -run TestIntegration_DataPipeline\n```\n\nIntegration tests cover:\n- **Data Pipeline**: Parallel data processing with multiple sources\n- **E-commerce**: Order processing with success/failure scenarios\n- **Microservices**: Complex orchestration with multiple service calls\n- **SavePoint Demo**: SavePoint functionality with conditional failures\n- **Rollback Demo**: Full rollback mechanism testing\n- **Human-in-the-loop**: Make decisions (confirm/reject)\n\n## Database Migrations\n\nThe library includes embedded database migrations using `go:embed`. Migrations are automatically applied when using `floxy.RunMigrations()`:\n\n```go\n// Run migrations\nif err := floxy.RunMigrations(ctx, pool); err != nil {\n    log.Fatal(err)\n}\n```\n\n### Pro Version Migrations\n\nThe Pro version uses partitioned tables managed by `pg_partman`. The migrations are located in `migrations_pro/`:\n\n- `001_initial.up.sql`: Initial schema with partitioned tables (`workflow_instances`, `workflow_steps`, `workflow_events`, `workflow_dlq`) using `pg_partman`\n- `002_add_savepoint_and_rollback.up.sql`: SavePoint and rollback support\n- `003_add_compensation_retry_count.up.sql`: Compensation step status and compensation_retry_count added\n- `004_add_compensation_to_views.up.sql`: Active workflows view updated\n- `005_add_idempotency_key_to_steps.up.sql`: Idempotency Key added to step table\n- `006_add_human_in_the_loop_step.up.sql`: Human-in-the-loop step support and decision tracking\n- `007_add_workflow_cancel_requests_table.up.sql`: Cancel requests table\n- `008_add_dead_letter_queue.up.sql`: Dead Letter Queue for failed steps\n- `009_add_dlq_and_paused_statuses.up.sql`: DLQ and paused statuses support\n- `010_add_cleanup_function.up.sql`: Cleanup function for partitioned tables\n\n**Note**: The Pro version requires the `pg_partman` extension to be installed in PostgreSQL. The extension is automatically created in the `partman` schema during migration.\n\n## Dead Letter Queue (DLQ)\n\n### Overview\n\nFloxy supports two different error handling modes:\n\n1. **Classic Saga Mode** (default): When a step fails, the engine performs rollback to the last SavePoint and executes compensation handlers\n2. **DLQ Mode**: Rollback is disabled, the workflow pauses in `dlq` state, and failed steps are stored in DLQ for manual investigation\n\n### DLQ Mode Behavior\n\nWhen DLQ is enabled for a workflow:\n\n- **No Rollback**: Compensation handlers are **not** executed on failure\n- **Workflow Paused**: Instance status → `dlq` (not terminal, can be resumed)\n- **Active Steps Frozen**: All `running` steps are set to `paused` status\n- **Queue Cleared**: Instance queue is cleared to prevent further progress\n- **Manual Recovery**: After fixing issues, use `RequeueFromDLQ` to resume\n\n### Enabling DLQ Mode\n\nEnable DLQ for a workflow during definition:\n\n```go\nworkflow, err := floxy.NewBuilder(\"payment-processing\", 1, floxy.WithDLQEnabled(true)).\n    Step(\"validate-payment\", \"payment-validator\", floxy.WithStepMaxRetries(2)).\n    Then(\"process-payment\", \"payment-processor\", floxy.WithStepMaxRetries(3)).\n    Then(\"notify-user\", \"notification-service\", floxy.WithStepMaxRetries(1)).\n    Build()\n```\n\n### Fork/Join with DLQ\n\nWhen using Fork/Join with DLQ enabled:\n\n- **Parallel Branches**: Other branches continue to completion before workflow pauses\n- **Join Step**: Created as `paused` when all dependencies are met but the instance is in `dlq` state\n- **Requeue Behavior**: After requeuing the failed step, join transitions from `paused` → `pending` for automatic continuation\n\n### RequeueFromDLQ\n\nThe `RequeueFromDLQ` method restores a failed step from DLQ and resumes workflow execution:\n\n```go\n// Requeue with original input\nerr := engine.RequeueFromDLQ(ctx, dlqID, nil)\n\n// Requeue with modified input\nnewInput := json.RawMessage(`{\"status\": \"fixed\", \"data\": \"corrected\"}`)\nerr := engine.RequeueFromDLQ(ctx, dlqID, \u0026newInput)\n```\n\n**What happens during requeue:**\n1. Instance status: `dlq` → `running`\n2. Failed step: `paused` → `pending` (retry counters reset)\n3. Input updated if `newInput` provided\n4. Step enqueued for execution\n5. Join steps: `paused` → `pending`\n6. DLQ record deleted\n\n### Use Cases for DLQ Mode\n\n- **Manual Data Review**: Steps that require human inspection before retry\n- **External Service Outages**: When downstream services are temporarily unavailable\n- **Data Quality Issues**: Malformed data requiring manual correction\n- **Complex Debugging**: When failures need detailed investigation\n- **Business Approval Workflows**: Where failures should pause for review rather than auto-rollback\n\n### Example: Payment Processing with DLQ\n\n```go\n// Define workflow with DLQ enabled\nworkflow, err := floxy.NewBuilder(\"payment-processing\", 1, floxy.WithDLQEnabled(true)).\n    Step(\"validate-payment\", \"payment-validator\", floxy.WithStepMaxRetries(2)).\n    Then(\"process-payment\", \"payment-processor\", floxy.WithStepMaxRetries(3)).\n    Then(\"notify-user\", \"notification-service\", floxy.WithStepMaxRetries(1)).\n    Build()\n\n// Register and start workflow\nerr = engine.RegisterWorkflow(ctx, workflow)\ninstanceID, err := engine.Start(ctx, \"payment-processing-v1\", input)\n\n// Process workflow - if step fails, workflow goes to dlq state\nfor {\n    empty, err := engine.ExecuteNext(ctx, \"worker1\")\n    if empty || err != nil {\n        break\n    }\n}\n\n// Later: investigate failure in DLQ, fix issue, then requeue\nnewInput := json.RawMessage(`{\"payment_id\": \"corrected-id\", \"amount\": 100.0}`)\nerr = engine.RequeueFromDLQ(ctx, dlqID, \u0026newInput)\n\n// Workflow resumes from where it paused\n```\n\n## Known Issues\n\n### Condition Steps in Forked Branches\n\nWhen using `Condition` steps within `Fork` branches, the `JoinStep` step may not wait for all dynamically created steps (like `else` branches) to complete before considering the workflow finished. This can lead to premature workflow completion.\n\n**Example of problematic case:**\n```go\nFork(\"parallel_branch\", func(branch1 *floxy.Builder) {\n    branch1.Step(\"branch1_step1\", \"handler\").\n        Condition(\"branch1_condition\", \"{{ gt .count 5 }}\", func(elseBranch *floxy.Builder) {\n            elseBranch.Step(\"branch1_else\", \"handler\") // This step might not be waited for\n        }).\n        Then(\"branch1_next\", \"handler\")\n}, func(branch2 *floxy.Builder) {\n    branch2.Step(\"branch2_step1\", \"handler\").\n        Condition(\"branch2_condition\", \"{{ lt .count 3 }}\", func(elseBranch *floxy.Builder) {\n            elseBranch.Step(\"branch2_else\", \"handler\") // This step might not be waited for\n        }).\n        Then(\"branch2_next\", \"handler\")\n}).\nJoinStep(\"join\", []string{\"branch1_step1\", \"branch2_step1\"}, floxy.JoinStrategyAll)\n```\n\n**SOLVED**: Avoid using `JoinStep` with `Condition`, use `Join` instead that dynamically creates waitFor list (virtual steps conception used).\n\nSee `examples/condition/main.go` for a demonstration of this issue.\n\n### Rollback for nested Fork/Join branches\n\n**SOLVED**: Nested `Fork`/`Join` branches are fully supported for rollback. The engine uses a `visited` map to prevent double rollback of steps that may be reached through multiple paths in nested fork/join structures.\n\n## Installation\n\n```bash\ngo get github.com/rom8726/floxy\n```\n\n## Dependencies\n\n- PostgreSQL database (with `pg_partman` extension for Pro version)\n- Go 1.25+\n\n**Pro Version Requirements:**\n- PostgreSQL with `pg_partman` extension installed\n- The extension is automatically created during migration, but PostgreSQL must have the extension available\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffloxy-project%2Ffloxy-pro","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffloxy-project%2Ffloxy-pro","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffloxy-project%2Ffloxy-pro/lists"}