pgwerk 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. pgwerk-0.1.0/.github/workflows/ci.yml +72 -0
  2. pgwerk-0.1.0/.github/workflows/docs.yml +31 -0
  3. pgwerk-0.1.0/.github/workflows/release.yml +49 -0
  4. pgwerk-0.1.0/.gitignore +26 -0
  5. pgwerk-0.1.0/CHANGELOG.md +38 -0
  6. pgwerk-0.1.0/CLAUDE.md +68 -0
  7. pgwerk-0.1.0/CONTRIBUTING.md +83 -0
  8. pgwerk-0.1.0/LICENSE +21 -0
  9. pgwerk-0.1.0/Makefile +59 -0
  10. pgwerk-0.1.0/PKG-INFO +279 -0
  11. pgwerk-0.1.0/README.md +209 -0
  12. pgwerk-0.1.0/dashboard/components.json +21 -0
  13. pgwerk-0.1.0/dashboard/index.html +16 -0
  14. pgwerk-0.1.0/dashboard/package.json +43 -0
  15. pgwerk-0.1.0/dashboard/pnpm-lock.yaml +3057 -0
  16. pgwerk-0.1.0/dashboard/postcss.config.js +6 -0
  17. pgwerk-0.1.0/dashboard/public/favicon.svg +9 -0
  18. pgwerk-0.1.0/dashboard/src/App.tsx +42 -0
  19. pgwerk-0.1.0/dashboard/src/components/StatusBadge.tsx +51 -0
  20. pgwerk-0.1.0/dashboard/src/components/cron/TriggerCronDialog.tsx +83 -0
  21. pgwerk-0.1.0/dashboard/src/components/jobs/ExecutionHistory.tsx +56 -0
  22. pgwerk-0.1.0/dashboard/src/components/jobs/JobDetail.tsx +181 -0
  23. pgwerk-0.1.0/dashboard/src/components/jobs/JobFilters.tsx +54 -0
  24. pgwerk-0.1.0/dashboard/src/components/jobs/JobsTable.tsx +127 -0
  25. pgwerk-0.1.0/dashboard/src/components/jobs/SendJobDialog.tsx +148 -0
  26. pgwerk-0.1.0/dashboard/src/components/layout/Header.tsx +29 -0
  27. pgwerk-0.1.0/dashboard/src/components/layout/Logo.tsx +32 -0
  28. pgwerk-0.1.0/dashboard/src/components/layout/Sidebar.tsx +52 -0
  29. pgwerk-0.1.0/dashboard/src/components/layout/TopNav.tsx +133 -0
  30. pgwerk-0.1.0/dashboard/src/components/overview/JobStatusPieChart.tsx +83 -0
  31. pgwerk-0.1.0/dashboard/src/components/overview/QueueDepthChart.tsx +79 -0
  32. pgwerk-0.1.0/dashboard/src/components/overview/QueueTable.tsx +82 -0
  33. pgwerk-0.1.0/dashboard/src/components/overview/StatCards.tsx +81 -0
  34. pgwerk-0.1.0/dashboard/src/components/overview/ThroughputChart.tsx +125 -0
  35. pgwerk-0.1.0/dashboard/src/components/ui/badge.tsx +36 -0
  36. pgwerk-0.1.0/dashboard/src/components/ui/button.tsx +56 -0
  37. pgwerk-0.1.0/dashboard/src/components/ui/card.tsx +79 -0
  38. pgwerk-0.1.0/dashboard/src/components/ui/dialog.tsx +120 -0
  39. pgwerk-0.1.0/dashboard/src/components/ui/dropdown-menu.tsx +198 -0
  40. pgwerk-0.1.0/dashboard/src/components/ui/select.tsx +160 -0
  41. pgwerk-0.1.0/dashboard/src/components/ui/separator.tsx +29 -0
  42. pgwerk-0.1.0/dashboard/src/components/ui/sheet.tsx +140 -0
  43. pgwerk-0.1.0/dashboard/src/components/ui/skeleton.tsx +15 -0
  44. pgwerk-0.1.0/dashboard/src/components/ui/table.tsx +117 -0
  45. pgwerk-0.1.0/dashboard/src/components/ui/tabs.tsx +53 -0
  46. pgwerk-0.1.0/dashboard/src/components/ui/tooltip.tsx +30 -0
  47. pgwerk-0.1.0/dashboard/src/components/workers/WorkerCard.tsx +106 -0
  48. pgwerk-0.1.0/dashboard/src/components/workers/WorkersList.tsx +80 -0
  49. pgwerk-0.1.0/dashboard/src/hooks/useTheme.ts +21 -0
  50. pgwerk-0.1.0/dashboard/src/hooks/useWorkerHistory.ts +34 -0
  51. pgwerk-0.1.0/dashboard/src/index.css +65 -0
  52. pgwerk-0.1.0/dashboard/src/lib/api.ts +123 -0
  53. pgwerk-0.1.0/dashboard/src/lib/utils.ts +51 -0
  54. pgwerk-0.1.0/dashboard/src/main.tsx +10 -0
  55. pgwerk-0.1.0/dashboard/src/pages/CronPage.tsx +104 -0
  56. pgwerk-0.1.0/dashboard/src/pages/JobsPage.tsx +137 -0
  57. pgwerk-0.1.0/dashboard/src/pages/MaintenancePage.tsx +524 -0
  58. pgwerk-0.1.0/dashboard/src/pages/OverviewPage.tsx +237 -0
  59. pgwerk-0.1.0/dashboard/src/pages/WorkerDetailPage.tsx +232 -0
  60. pgwerk-0.1.0/dashboard/src/pages/WorkersPage.tsx +35 -0
  61. pgwerk-0.1.0/dashboard/src/types.ts +142 -0
  62. pgwerk-0.1.0/dashboard/src/vite-env.d.ts +1 -0
  63. pgwerk-0.1.0/dashboard/tailwind.config.js +73 -0
  64. pgwerk-0.1.0/dashboard/tsconfig.app.json +23 -0
  65. pgwerk-0.1.0/dashboard/tsconfig.json +7 -0
  66. pgwerk-0.1.0/dashboard/tsconfig.node.json +16 -0
  67. pgwerk-0.1.0/dashboard/vite.config.ts +25 -0
  68. pgwerk-0.1.0/docs/assets/logo.svg +9 -0
  69. pgwerk-0.1.0/docs/changelog.md +1 -0
  70. pgwerk-0.1.0/docs/guide/cli.md +146 -0
  71. pgwerk-0.1.0/docs/guide/cron.md +137 -0
  72. pgwerk-0.1.0/docs/guide/dashboard.md +42 -0
  73. pgwerk-0.1.0/docs/guide/enqueueing.md +220 -0
  74. pgwerk-0.1.0/docs/guide/quickstart.md +113 -0
  75. pgwerk-0.1.0/docs/guide/serializers.md +68 -0
  76. pgwerk-0.1.0/docs/guide/workers.md +158 -0
  77. pgwerk-0.1.0/docs/index.md +34 -0
  78. pgwerk-0.1.0/docs/reference/app.md +222 -0
  79. pgwerk-0.1.0/docs/reference/exceptions.md +80 -0
  80. pgwerk-0.1.0/docs/reference/schemas.md +252 -0
  81. pgwerk-0.1.0/docs/reference/workers.md +120 -0
  82. pgwerk-0.1.0/example/__init__.py +15 -0
  83. pgwerk-0.1.0/example/producer.py +211 -0
  84. pgwerk-0.1.0/example/tasks.py +94 -0
  85. pgwerk-0.1.0/example/worker.py +36 -0
  86. pgwerk-0.1.0/justfile +66 -0
  87. pgwerk-0.1.0/mkdocs.yml +36 -0
  88. pgwerk-0.1.0/pgwerk/__init__.py +72 -0
  89. pgwerk-0.1.0/pgwerk/api/__init__.py +4 -0
  90. pgwerk-0.1.0/pgwerk/api/app.py +148 -0
  91. pgwerk-0.1.0/pgwerk/api/models.py +458 -0
  92. pgwerk-0.1.0/pgwerk/api/routes.py +543 -0
  93. pgwerk-0.1.0/pgwerk/app.py +1071 -0
  94. pgwerk-0.1.0/pgwerk/cli/__init__.py +35 -0
  95. pgwerk-0.1.0/pgwerk/cli/api.py +77 -0
  96. pgwerk-0.1.0/pgwerk/cli/cron.py +39 -0
  97. pgwerk-0.1.0/pgwerk/cli/dashboard.py +160 -0
  98. pgwerk-0.1.0/pgwerk/cli/info.py +58 -0
  99. pgwerk-0.1.0/pgwerk/cli/jobs.py +116 -0
  100. pgwerk-0.1.0/pgwerk/cli/purge.py +41 -0
  101. pgwerk-0.1.0/pgwerk/cli/slowest.py +103 -0
  102. pgwerk-0.1.0/pgwerk/cli/stats.py +137 -0
  103. pgwerk-0.1.0/pgwerk/cli/throughput.py +98 -0
  104. pgwerk-0.1.0/pgwerk/cli/utils.py +103 -0
  105. pgwerk-0.1.0/pgwerk/cli/worker.py +55 -0
  106. pgwerk-0.1.0/pgwerk/commons.py +41 -0
  107. pgwerk-0.1.0/pgwerk/config.py +76 -0
  108. pgwerk-0.1.0/pgwerk/cron.py +247 -0
  109. pgwerk-0.1.0/pgwerk/database.py +289 -0
  110. pgwerk-0.1.0/pgwerk/exceptions.py +26 -0
  111. pgwerk-0.1.0/pgwerk/exporter/__init__.py +256 -0
  112. pgwerk-0.1.0/pgwerk/logging.py +132 -0
  113. pgwerk-0.1.0/pgwerk/py.typed +0 -0
  114. pgwerk-0.1.0/pgwerk/repos.py +1168 -0
  115. pgwerk-0.1.0/pgwerk/schemas.py +497 -0
  116. pgwerk-0.1.0/pgwerk/serializers.py +166 -0
  117. pgwerk-0.1.0/pgwerk/utils.py +245 -0
  118. pgwerk-0.1.0/pgwerk/worker/__init__.py +14 -0
  119. pgwerk-0.1.0/pgwerk/worker/aio.py +72 -0
  120. pgwerk-0.1.0/pgwerk/worker/base.py +642 -0
  121. pgwerk-0.1.0/pgwerk/worker/fork.py +138 -0
  122. pgwerk-0.1.0/pgwerk/worker/process.py +81 -0
  123. pgwerk-0.1.0/pgwerk/worker/thread.py +76 -0
  124. pgwerk-0.1.0/pyproject.toml +110 -0
  125. pgwerk-0.1.0/static/logo-dark.svg +7 -0
  126. pgwerk-0.1.0/static/logo-light.svg +7 -0
  127. pgwerk-0.1.0/tests/__init__.py +0 -0
  128. pgwerk-0.1.0/tests/integration/__init__.py +0 -0
  129. pgwerk-0.1.0/tests/integration/conftest.py +72 -0
  130. pgwerk-0.1.0/tests/integration/tasks.py +241 -0
  131. pgwerk-0.1.0/tests/integration/test_api_smoke.py +66 -0
  132. pgwerk-0.1.0/tests/integration/test_apply.py +39 -0
  133. pgwerk-0.1.0/tests/integration/test_before_after_process_hooks.py +86 -0
  134. pgwerk-0.1.0/tests/integration/test_concurrency.py +67 -0
  135. pgwerk-0.1.0/tests/integration/test_concurrency_limit.py +53 -0
  136. pgwerk-0.1.0/tests/integration/test_dependencies.py +222 -0
  137. pgwerk-0.1.0/tests/integration/test_enqueue.py +98 -0
  138. pgwerk-0.1.0/tests/integration/test_enqueue_many.py +101 -0
  139. pgwerk-0.1.0/tests/integration/test_expired_jobs.py +61 -0
  140. pgwerk-0.1.0/tests/integration/test_failure_mode.py +52 -0
  141. pgwerk-0.1.0/tests/integration/test_fork_worker.py +86 -0
  142. pgwerk-0.1.0/tests/integration/test_get_executions.py +31 -0
  143. pgwerk-0.1.0/tests/integration/test_get_job.py +26 -0
  144. pgwerk-0.1.0/tests/integration/test_group_key.py +100 -0
  145. pgwerk-0.1.0/tests/integration/test_job_heartbeat.py +92 -0
  146. pgwerk-0.1.0/tests/integration/test_job_termination.py +85 -0
  147. pgwerk-0.1.0/tests/integration/test_lifecycle_hooks.py +69 -0
  148. pgwerk-0.1.0/tests/integration/test_list_jobs.py +79 -0
  149. pgwerk-0.1.0/tests/integration/test_map.py +57 -0
  150. pgwerk-0.1.0/tests/integration/test_process_worker.py +63 -0
  151. pgwerk-0.1.0/tests/integration/test_prod_paths.py +252 -0
  152. pgwerk-0.1.0/tests/integration/test_race_detection.py +97 -0
  153. pgwerk-0.1.0/tests/integration/test_robustness.py +82 -0
  154. pgwerk-0.1.0/tests/integration/test_sweep.py +178 -0
  155. pgwerk-0.1.0/tests/integration/test_thread_worker.py +71 -0
  156. pgwerk-0.1.0/tests/integration/test_unique_key.py +29 -0
  157. pgwerk-0.1.0/tests/integration/test_wait_for.py +51 -0
  158. pgwerk-0.1.0/tests/integration/test_worker_callbacks.py +42 -0
  159. pgwerk-0.1.0/tests/integration/test_worker_execution.py +101 -0
  160. pgwerk-0.1.0/tests/integration/test_worker_failure.py +41 -0
  161. pgwerk-0.1.0/tests/integration/test_worker_lifecycle.py +78 -0
  162. pgwerk-0.1.0/tests/integration/test_worker_repeat.py +36 -0
  163. pgwerk-0.1.0/tests/integration/test_worker_retry.py +184 -0
  164. pgwerk-0.1.0/tests/integration/test_wrk_init.py +30 -0
  165. pgwerk-0.1.0/tests/unit/__init__.py +0 -0
  166. pgwerk-0.1.0/tests/unit/test_app_helpers.py +386 -0
  167. pgwerk-0.1.0/tests/unit/test_cli.py +106 -0
  168. pgwerk-0.1.0/tests/unit/test_commons.py +201 -0
  169. pgwerk-0.1.0/tests/unit/test_cron.py +315 -0
  170. pgwerk-0.1.0/tests/unit/test_database.py +43 -0
  171. pgwerk-0.1.0/tests/unit/test_exceptions.py +80 -0
  172. pgwerk-0.1.0/tests/unit/test_job.py +183 -0
  173. pgwerk-0.1.0/tests/unit/test_schema_consistency.py +105 -0
  174. pgwerk-0.1.0/tests/unit/test_serializers.py +132 -0
  175. pgwerk-0.1.0/tests/unit/test_worker_aio.py +151 -0
  176. pgwerk-0.1.0/tests/unit/test_worker_base.py +297 -0
  177. pgwerk-0.1.0/tests/unit/test_worker_variants.py +196 -0
  178. pgwerk-0.1.0/uv.lock +1161 -0
