Skip to main content

Official Python client for GoQueue - a high-performance distributed message queue

Project description

GoQueue Python Client

Python SDK for GoQueue - A high-performance distributed message queue that combines the best features of Kafka, SQS, and RabbitMQ.

Features

  • Async-first design - Built on httpx for efficient async I/O
  • Full type hints - Complete type coverage for IDE support
  • Rich docstrings - Documentation available in your IDE
  • Auto-retry - Automatic retry with exponential backoff
  • Context manager - Clean resource management

Installation

pip install goqueue-client

Quick Start

import asyncio
from goqueue_client import GoQueueClient

async def main():
    # Create a client (async context manager)
    async with GoQueueClient("http://localhost:8080") as client:
        # Create a topic
        await client.topics.create({
            "name": "orders",
            "num_partitions": 3
        })
        
        # Publish messages
        await client.messages.publish("orders", [
            {"value": '{"orderId": "12345", "amount": 99.99}'}
        ])
        
        # Simple consume
        response = await client.messages.consume("orders", partition=0)
        for msg in response["messages"]:
            print(f"Received: {msg['value']}")

asyncio.run(main())

Consumer Groups

For production workloads, use consumer groups for parallel processing:

import asyncio
from goqueue_client import GoQueueClient

async def worker(client: GoQueueClient, group_id: str, worker_id: str):
    """Worker that processes messages from a consumer group."""
    
    # Join the consumer group
    join_response = await client.groups.join(group_id, {
        "client_id": worker_id,
        "topics": ["orders"],
        "session_timeout": "30s"
    })
    member_id = join_response["member_id"]
    generation = join_response["generation"]
    
    print(f"Worker {worker_id} joined with partitions: {join_response['assigned_partitions']}")
    
    try:
        while True:
            # Poll for messages
            response = await client.groups.poll(
                group_id,
                member_id,
                max_messages=10,
                timeout="10s"
            )
            
            # Process messages
            for msg in response["messages"]:
                print(f"Worker {worker_id} processing: {msg['value']}")
                
                # Acknowledge successful processing
                await client.messages.ack(msg["receipt_handle"])
            
            # Send heartbeat
            heartbeat = await client.groups.heartbeat(group_id, {
                "member_id": member_id,
                "generation": generation
            })
            
            # Handle rebalances
            if heartbeat.get("rebalance_required"):
                print(f"Worker {worker_id} needs to rejoin")
                break
    finally:
        # Leave the group
        await client.groups.leave(group_id, {"member_id": member_id})

async def main():
    async with GoQueueClient("http://localhost:8080") as client:
        # Create topic
        await client.topics.create({
            "name": "orders",
            "num_partitions": 6
        })
        
        # Start multiple workers
        await asyncio.gather(
            worker(client, "order-processors", "worker-1"),
            worker(client, "order-processors", "worker-2"),
            worker(client, "order-processors", "worker-3"),
        )

asyncio.run(main())

Message Options

With Keys (Ordering)

Messages with the same key go to the same partition:

await client.messages.publish("orders", [
    {"key": "user-123", "value": '{"event": "order_created"}'},
    {"key": "user-123", "value": '{"event": "payment_received"}'},  # Same partition
])

Priority Messages

Higher priority messages are delivered first:

await client.messages.publish("alerts", [
    {"value": "Normal alert", "priority": "normal"},
    {"value": "Critical alert!", "priority": "critical"},  # Delivered first
])

Delayed Messages

Schedule messages for future delivery:

await client.messages.publish("reminders", [
    {"value": "Follow up in 1 hour", "delay": "1h"},
    {"value": "Daily report", "delay": "24h"},
])

Reliability Patterns

ACK/NACK/Reject

async def process_message(client: GoQueueClient, msg: dict):
    """Process a message with proper acknowledgment."""
    try:
        # Process the message
        result = await do_work(msg["value"])
        
        # Success - acknowledge
        await client.messages.ack(msg["receipt_handle"])
        
    except TemporaryError:
        # Temporary failure - NACK for redelivery
        await client.messages.nack(
            msg["receipt_handle"],
            delay="30s"  # Retry after 30 seconds
        )
        
    except PermanentError as e:
        # Permanent failure - send to DLQ
        await client.messages.reject(
            msg["receipt_handle"],
            reason=str(e)
        )

Extending Visibility Timeout

For long-running operations:

async def long_process(client: GoQueueClient, msg: dict):
    """Process that takes longer than visibility timeout."""
    
    # Start an async task to extend visibility periodically
    async def extend_visibility():
        while True:
            await asyncio.sleep(20)  # Extend every 20 seconds
            await client.messages.extend_visibility(
                msg["receipt_handle"],
                timeout="60s"
            )
    
    extend_task = asyncio.create_task(extend_visibility())
    
    try:
        await do_long_work(msg["value"])
        await client.messages.ack(msg["receipt_handle"])
    finally:
        extend_task.cancel()

Transactions (Exactly-Once)

