Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .github/workflows/release_docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Release docs

on:
push:
branches: [main]
paths:
- "docs/**"
- "zensical.toml"
- ".github/workflows/release_docs.yml"

permissions:
contents: read
pages: write
id-token: write

jobs:
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
with:
enable-cache: true
python-version: "3.12"
version: "latest"
- run: uv sync --only-group docs
- run: uv run zensical build --clean
- uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
with:
path: site
- uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
id: deployment
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ init: ## Install all project dependencies with extras

.PHONY: run_docs
run_docs: ## Run documentation server
@uv run mkdocs serve --livereload
@uv run zensical serve

.PHONY: run_infra
run_infra: ## Run rabbitmq in docker for integration tests
run_infra: ## Run ministack (local AWS emulator) in docker for integration tests
@docker compose -f docker-compose.yml up -d

##@ Code quality
Expand Down
44 changes: 44 additions & 0 deletions docs/contributing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
title: Contributing
description: >-
Set up taskiq-sqs locally with uv, run lint and tests against the ministack AWS emulator, and
build the docs with zensical.
---

taskiq-sqs follows the same contribution philosophy as the rest of the taskiq ecosystem — see the
[taskiq contribution guide](https://taskiq-python.github.io/contrib.html) for the general rules (found a bug?
open an issue; not sure about something? open a draft PR and ask in the description; and so on).

The commands below are specific to this repository — it uses [uv](https://docs.astral.sh/uv/) and
[zensical](https://zensical.org/) rather than the tox/VuePress setup described on that page.

## Setting up the environment

```bash
git clone https://github.com/taskiq-python/taskiq-sqs.git
cd taskiq-sqs
make init
```

Tests need a local AWS emulator ([ministack](https://github.com/ministackorg/ministack)):

```bash
make run_infra
```

## Linting and testing

```bash
make lint # ruff + mypy
make test # pytest, against the ministack container started above
```

`make help` lists every available target.

## Working with documentation

This site is built with [zensical](https://zensical.org/). To preview changes locally:

```bash
make run_docs
```
58 changes: 58 additions & 0 deletions docs/examples/large_payloads_with_s3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""
Run worker:
taskiq worker docs.examples.large_payloads_with_s3:broker

Run this script:
python docs/examples/large_payloads_with_s3.py
"""

import asyncio

import dotenv

from taskiq_sqs import S3OffloadMiddleware, S3ResultBackend, SQSBroker
from taskiq_sqs.types import S3Bucket, SQSQueue


dotenv.load_dotenv()

ENDPOINT_URL = "http://localhost:4566"
AWS_REGION = "us-east-1"

broker = SQSBroker(
queues=SQSQueue(name="large-payload-queue"),
endpoint_url=ENDPOINT_URL,
aws_region_name=AWS_REGION,
).with_result_backend(
S3ResultBackend(
bucket=S3Bucket(name="large-payload-results"),
endpoint_url=ENDPOINT_URL,
aws_region_name=AWS_REGION,
),
)
broker.add_middlewares(
S3OffloadMiddleware(
bucket=S3Bucket(name="large-payload-offload"),
max_message_size=200_000, # payloads larger than this many bytes are offloaded to S3
endpoint_url=ENDPOINT_URL,
aws_region_name=AWS_REGION,
),
)


@broker.task()
async def summarize_document(content: str) -> dict[str, int]:
return {"characters": len(content), "words": len(content.split())}


async def main() -> None:
await broker.startup()
document = "taskiq-sqs " * 100_000 # well over the 256 KiB SQS message limit
task = await summarize_document.kiq(document)
result = await task.wait_result(timeout=10)
print(result.return_value)
await broker.shutdown()


if __name__ == "__main__":
asyncio.run(main())
60 changes: 60 additions & 0 deletions docs/examples/priority_queues.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""
Run worker:
taskiq worker docs.examples.priority_queues:broker

Run this script to kick a batch of bulk events and two ordered urgent alerts:
python docs/examples/priority_queues.py
"""

import asyncio

import dotenv

from taskiq_sqs import SQSBroker
from taskiq_sqs.types import SQSQueue


dotenv.load_dotenv()

ENDPOINT_URL = "http://localhost:4566"
AWS_REGION = "us-east-1"

broker = SQSBroker(
queues=[
# bulk, low-priority work: batched together to cut down on SendMessage calls
SQSQueue(name="bulk-queue", is_batching_enabled=True, batch_size=10, batch_timeout=1.0),
# time-sensitive work: FIFO so alerts from the same source stay in order. ContentBasedDeduplication is
# required here since these messages don't set an explicit deduplication_id label.
SQSQueue(name="urgent-queue.fifo", options={"ContentBasedDeduplication": "true"}),
],
endpoint_url=ENDPOINT_URL,
aws_region_name=AWS_REGION,
)


@broker.task()
async def process_bulk_event(event_id: int) -> None:
"""Runs on the default queue (the first one in `queues`), since no `queue_name` label is set."""
print(f"Processed bulk event {event_id}")


@broker.task(queue_name="urgent-queue.fifo")
async def process_urgent_alert(source: str, message: str) -> None:
print(f"[{source}] {message}")


async def main() -> None:
await broker.startup()

for event_id in range(5):
await process_bulk_event.kiq(event_id)

# group_id keeps alerts from the same source ordered relative to each other
await process_urgent_alert.kicker().with_labels(group_id="sensor-1").kiq("sensor-1", "temperature spike")
await process_urgent_alert.kicker().with_labels(group_id="sensor-1").kiq("sensor-1", "temperature back to normal")

await broker.shutdown()


if __name__ == "__main__":
asyncio.run(main())
55 changes: 55 additions & 0 deletions docs/examples/reliable_task_delivery.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"""
Run worker:
taskiq worker docs.examples.reliable_task_delivery:broker

Run this script:
python docs/examples/reliable_task_delivery.py
"""

import asyncio
import time

import dotenv

from taskiq_sqs import SQSBroker
from taskiq_sqs.types import SQSQueue


dotenv.load_dotenv()

ENDPOINT_URL = "http://localhost:4566"
AWS_REGION = "us-east-1"

broker = SQSBroker(queues=SQSQueue(name="retry-queue"), endpoint_url=ENDPOINT_URL, aws_region_name=AWS_REGION)


@broker.task()
async def send_verification_email(user_id: int) -> None:
print(f"Sent verification email to user {user_id}")


async def schedule_with_deadline(user_id: int, *, delay_seconds: int, deadline_seconds: float) -> None:
"""Deliver after `delay_seconds`, but give up if no worker picks it up within `deadline_seconds` of now.

`deadline_seconds` must be greater than `delay_seconds` — the message isn't even visible to a worker
before `delay_seconds` elapses, so a smaller deadline would make it expire before anyone can receive it.
"""
await (
send_verification_email.kicker()
.with_labels(
delay=delay_seconds,
expiry=time.time() + deadline_seconds,
)
.kiq(user_id)
)


async def main() -> None:
await broker.startup()
# deliver in 5 seconds, but only if a worker actually picks it up within 30 seconds of being kicked
await schedule_with_deadline(user_id=42, delay_seconds=5, deadline_seconds=30)
await broker.shutdown()


if __name__ == "__main__":
asyncio.run(main())
Loading