@@ -0,0 +1,72 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths:
7
+ - ".github/workflows/ci.yml"
8
+ - "pgwerk/**"
9
+ - "tests/**"
10
+ - "pyproject.toml"
11
+ - "uv.lock"
12
+ - "README.md"
13
+ - "CHANGELOG.md"
14
+ - "LICENSE"
15
+ pull_request:
16
+ branches: [main]
17
+ paths:
18
+ - ".github/workflows/ci.yml"
19
+ - "pgwerk/**"
20
+ - "tests/**"
21
+ - "pyproject.toml"
22
+ - "uv.lock"
23
+ - "README.md"
24
+ - "CHANGELOG.md"
25
+ - "LICENSE"
26
+
27
+ jobs:
28
+ test:
29
+ name: Python ${{ matrix.python-version }} / Postgres ${{ matrix.postgres-version }}
30
+ runs-on: ubuntu-latest
31
+
32
+ strategy:
33
+ fail-fast: false
34
+ matrix:
35
+ python-version: ["3.12"]
36
+ postgres-version: ["16"]
37
+
38
+ services:
39
+ postgres:
40
+ image: postgres:${{ matrix.postgres-version }}
41
+ env:
42
+ POSTGRES_USER: pgwerk
43
+ POSTGRES_PASSWORD: pgwerk
44
+ POSTGRES_DB: pgwerk_test
45
+ ports:
46
+ - 5432:5432
47
+ options: >-
48
+ --health-cmd pg_isready
49
+ --health-interval 5s
50
+ --health-timeout 5s
51
+ --health-retries 10
52
+
53
+ steps:
54
+ - uses: actions/checkout@v4
55
+
56
+ - uses: astral-sh/setup-uv@v4
57
+ with:
58
+ python-version: ${{ matrix.python-version }}
59
+
60
+ - name: Install dependencies
61
+ run: uv sync --extra dev
62
+
63
+ - name: Lint
64
+ run: uv run ruff check .
65
+
66
+ - name: Unit tests
67
+ run: uv run pytest tests/unit/ -v --tb=short
68
+
69
+ - name: Integration tests
70
+ env:
71
+ PGWERK_TEST_DSN: postgresql://pgwerk:pgwerk@localhost/pgwerk_test
72
+ run: uv run pytest tests/integration/ -v --tb=short
@@ -0,0 +1,31 @@
1
+ name: Docs
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths:
7
+ - ".github/workflows/docs.yml"
8
+ - "docs/**"
9
+ - "mkdocs.yml"
10
+ - "README.md"
11
+ - "CHANGELOG.md"
12
+ - "docs/assets/**"
13
+
14
+ permissions:
15
+ contents: write
16
+
17
+ jobs:
18
+ deploy:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+
23
+ - uses: astral-sh/setup-uv@v4
24
+ with:
25
+ python-version: "3.12"
26
+
27
+ - name: Install dependencies
28
+ run: uv sync --extra dev
29
+
30
+ - name: Deploy
31
+ run: uv run mkdocs gh-deploy --force
@@ -0,0 +1,49 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ release:
10
+ runs-on: ubuntu-latest
11
+ environment: release
12
+ permissions:
13
+ contents: read
14
+
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ # ── Build dashboard ──────────────────────────────────────────────────
19
+ - uses: pnpm/action-setup@v4
20
+ with:
21
+ version: latest
22
+
23
+ - uses: actions/setup-node@v4
24
+ with:
25
+ node-version: "20"
26
+ cache: pnpm
27
+ cache-dependency-path: dashboard/pnpm-lock.yaml
28
+
29
+ - name: Install dashboard dependencies
30
+ run: pnpm install --frozen-lockfile
31
+ working-directory: dashboard
32
+
33
+ - name: Build dashboard
34
+ run: pnpm build
35
+ working-directory: dashboard
36
+ # outputs to pgwerk/api/static/ via vite.config.ts outDir
37
+
38
+ # ── Build and publish Python wheel ───────────────────────────────────
39
+ - uses: astral-sh/setup-uv@v4
40
+ with:
41
+ python-version: "3.12"
42
+
43
+ - name: Build wheel
44
+ run: uv build
45
+
46
+ - name: Publish to PyPI
47
+ uses: pypa/gh-action-pypi-publish@release/v1
48
+ with:
49
+ password: ${{ secrets.PYPI_TOKEN }}
@@ -0,0 +1,26 @@
1
+ .refs
2
+ .claude
3
+ .local
4
+ .internals
5
+ .env
6
+ .envrc
7
+ .venv
8
+ __pycache__
9
+ *.pyc
10
+ *.pyo
11
+ *.pyd
12
+ *.log
13
+ .coverage
14
+ .coverage.*
15
+ htmlcov/
16
+ .coverage
17
+ .vscode/
18
+ .pytest_cache/
19
+ node_modules/
20
+ dashboard/dist/
21
+ *.tsbuildinfo
22
+ dashboard/package-lock.json
23
+ pgwerk/api/static/*
24
+ !pgwerk/api/static/.gitkeep
25
+ site/
26
+ .DS_Store
@@ -0,0 +1,38 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented here.
4
+
5
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
+ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ---
9
+
10
+ ## [Unreleased]
11
+
12
+ ## [0.1.0] - 2026-04-22
13
+
14
+ ### Added
15
+
16
+ - `Werk` app class — connect/disconnect, enqueue, enqueue_many, get_job, get_executions, cancel_job, sweep
17
+ - `AsyncWorker`, `ThreadWorker`, `ProcessWorker`, `ForkWorker` — four concurrency models
18
+ - `LISTEN/NOTIFY` wake-up so workers react instantly to new jobs
19
+ - `SELECT … FOR UPDATE SKIP LOCKED` dequeue with Priority, RoundRobin, and Random strategies
20
+ - `Retry` — configurable max attempts and per-interval back-off delays
21
+ - `Repeat` — re-enqueue a job N more times after each successful run
22
+ - `Dependency` / DAG — jobs that wait for one or more upstream jobs to complete
23
+ - Idempotency keys (`_key`) — duplicate enqueues silently dropped
24
+ - Group keys (`_group`) — at most one active job per group at a time
25
+ - `_heartbeat` — worker auto-renews long-running jobs to prevent sweep reaping
26
+ - `CronScheduler` + `CronJob` — cron-expression and interval-based recurring jobs, with Postgres advisory lock so only one scheduler runs at a time
27
+ - `JSONSerializer` (default) and `PickleSerializer`
28
+ - Auto-migration on `connect()` with advisory lock to prevent races
29
+ - `before_process` / `after_process` hooks on workers
30
+ - `on_success` / `on_failure` / `on_stopped` callbacks per job
31
+ - `failure_mode=delete` — terminal failures remove the row instead of marking it failed
32
+ - `result_ttl` / `failure_ttl` — automatic expiry of completed / failed rows
33
+ - `burst` mode — worker exits once the queue drains
34
+ - `BaseWorker.push_exception_handler` / `pop_exception_handler` stack
35
+ - `werk` CLI — `worker`, `info`, `purge` sub-commands
36
+ - REST API (optional `litestar` extra) — job inspection and queue stats
37
+ - Prometheus metrics exporter (optional `prometheus-client` extra)
38
+ - `werk info` dashboard using `rich` + `plotext` (optional `analytics` extra)
pgwerk-0.1.0/CLAUDE.md ADDED
@@ -0,0 +1,68 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Overview
6
+
7
+ **wrk** is a Postgres-backed job queue library. It auto-migrates a shared schema on first connect using advisory locks to prevent races.
8
+
9
+ ## Commands
10
+
11
+ ### Python
12
+
13
+ ```bash
14
+ # Install (dev)
15
+ uv sync --extra dev
16
+
17
+ # Run all tests
18
+ uv run pytest
19
+
20
+ # Run a single test file
21
+ uv run pytest tests/unit/test_job.py
22
+
23
+ # Run integration tests (requires Postgres — see below)
24
+ PGWERK_TEST_DSN="postgresql://pgwerk:pgwerk@localhost/pgwerk_test" uv run pytest tests/integration/
25
+
26
+ # Lint
27
+ uv run ruff check .
28
+ uv run ruff format .
29
+ ```
30
+
31
+
32
+ ## Integration test database
33
+
34
+ Integration tests expect a Postgres instance at `postgresql://pgwerk:pgwerk@localhost/pgwerk_test` (override with `PGWERK_TEST_DSN`). Tables are auto-created by `Werk.connect()` / `app.connect()` and truncated between tests via the `app` fixture in `tests/integration/conftest.py`.
35
+
36
+ ## Architecture
37
+
38
+ ### Schema (both implementations)
39
+
40
+ Four tables, prefixed (default `_pgwerk_`), optionally schema-qualified:
41
+
42
+ | Table | Purpose |
43
+ |---|---|
44
+ | `_pgwerk_worker` | Registered worker instances + heartbeat |
45
+ | `_pgwerk_jobs` | The job queue — all state lives here |
46
+ | `_pgwerk_worker_jobs` | Claim tracking (worker ↔ job) |
47
+ | `_pgwerk_jobs_executions` | Per-attempt execution history |
48
+ | `_pgwerk_job_deps` | Job dependency graph (Python only) |
49
+
50
+ Dequeue uses `SELECT … FOR UPDATE SKIP LOCKED` inside a transaction for safe concurrent polling. Workers also use `LISTEN/NOTIFY` for instant wake-up on enqueue.
51
+
52
+ ### Python worker hierarchy
53
+
54
+ `BaseWorker` (polling loop, dequeue, ack/nack) is subclassed by:
55
+ - `AsyncWorker` — asyncio, runs handlers as coroutines
56
+ - `ThreadWorker` — runs handlers in a thread pool
57
+ - `ProcessWorker` — runs handlers in a process pool
58
+ - `ForkWorker` — forks per job
59
+
60
+ ### Python-only features vs Go
61
+
62
+ The Python implementation adds: `Retry` (exponential back-off intervals), `Repeat` (recurring jobs), `Callback` (on_success/on_failure hooks), `Dependency` (DAG dependencies), `CronScheduler`, pluggable `Serializer` (JSON/Pickle), `heartbeat_secs` for long-running jobs, and a `CLI` (`werk` command via Click).
63
+
64
+ The Go implementation is a leaner subset: enqueue, get, cancel, and a single goroutine-based `Worker`.
65
+
66
+ ### Configuration
67
+
68
+ Both use an options/builder pattern. Python: `Werk(dsn, prefix=..., schema=..., serializer=...)`. Go: `New(dsn, WithSchema(...), WithPrefix(...), WithPoolSize(...))`. Table names are constructed at init time and stored — never interpolated at query time except through the pre-built identifiers.
@@ -0,0 +1,83 @@
1
+ # Contributing
2
+
3
+ Thanks for taking the time. Here's how to get up and running and what to expect when you open a PR.
4
+
5
+ ## Setup
6
+
7
+ You need Python 3.11+ and a Postgres instance for integration tests.
8
+
9
+ ```bash
10
+ # Clone
11
+ git clone https://github.com/pgwerk/pgwerk
12
+ cd pgwerk
13
+
14
+ # Install with dev extras
15
+ uv sync --extra dev
16
+
17
+ # Start Postgres (Docker example)
18
+ docker run -d \
19
+ --name pgwerk-postgres \
20
+ -e POSTGRES_USER=pgwerk \
21
+ -e POSTGRES_PASSWORD=pgwerk \
22
+ -e POSTGRES_DB=pgwerk_test \
23
+ -p 5432:5432 \
24
+ postgres:16
25
+ ```
26
+
27
+ ## Running tests
28
+
29
+ ```bash
30
+ # Unit tests (no Postgres needed)
31
+ uv run pytest tests/unit/
32
+
33
+ # Integration tests
34
+ PGWERK_TEST_DSN="postgresql://pgwerk:pgwerk@localhost/pgwerk_test" uv run pytest tests/integration/
35
+
36
+ # All tests
37
+ PGWERK_TEST_DSN="postgresql://pgwerk:pgwerk@localhost/pgwerk_test" uv run pytest
38
+
39
+ # Single file
40
+ uv run pytest tests/unit/test_job.py -v
41
+ ```
42
+
43
+ ## Linting
44
+
45
+ ```bash
46
+ uv run ruff check .
47
+ uv run ruff format .
48
+ ```
49
+
50
+ CI runs both on every push and pull request.
51
+
52
+ ## Opening a PR
53
+
54
+ - Keep PRs focused — one concern per PR makes review faster.
55
+ - Add or update tests for any changed behaviour.
56
+ - Integration tests live in `tests/integration/`; unit tests in `tests/unit/`.
57
+ - Update `CHANGELOG.md` under `[Unreleased]` with a brief description of your change.
58
+ - CI must pass before merge.
59
+
60
+ ## Project structure
61
+
62
+ ```
63
+ pgwerk/ source package
64
+ app.py Werk app (connect, enqueue, inspect)
65
+ worker/ BaseWorker + AsyncWorker / ThreadWorker / ProcessWorker / ForkWorker
66
+ repos.py Database repository layer (all SQL lives here)
67
+ database.py Migration runner
68
+ schemas.py Pydantic / dataclass models
69
+ cron.py CronScheduler + CronJob
70
+ cli/ Click CLI (wrk worker / info / purge)
71
+ api/ Optional Litestar REST API
72
+ exporter/ Optional Prometheus metrics
73
+ tests/
74
+ unit/ Pure-Python unit tests, no DB required
75
+ integration/ Full-stack tests against a live Postgres instance
76
+ ```
77
+
78
+ ## Reporting bugs
79
+
80
+ Open an issue with:
81
+ 1. What you expected to happen.
82
+ 2. What actually happened (include logs where relevant).
83
+ 3. A minimal reproduction — ideally a single `pytest` test or a short script.
pgwerk-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Caio Cavalcanti
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
pgwerk-0.1.0/Makefile ADDED
@@ -0,0 +1,59 @@
1
+ .DEFAULT_GOAL := help
2
+
3
+ .PHONY: help setup lint format test test-unit test-integration docs docs-build docs-deploy dashboard clean
4
+
5
+ help: ## Show this help
6
+ @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}'
7
+
8
+ # ── Setup ─────────────────────────────────────────────────────────────────────
9
+
10
+ setup: ## Install all dependencies and set up the dev environment
11
+ uv sync --extra dev
12
+ pnpm install --dir dashboard
13
+ @echo ""
14
+ @echo "Done. To run integration tests, export PGWERK_TEST_DSN:"
15
+ @echo " export PGWERK_TEST_DSN=postgresql://werk:wrk@localhost/wrk_test"
16
+
17
+ # ── Code quality ──────────────────────────────────────────────────────────────
18
+
19
+ lint: ## Run the linter
20
+ uv run ruff check .
21
+
22
+ format: ## Format code
23
+ uv run ruff format .
24
+
25
+ # ── Tests ─────────────────────────────────────────────────────────────────────
26
+
27
+ test: ## Run all tests (requires PGWERK_TEST_DSN for integration)
28
+ uv run pytest
29
+
30
+ test-unit: ## Run unit tests only
31
+ uv run pytest tests/unit/
32
+
33
+ test-integration: ## Run integration tests (requires PGWERK_TEST_DSN)
34
+ uv run pytest tests/integration/
35
+
36
+ # ── Dashboard ─────────────────────────────────────────────────────────────────
37
+
38
+ dashboard: ## Run dashboard dev server (proxies API at localhost:8000)
39
+ pnpm --dir dashboard dev
40
+
41
+ # ── Docs ──────────────────────────────────────────────────────────────────────
42
+
43
+ docs: ## Serve docs locally at http://127.0.0.1:8000
44
+ uv run mkdocs serve
45
+
46
+ docs-build: ## Build docs to site/
47
+ uv run mkdocs build
48
+
49
+ docs-deploy: ## Deploy docs to GitHub Pages
50
+ uv run mkdocs gh-deploy --force
51
+
52
+ # ── Housekeeping ──────────────────────────────────────────────────────────────
53
+
54
+ clean: ## Remove caches and build artifacts
55
+ find . -type d -name __pycache__ -exec rm -r {} + 2>/dev/null; true
56
+ find . -type d -name .pytest_cache -exec rm -r {} + 2>/dev/null; true
57
+ find . -type d -name .mypy_cache -exec rm -r {} + 2>/dev/null; true
58
+ find . -name "*.pyc" -delete
59
+ find . -name ".coverage" -delete