async def transfer_funds(client: GoQueueClient, from_account: str, to_account: str, amount: float):
    """Transfer funds with exactly-once semantics."""
    
    # Initialize producer
    producer = await client.transactions.init_producer({
        "transactional_id": f"transfer-{from_account}-{to_account}"
    })
    producer_id = producer["producer_id"]
    epoch = producer["epoch"]
    
    try:
        # Begin transaction
        await client.transactions.begin({
            "producer_id": producer_id,
            "epoch": epoch,
            "transactional_id": f"transfer-{from_account}-{to_account}"
        })
        
        # Publish debit message
        await client.transactions.publish({
            "producer_id": producer_id,
            "epoch": epoch,
            "topic": "account-debits",
            "value": json.dumps({
                "account": from_account,
                "amount": -amount
            }),
            "sequence": 1
        })
        
        # Publish credit message
        await client.transactions.publish({
            "producer_id": producer_id,
            "epoch": epoch,
            "topic": "account-credits",
            "value": json.dumps({
                "account": to_account,
                "amount": amount
            }),
            "sequence": 2
        })
        
        # Commit transaction - both or neither
        await client.transactions.commit({
            "producer_id": producer_id,
            "epoch": epoch,
            "transactional_id": f"transfer-{from_account}-{to_account}"
        })
        
    except Exception:
        # Abort transaction on any error
        await client.transactions.abort({
            "producer_id": producer_id,
            "epoch": epoch,
            "transactional_id": f"transfer-{from_account}-{to_account}"
        })
        raise

Schema Registry

import json

# Register a schema
await client.schemas.register("orders-value", {
    "schema": json.dumps({
        "type": "object",
        "properties": {
            "orderId": {"type": "string"},
            "amount": {"type": "number"},
            "items": {
                "type": "array",
                "items": {"type": "object"}
            }
        },
        "required": ["orderId", "amount"]
    })
})

# Get latest schema
schema = await client.schemas.get_version("orders-value", "latest")
print(f"Schema ID: {schema['id']}")

# Configure compatibility (BACKWARD, FORWARD, FULL, NONE)
await client.schemas.set_config({"compatibilityLevel": "BACKWARD"})

Health Checks

# Simple health check
health = await client.health.check()
print(f"Status: {health['status']}")

# Kubernetes probes
liveness = await client.health.liveness()   # /healthz
readiness = await client.health.readiness()  # /readyz

# Detailed readiness with checks
readiness = await client.health.readiness(verbose=True)
for check_name, result in readiness.get("checks", {}).items():
    print(f"{check_name}: {result['status']}")

# Get statistics
stats = await client.health.stats()
print(f"Topics: {stats['topics']}")
print(f"Uptime: {stats['uptime']}")

Error Handling

from goqueue_client import GoQueueClient, GoQueueError

async with GoQueueClient("http://localhost:8080") as client:
    try:
        await client.topics.get("non-existent-topic")
    except GoQueueError as e:
        print(f"Error: {e.message}")
        print(f"Status: {e.status}")  # 404
        print(f"Body: {e.body}")

Configuration

client = GoQueueClient(
    "http://localhost:8080",
    timeout=30.0,           # Request timeout in seconds
    headers={               # Additional headers
        "X-Tenant-ID": "my-tenant",
        "Authorization": "Bearer token"
    },
    max_retries=3,          # Retry attempts for failed requests
    initial_delay=0.1,      # Initial retry delay (seconds)
    max_delay=5.0,          # Maximum retry delay (seconds)
)

API Reference

Services

Service Description
client.health Health checks and probes
client.topics Topic management
client.messages Message publish/consume/ack
client.delayed Delayed message operations
client.groups Consumer group operations
client.priority Priority queue statistics
client.schemas Schema registry operations
client.transactions Transaction operations
client.tracing Message tracing
client.admin Administrative operations

Requirements

  • Python 3.9+
  • httpx

License

MIT License - see LICENSE for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

goqueue_client-1.0.0.tar.gz (25.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

goqueue_client-1.0.0-py3-none-any.whl (24.8 kB view details)

Uploaded Python 3

File details

Details for the file goqueue_client-1.0.0.tar.gz.

File metadata

  • Download URL: goqueue_client-1.0.0.tar.gz
  • Upload date:
  • Size: 25.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.15.0 pkginfo/1.8.3 requests/2.27.1 setuptools/41.2.0 requests-toolbelt/1.0.0 tqdm/4.64.1 CPython/2.7.18

File hashes

Hashes for goqueue_client-1.0.0.tar.gz
Algorithm Hash digest
SHA256 627c61671696ae77519842ad12268b2e6ac72d3fa0d10a4e8218704ac703e64a
MD5 7866794d84739c23b1a324f407eb016b
BLAKE2b-256 45ec19f63906421cd8fc184fc36fe29e77122ff9216d6f1071348ae5d78aec53

See more details on using hashes here.

File details

Details for the file goqueue_client-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: goqueue_client-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 24.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.15.0 pkginfo/1.8.3 requests/2.27.1 setuptools/41.2.0 requests-toolbelt/1.0.0 tqdm/4.64.1 CPython/2.7.18

File hashes

Hashes for goqueue_client-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a5af1eca34f9858b8c3028fe59cfb1bfe98266f345ea19f823562ab44bbca353
MD5 9684d1cbad20874d8b956d00ea7cb9c6
BLAKE2b-256 d06df52fd51c3cacaaa9e1393abdf6aebae5bb8758ff645402ef7381e63a7f25

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page