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.
- pgwerk-0.1.0/.github/workflows/ci.yml +72 -0
- pgwerk-0.1.0/.github/workflows/docs.yml +31 -0
- pgwerk-0.1.0/.github/workflows/release.yml +49 -0
- pgwerk-0.1.0/.gitignore +26 -0
- pgwerk-0.1.0/CHANGELOG.md +38 -0
- pgwerk-0.1.0/CLAUDE.md +68 -0
- pgwerk-0.1.0/CONTRIBUTING.md +83 -0
- pgwerk-0.1.0/LICENSE +21 -0
- pgwerk-0.1.0/Makefile +59 -0
- pgwerk-0.1.0/PKG-INFO +279 -0
- pgwerk-0.1.0/README.md +209 -0
- pgwerk-0.1.0/dashboard/components.json +21 -0
- pgwerk-0.1.0/dashboard/index.html +16 -0
- pgwerk-0.1.0/dashboard/package.json +43 -0
- pgwerk-0.1.0/dashboard/pnpm-lock.yaml +3057 -0
- pgwerk-0.1.0/dashboard/postcss.config.js +6 -0
- pgwerk-0.1.0/dashboard/public/favicon.svg +9 -0
- pgwerk-0.1.0/dashboard/src/App.tsx +42 -0
- pgwerk-0.1.0/dashboard/src/components/StatusBadge.tsx +51 -0
- pgwerk-0.1.0/dashboard/src/components/cron/TriggerCronDialog.tsx +83 -0
- pgwerk-0.1.0/dashboard/src/components/jobs/ExecutionHistory.tsx +56 -0
- pgwerk-0.1.0/dashboard/src/components/jobs/JobDetail.tsx +181 -0
- pgwerk-0.1.0/dashboard/src/components/jobs/JobFilters.tsx +54 -0
- pgwerk-0.1.0/dashboard/src/components/jobs/JobsTable.tsx +127 -0
- pgwerk-0.1.0/dashboard/src/components/jobs/SendJobDialog.tsx +148 -0
- pgwerk-0.1.0/dashboard/src/components/layout/Header.tsx +29 -0
- pgwerk-0.1.0/dashboard/src/components/layout/Logo.tsx +32 -0
- pgwerk-0.1.0/dashboard/src/components/layout/Sidebar.tsx +52 -0
- pgwerk-0.1.0/dashboard/src/components/layout/TopNav.tsx +133 -0
- pgwerk-0.1.0/dashboard/src/components/overview/JobStatusPieChart.tsx +83 -0
- pgwerk-0.1.0/dashboard/src/components/overview/QueueDepthChart.tsx +79 -0
- pgwerk-0.1.0/dashboard/src/components/overview/QueueTable.tsx +82 -0
- pgwerk-0.1.0/dashboard/src/components/overview/StatCards.tsx +81 -0
- pgwerk-0.1.0/dashboard/src/components/overview/ThroughputChart.tsx +125 -0
- pgwerk-0.1.0/dashboard/src/components/ui/badge.tsx +36 -0
- pgwerk-0.1.0/dashboard/src/components/ui/button.tsx +56 -0
- pgwerk-0.1.0/dashboard/src/components/ui/card.tsx +79 -0
- pgwerk-0.1.0/dashboard/src/components/ui/dialog.tsx +120 -0
- pgwerk-0.1.0/dashboard/src/components/ui/dropdown-menu.tsx +198 -0
- pgwerk-0.1.0/dashboard/src/components/ui/select.tsx +160 -0
- pgwerk-0.1.0/dashboard/src/components/ui/separator.tsx +29 -0
- pgwerk-0.1.0/dashboard/src/components/ui/sheet.tsx +140 -0
- pgwerk-0.1.0/dashboard/src/components/ui/skeleton.tsx +15 -0
- pgwerk-0.1.0/dashboard/src/components/ui/table.tsx +117 -0
- pgwerk-0.1.0/dashboard/src/components/ui/tabs.tsx +53 -0
- pgwerk-0.1.0/dashboard/src/components/ui/tooltip.tsx +30 -0
- pgwerk-0.1.0/dashboard/src/components/workers/WorkerCard.tsx +106 -0
- pgwerk-0.1.0/dashboard/src/components/workers/WorkersList.tsx +80 -0
- pgwerk-0.1.0/dashboard/src/hooks/useTheme.ts +21 -0
- pgwerk-0.1.0/dashboard/src/hooks/useWorkerHistory.ts +34 -0
- pgwerk-0.1.0/dashboard/src/index.css +65 -0
- pgwerk-0.1.0/dashboard/src/lib/api.ts +123 -0
- pgwerk-0.1.0/dashboard/src/lib/utils.ts +51 -0
- pgwerk-0.1.0/dashboard/src/main.tsx +10 -0
- pgwerk-0.1.0/dashboard/src/pages/CronPage.tsx +104 -0
- pgwerk-0.1.0/dashboard/src/pages/JobsPage.tsx +137 -0
- pgwerk-0.1.0/dashboard/src/pages/MaintenancePage.tsx +524 -0
- pgwerk-0.1.0/dashboard/src/pages/OverviewPage.tsx +237 -0
- pgwerk-0.1.0/dashboard/src/pages/WorkerDetailPage.tsx +232 -0
- pgwerk-0.1.0/dashboard/src/pages/WorkersPage.tsx +35 -0
- pgwerk-0.1.0/dashboard/src/types.ts +142 -0
- pgwerk-0.1.0/dashboard/src/vite-env.d.ts +1 -0
- pgwerk-0.1.0/dashboard/tailwind.config.js +73 -0
- pgwerk-0.1.0/dashboard/tsconfig.app.json +23 -0
- pgwerk-0.1.0/dashboard/tsconfig.json +7 -0
- pgwerk-0.1.0/dashboard/tsconfig.node.json +16 -0
- pgwerk-0.1.0/dashboard/vite.config.ts +25 -0
- pgwerk-0.1.0/docs/assets/logo.svg +9 -0
- pgwerk-0.1.0/docs/changelog.md +1 -0
- pgwerk-0.1.0/docs/guide/cli.md +146 -0
- pgwerk-0.1.0/docs/guide/cron.md +137 -0
- pgwerk-0.1.0/docs/guide/dashboard.md +42 -0
- pgwerk-0.1.0/docs/guide/enqueueing.md +220 -0
- pgwerk-0.1.0/docs/guide/quickstart.md +113 -0
- pgwerk-0.1.0/docs/guide/serializers.md +68 -0
- pgwerk-0.1.0/docs/guide/workers.md +158 -0
- pgwerk-0.1.0/docs/index.md +34 -0
- pgwerk-0.1.0/docs/reference/app.md +222 -0
- pgwerk-0.1.0/docs/reference/exceptions.md +80 -0
- pgwerk-0.1.0/docs/reference/schemas.md +252 -0
- pgwerk-0.1.0/docs/reference/workers.md +120 -0
- pgwerk-0.1.0/example/__init__.py +15 -0
- pgwerk-0.1.0/example/producer.py +211 -0
- pgwerk-0.1.0/example/tasks.py +94 -0
- pgwerk-0.1.0/example/worker.py +36 -0
- pgwerk-0.1.0/justfile +66 -0
- pgwerk-0.1.0/mkdocs.yml +36 -0
- pgwerk-0.1.0/pgwerk/__init__.py +72 -0
- pgwerk-0.1.0/pgwerk/api/__init__.py +4 -0
- pgwerk-0.1.0/pgwerk/api/app.py +148 -0
- pgwerk-0.1.0/pgwerk/api/models.py +458 -0
- pgwerk-0.1.0/pgwerk/api/routes.py +543 -0
- pgwerk-0.1.0/pgwerk/app.py +1071 -0
- pgwerk-0.1.0/pgwerk/cli/__init__.py +35 -0
- pgwerk-0.1.0/pgwerk/cli/api.py +77 -0
- pgwerk-0.1.0/pgwerk/cli/cron.py +39 -0
- pgwerk-0.1.0/pgwerk/cli/dashboard.py +160 -0
- pgwerk-0.1.0/pgwerk/cli/info.py +58 -0
- pgwerk-0.1.0/pgwerk/cli/jobs.py +116 -0
- pgwerk-0.1.0/pgwerk/cli/purge.py +41 -0
- pgwerk-0.1.0/pgwerk/cli/slowest.py +103 -0
- pgwerk-0.1.0/pgwerk/cli/stats.py +137 -0
- pgwerk-0.1.0/pgwerk/cli/throughput.py +98 -0
- pgwerk-0.1.0/pgwerk/cli/utils.py +103 -0
- pgwerk-0.1.0/pgwerk/cli/worker.py +55 -0
- pgwerk-0.1.0/pgwerk/commons.py +41 -0
- pgwerk-0.1.0/pgwerk/config.py +76 -0
- pgwerk-0.1.0/pgwerk/cron.py +247 -0
- pgwerk-0.1.0/pgwerk/database.py +289 -0
- pgwerk-0.1.0/pgwerk/exceptions.py +26 -0
- pgwerk-0.1.0/pgwerk/exporter/__init__.py +256 -0
- pgwerk-0.1.0/pgwerk/logging.py +132 -0
- pgwerk-0.1.0/pgwerk/py.typed +0 -0
- pgwerk-0.1.0/pgwerk/repos.py +1168 -0
- pgwerk-0.1.0/pgwerk/schemas.py +497 -0
- pgwerk-0.1.0/pgwerk/serializers.py +166 -0
- pgwerk-0.1.0/pgwerk/utils.py +245 -0
- pgwerk-0.1.0/pgwerk/worker/__init__.py +14 -0
- pgwerk-0.1.0/pgwerk/worker/aio.py +72 -0
- pgwerk-0.1.0/pgwerk/worker/base.py +642 -0
- pgwerk-0.1.0/pgwerk/worker/fork.py +138 -0
- pgwerk-0.1.0/pgwerk/worker/process.py +81 -0
- pgwerk-0.1.0/pgwerk/worker/thread.py +76 -0
- pgwerk-0.1.0/pyproject.toml +110 -0
- pgwerk-0.1.0/static/logo-dark.svg +7 -0
- pgwerk-0.1.0/static/logo-light.svg +7 -0
- pgwerk-0.1.0/tests/__init__.py +0 -0
- pgwerk-0.1.0/tests/integration/__init__.py +0 -0
- pgwerk-0.1.0/tests/integration/conftest.py +72 -0
- pgwerk-0.1.0/tests/integration/tasks.py +241 -0
- pgwerk-0.1.0/tests/integration/test_api_smoke.py +66 -0
- pgwerk-0.1.0/tests/integration/test_apply.py +39 -0
- pgwerk-0.1.0/tests/integration/test_before_after_process_hooks.py +86 -0
- pgwerk-0.1.0/tests/integration/test_concurrency.py +67 -0
- pgwerk-0.1.0/tests/integration/test_concurrency_limit.py +53 -0
- pgwerk-0.1.0/tests/integration/test_dependencies.py +222 -0
- pgwerk-0.1.0/tests/integration/test_enqueue.py +98 -0
- pgwerk-0.1.0/tests/integration/test_enqueue_many.py +101 -0
- pgwerk-0.1.0/tests/integration/test_expired_jobs.py +61 -0
- pgwerk-0.1.0/tests/integration/test_failure_mode.py +52 -0
- pgwerk-0.1.0/tests/integration/test_fork_worker.py +86 -0
- pgwerk-0.1.0/tests/integration/test_get_executions.py +31 -0
- pgwerk-0.1.0/tests/integration/test_get_job.py +26 -0
- pgwerk-0.1.0/tests/integration/test_group_key.py +100 -0
- pgwerk-0.1.0/tests/integration/test_job_heartbeat.py +92 -0
- pgwerk-0.1.0/tests/integration/test_job_termination.py +85 -0
- pgwerk-0.1.0/tests/integration/test_lifecycle_hooks.py +69 -0
- pgwerk-0.1.0/tests/integration/test_list_jobs.py +79 -0
- pgwerk-0.1.0/tests/integration/test_map.py +57 -0
- pgwerk-0.1.0/tests/integration/test_process_worker.py +63 -0
- pgwerk-0.1.0/tests/integration/test_prod_paths.py +252 -0
- pgwerk-0.1.0/tests/integration/test_race_detection.py +97 -0
- pgwerk-0.1.0/tests/integration/test_robustness.py +82 -0
- pgwerk-0.1.0/tests/integration/test_sweep.py +178 -0
- pgwerk-0.1.0/tests/integration/test_thread_worker.py +71 -0
- pgwerk-0.1.0/tests/integration/test_unique_key.py +29 -0
- pgwerk-0.1.0/tests/integration/test_wait_for.py +51 -0
- pgwerk-0.1.0/tests/integration/test_worker_callbacks.py +42 -0
- pgwerk-0.1.0/tests/integration/test_worker_execution.py +101 -0
- pgwerk-0.1.0/tests/integration/test_worker_failure.py +41 -0
- pgwerk-0.1.0/tests/integration/test_worker_lifecycle.py +78 -0
- pgwerk-0.1.0/tests/integration/test_worker_repeat.py +36 -0
- pgwerk-0.1.0/tests/integration/test_worker_retry.py +184 -0
- pgwerk-0.1.0/tests/integration/test_wrk_init.py +30 -0
- pgwerk-0.1.0/tests/unit/__init__.py +0 -0
- pgwerk-0.1.0/tests/unit/test_app_helpers.py +386 -0
- pgwerk-0.1.0/tests/unit/test_cli.py +106 -0
- pgwerk-0.1.0/tests/unit/test_commons.py +201 -0
- pgwerk-0.1.0/tests/unit/test_cron.py +315 -0
- pgwerk-0.1.0/tests/unit/test_database.py +43 -0
- pgwerk-0.1.0/tests/unit/test_exceptions.py +80 -0
- pgwerk-0.1.0/tests/unit/test_job.py +183 -0
- pgwerk-0.1.0/tests/unit/test_schema_consistency.py +105 -0
- pgwerk-0.1.0/tests/unit/test_serializers.py +132 -0
- pgwerk-0.1.0/tests/unit/test_worker_aio.py +151 -0
- pgwerk-0.1.0/tests/unit/test_worker_base.py +297 -0
- pgwerk-0.1.0/tests/unit/test_worker_variants.py +196 -0
- 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 }}
|
pgwerk-0.1.0/.gitignore
ADDED
|
@@ -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
|