# Fairness Queues
Source: https://densumesh-broccoli-27.localhost:3000/advanced/fairness
Ensure fair processing across message categories
## Overview
Fairness queues prevent one category of messages from monopolizing processing resources. By using disambiguators, you can ensure messages are processed fairly across different tenants, users, or categories.
Fairness queues are currently only supported with the **Redis** broker.
## The problem
Without fairness, a burst of messages from one source can delay others:
```mermaid theme={null}
timeline
title Without Fairness
section Queue
Tenant A : 100 messages
Tenant B : 5 messages
Tenant C : 5 messages
section Processing
0-100ms : All Tenant A
100-105ms : Tenant B
105-110ms : Tenant C
```
Tenant B and C must wait for all of Tenant A's messages to process.
## With fairness
Fairness distributes processing across categories:
```mermaid theme={null}
timeline
title With Fairness
section Processing
0-3ms : A, B, C (round robin)
3-6ms : A, B, C
... : continues fairly
```
## Using disambiguators
### Publishing with disambiguators
Include a disambiguator when publishing:
```rust theme={null}
// Each tenant gets their own sub-queue
queue.publish("jobs", Some("tenant-a".into()), &job_a, None).await?;
queue.publish("jobs", Some("tenant-b".into()), &job_b, None).await?;
queue.publish("jobs", Some("tenant-c".into()), &job_c, None).await?;
```
### Consuming with fairness
Enable fairness in consume options:
```rust theme={null}
use broccoli_queue::queue::ConsumeOptions;
let options = ConsumeOptions::builder()
.fairness(true)
.build();
queue.process_messages("jobs", Some(4), Some(options), |msg| async move {
println!("Processing for {:?}", msg.disambiguator);
Ok(())
}).await?;
```
## How it works
With fairness enabled:
1. Messages are stored in sub-queues based on disambiguator
2. The consumer rotates through sub-queues in round-robin fashion
3. Each consume operation pulls from the next sub-queue
```mermaid theme={null}
flowchart LR
subgraph "Main Queue: jobs"
direction TB
A[tenant-a queue]
B[tenant-b queue]
C[tenant-c queue]
end
Consumer --> |round robin| A
Consumer --> |round robin| B
Consumer --> |round robin| C
```
## Use cases
### Multi-tenant applications
Prevent one tenant from affecting others:
```rust theme={null}
async fn handle_tenant_job(tenant_id: &str, job: TenantJob) {
queue.publish(
"tenant-jobs",
Some(tenant_id.to_string()),
&job,
None
).await?;
}
// Consumer processes fairly across tenants
let options = ConsumeOptions::builder().fairness(true).build();
queue.process_messages("tenant-jobs", Some(8), Some(options), handler).await?;
```
### Priority levels
Implement soft priority with multiple queues:
```rust theme={null}
// High priority gets its own queue
queue.publish("jobs", Some("priority-high".into()), &urgent_job, None).await?;
// Normal priority
queue.publish("jobs", Some("priority-normal".into()), &normal_job, None).await?;
// With fairness, both get equal attention
// For true priority, use separate queues or priority options
```
### User-based fairness
Prevent a single user from overwhelming the system:
```rust theme={null}
let user_id = request.user_id;
queue.publish("user-jobs", Some(user_id), &job, None).await?;
```
## Queue size with fairness
When using fairness, `queue.size()` returns sizes for each sub-queue:
```rust theme={null}
let sizes = queue.size("jobs").await?;
for (queue_name, size) in sizes {
println!("{}: {} messages", queue_name, size);
}
// Output:
// jobs:fairness:tenant-a: 45 messages
// jobs:fairness:tenant-b: 12 messages
// jobs:fairness:tenant-c: 8 messages
```
## Configuration options
```rust theme={null}
let options = ConsumeOptions::builder()
.fairness(true)
.auto_ack(false) // Manual acknowledgment
.consume_wait(Duration::from_millis(10)) // Wait between iterations
.build();
```
## Best practices
Use identifiers that represent your fairness boundaries:
* Tenant ID for multi-tenant apps
* User ID for user fairness
* Region for geographic distribution
* Priority level for soft prioritization
Track the size of each sub-queue to detect imbalances:
```rust theme={null}
let sizes = queue.size("jobs").await?;
for (name, size) in sizes {
metrics::gauge!("queue.size", size as f64, "queue" => name);
}
```
Too many unique disambiguators can impact performance. Consider bucketing:
```rust theme={null}
// Instead of per-user, bucket by user hash
let bucket = format!("bucket-{}", user_id.hash() % 100);
queue.publish("jobs", Some(bucket), &job, None).await?;
```
## Limitations
* Only available with Redis broker
* Round-robin is fixed (no weighted fairness)
* Empty sub-queues are checked (slight overhead)
## Alternatives
If fairness queues don't fit your needs:
1. **Separate queues**: Create distinct queues per category
2. **Priority option**: Use `PublishOptions::priority()` for priority-based ordering
3. **Custom routing**: Implement your own routing logic with multiple queues
# Management API
Source: https://densumesh-broccoli-27.localhost:3000/advanced/management
Monitor and manage your queues
## Overview
The management feature provides APIs to inspect queue status and monitor your message processing system.
## Enabling management
Add the `management` feature to your `Cargo.toml`:
```toml theme={null}
[dependencies]
broccoli_queue = { version = "0.4", features = ["management"] }
```
Management is currently supported for Redis and RabbitMQ brokers only.
## Queue status
Get the status of a queue:
```rust theme={null}
#[cfg(feature = "management")]
async fn check_queue_health(queue: &BroccoliQueue) -> Result<(), BroccoliError> {
let status = queue.queue_status("jobs".into(), None).await?;
println!("Queue: jobs");
println!(" Pending: {}", status.pending_count);
println!(" Processing: {}", status.processing_count);
println!(" Failed: {}", status.failed_count);
Ok(())
}
```
### With disambiguator
For fairness queues, specify the disambiguator:
```rust theme={null}
let status = queue.queue_status(
"jobs".into(),
Some("tenant-123".into())
).await?;
```
## Queue size
Get queue sizes (available without management feature):
```rust theme={null}
let sizes = queue.size("jobs").await?;
for (queue_name, size) in sizes {
println!("{}: {} messages", queue_name, size);
}
```
For fairness queues, this returns sizes for each sub-queue.
## Building a monitoring dashboard
Example of exposing queue metrics:
```rust theme={null}
use std::collections::HashMap;
#[derive(Serialize)]
struct QueueMetrics {
name: String,
pending: u64,
processing: u64,
failed: u64,
}
async fn get_metrics(queue: &BroccoliQueue, queue_names: &[&str]) -> Vec {
let mut metrics = Vec::new();
for name in queue_names {
#[cfg(feature = "management")]
if let Ok(status) = queue.queue_status(name.to_string(), None).await {
metrics.push(QueueMetrics {
name: name.to_string(),
pending: status.pending_count,
processing: status.processing_count,
failed: status.failed_count,
});
}
}
metrics
}
```
## Health checks
Implement health checks for your queue system:
```rust theme={null}
async fn health_check(queue: &BroccoliQueue) -> bool {
// Try to get queue size as a connectivity check
match queue.size("health-check").await {
Ok(_) => true,
Err(e) => {
log::error!("Queue health check failed: {:?}", e);
false
}
}
}
```
## Alerting on failed messages
Monitor the failed queue for alerting:
```rust theme={null}
#[cfg(feature = "management")]
async fn check_failed_messages(queue: &BroccoliQueue) -> Result<(), BroccoliError> {
let status = queue.queue_status("jobs".into(), None).await?;
if status.failed_count > 100 {
// Send alert
alert::send("High failed message count", &format!(
"Queue 'jobs' has {} failed messages",
status.failed_count
)).await;
}
Ok(())
}
```
## Metrics integration
### Prometheus
```rust theme={null}
use prometheus::{IntGauge, register_int_gauge};
lazy_static! {
static ref QUEUE_PENDING: IntGauge = register_int_gauge!(
"broccoli_queue_pending",
"Number of pending messages"
).unwrap();
static ref QUEUE_PROCESSING: IntGauge = register_int_gauge!(
"broccoli_queue_processing",
"Number of messages being processed"
).unwrap();
static ref QUEUE_FAILED: IntGauge = register_int_gauge!(
"broccoli_queue_failed",
"Number of failed messages"
).unwrap();
}
#[cfg(feature = "management")]
async fn update_metrics(queue: &BroccoliQueue) {
if let Ok(status) = queue.queue_status("jobs".into(), None).await {
QUEUE_PENDING.set(status.pending_count as i64);
QUEUE_PROCESSING.set(status.processing_count as i64);
QUEUE_FAILED.set(status.failed_count as i64);
}
}
```
### OpenTelemetry
```rust theme={null}
use opentelemetry::metrics::Meter;
async fn record_metrics(meter: &Meter, queue: &BroccoliQueue) {
let pending_gauge = meter.i64_gauge("broccoli.queue.pending").init();
let failed_gauge = meter.i64_gauge("broccoli.queue.failed").init();
#[cfg(feature = "management")]
if let Ok(status) = queue.queue_status("jobs".into(), None).await {
pending_gauge.record(status.pending_count as i64, &[]);
failed_gauge.record(status.failed_count as i64, &[]);
}
}
```
## Redis CLI inspection
For Redis, you can also inspect queues directly:
```bash theme={null}
# Main queue size
redis-cli LLEN jobs
# Processing queue
redis-cli ZCARD jobs:processing
# Failed queue
redis-cli LLEN jobs:failed
# Scheduled messages
redis-cli ZCARD jobs:scheduled
# List failed messages
redis-cli LRANGE jobs:failed 0 10
```
## Best practices
Don't query status on every request. Use a background task:
```rust theme={null}
tokio::spawn(async move {
let mut interval = tokio::time::interval(Duration::from_secs(30));
loop {
interval.tick().await;
update_metrics(&queue).await;
}
});
```
Monitor the failed queue and alert when it exceeds thresholds.
Measure how long messages spend in processing state to detect stuck jobs.
# Message Scheduling
Source: https://densumesh-broccoli-27.localhost:3000/advanced/scheduling
Schedule messages for delayed or future delivery
## Overview
Broccoli supports scheduling messages for delayed delivery. You can either delay a message by a duration or schedule it for a specific time.
## Enabling scheduling
Enable scheduling when building your queue:
```rust theme={null}
let queue = BroccoliQueue::builder("redis://localhost:6379")
.enable_scheduling(true)
.build()
.await?;
```
For RabbitMQ, you must install the [delayed-exchange plugin](https://www.rabbitmq.com/blog/2015/04/16/scheduling-messages-with-rabbitmq).
## Delayed delivery
Delay a message by a specific duration:
```rust theme={null}
use broccoli_queue::queue::PublishOptions;
use time::Duration;
let options = PublishOptions::builder()
.delay(Duration::seconds(30))
.build();
queue.publish("jobs", None, &job, Some(options)).await?;
```
The message will be delivered approximately 30 seconds after publishing.
## Scheduled delivery
Schedule a message for a specific time:
```rust theme={null}
use broccoli_queue::queue::PublishOptions;
use time::OffsetDateTime;
// Schedule for tomorrow at midnight
let scheduled_time = OffsetDateTime::now_utc()
.replace_time(time::Time::MIDNIGHT)
+ time::Duration::days(1);
let options = PublishOptions::builder()
.schedule_at(scheduled_time)
.build();
queue.publish("jobs", None, &job, Some(options)).await?;
```
## Batch scheduling
Schedule multiple messages:
```rust theme={null}
use time::Duration;
let jobs = vec![
JobPayload { id: "1".into(), task: "task-a".into() },
JobPayload { id: "2".into(), task: "task-b".into() },
];
let options = PublishOptions::builder()
.delay(Duration::minutes(5))
.build();
queue.publish_batch("jobs", None, jobs, Some(options)).await?;
```
## PublishOptions
The `PublishOptions` struct supports several scheduling-related options:
```rust theme={null}
pub struct PublishOptions {
/// Time-to-live for the message
pub ttl: Option,
/// Message priority (1-5, where 1 is highest)
pub priority: Option,
/// Delay before delivery
pub delay: Option,
/// Specific delivery time
pub scheduled_at: Option,
}
```
### Builder pattern
```rust theme={null}
let options = PublishOptions::builder()
.delay(Duration::seconds(60))
.priority(1) // High priority
.ttl(Duration::hours(24))
.build();
```
## Use cases
### Scheduled reports
```rust theme={null}
#[derive(Clone, Serialize, Deserialize)]
struct ReportJob {
report_type: String,
recipients: Vec,
}
// Schedule daily report for 6 AM
let tomorrow_6am = OffsetDateTime::now_utc()
.replace_time(time::Time::from_hms(6, 0, 0)?)
+ time::Duration::days(1);
let report = ReportJob {
report_type: "daily_summary".into(),
recipients: vec!["admin@example.com".into()],
};
queue.publish(
"reports",
None,
&report,
Some(PublishOptions::builder().schedule_at(tomorrow_6am).build())
).await?;
```
### Retry with backoff
```rust theme={null}
async fn handle_with_retry(
queue: &BroccoliQueue,
job: JobPayload,
attempt: u8,
) -> Result<(), BroccoliError> {
match process_job(&job).await {
Ok(_) => Ok(()),
Err(e) if attempt < 5 => {
// Exponential backoff: 1s, 2s, 4s, 8s, 16s
let delay = Duration::seconds(2_i64.pow(attempt as u32));
queue.publish(
"jobs",
None,
&job,
Some(PublishOptions::builder().delay(delay).build())
).await?;
Ok(())
}
Err(e) => Err(e),
}
}
```
### Rate limiting
```rust theme={null}
// Spread 100 emails over 10 minutes to avoid rate limits
for (i, email) in emails.iter().enumerate() {
let delay = Duration::seconds((i as i64) * 6); // One every 6 seconds
queue.publish(
"emails",
None,
email,
Some(PublishOptions::builder().delay(delay).build())
).await?;
}
```
## How scheduling works
```mermaid theme={null}
sequenceDiagram
participant P as Producer
participant B as Broker
participant S as Scheduled Store
participant Q as Main Queue
participant C as Consumer
P->>B: publish(delay=30s)
B->>S: Store with scheduled_at
Note over S: Wait until scheduled_at
S->>Q: Move to main queue
Q->>C: Consumer picks up message
```
## Broker-specific behavior
| Feature | Redis | RabbitMQ | SurrealDB |
| -------------- | ---------- | ------------------- | ---------- |
| `delay` | ✅ | ✅ (requires plugin) | ✅ |
| `scheduled_at` | ✅ | ✅ (requires plugin) | ✅ |
| Precision | \~1 second | \~1 second | \~1 second |
## Best practices
For delays under a few hours, use `delay`:
```rust theme={null}
PublishOptions::builder().delay(Duration::minutes(30)).build()
```
For calendar-based scheduling, use `scheduled_at`:
```rust theme={null}
PublishOptions::builder().schedule_at(next_monday_9am).build()
```
`scheduled_at` uses `OffsetDateTime`. Be explicit about time zones:
```rust theme={null}
use time::{OffsetDateTime, UtcOffset};
// Schedule for 9 AM in UTC-5 (EST)
let est = UtcOffset::from_hms(-5, 0, 0)?;
let scheduled = OffsetDateTime::now_utc()
.to_offset(est)
.replace_time(time::Time::from_hms(9, 0, 0)?);
```
With Redis, check the scheduled queue:
```bash theme={null}
redis-cli ZCARD jobs:scheduled
```
# Errors
Source: https://densumesh-broccoli-27.localhost:3000/api/errors
API reference for BroccoliError and error handling
## BroccoliError
The error type for all Broccoli operations.
```rust theme={null}
use broccoli_queue::error::BroccoliError;
```
### Variants
```rust theme={null}
#[derive(Debug, thiserror::Error)]
pub enum BroccoliError {
/// Broker connection or operation errors
#[error("Broker error: {0}")]
Broker(String),
/// Non-idempotent concurrent operation (SurrealDB)
#[error("Broker error: non-idempotent operation {0}")]
BrokerNonIdempotentOp(String),
/// Retriable non-idempotent operation (SurrealDB)
#[error("Broker error: non-idempotent retriable operation {0}")]
BrokerNonIdempotentRetriableOp(String),
/// Failed to publish message
#[error("Failed to publish message: {0}")]
Publish(String),
/// Failed to consume message
#[error("Failed to consume message: {0}")]
Consume(String),
/// Failed to acknowledge message
#[error("Failed to acknowledge message: {0}")]
Acknowledge(String),
/// Failed to reject message
#[error("Failed to reject message: {0}")]
Reject(String),
/// Failed to cancel message
#[error("Failed to cancel message: {0}")]
Cancel(String),
/// Failed to get message position
#[error("Failed to get message position: {0}")]
GetMessagePosition(String),
/// JSON serialization/deserialization error
#[error("Serialization error: {0}")]
Serialization(#[from] serde_json::Error),
/// Redis-specific error (with redis feature)
#[cfg(feature = "redis")]
#[error("Redis error: {0}")]
Redis(#[from] redis::RedisError),
/// SurrealDB-specific error (with surrealdb feature)
#[cfg(feature = "surrealdb")]
#[error("SurrealDB error: {0}")]
SurrealDB(#[from] surrealdb::Error),
/// Job processing error
#[error("Job error: {0}")]
Job(String),
/// Queue status retrieval error
#[error("Queue status error: {0}")]
QueueStatus(String),
/// Connection timeout
#[error("Connection timeout after {0} retries")]
ConnectionTimeout(u32),
/// Feature not implemented for broker
#[error("Feature not implemented")]
NotImplemented,
}
```
## Error handling
### Basic error handling
```rust theme={null}
use broccoli_queue::error::BroccoliError;
async fn publish_job(queue: &BroccoliQueue, job: &Job) -> Result<(), BroccoliError> {
queue.publish("jobs", None, job, None).await?;
Ok(())
}
// Usage
match publish_job(&queue, &job).await {
Ok(_) => println!("Published successfully"),
Err(BroccoliError::Publish(msg)) => eprintln!("Publish failed: {}", msg),
Err(BroccoliError::Broker(msg)) => eprintln!("Broker error: {}", msg),
Err(e) => eprintln!("Other error: {}", e),
}
```
### In message handlers
```rust theme={null}
queue.process_messages("jobs", Some(4), None, |msg: BrokerMessage| async move {
// Return BroccoliError::Job for business logic failures
if !is_valid(&msg.payload) {
return Err(BroccoliError::Job("Invalid job data".into()));
}
// Process the job
process(&msg.payload).await.map_err(|e| {
BroccoliError::Job(format!("Processing failed: {}", e))
})?;
Ok(())
}).await?;
```
### Converting from other errors
```rust theme={null}
async fn process_job(job: &Job) -> Result<(), BroccoliError> {
// From serde_json::Error
let data: Value = serde_json::from_str(&job.data)?;
// From custom errors
external_service(&data)
.await
.map_err(|e| BroccoliError::Job(e.to_string()))?;
Ok(())
}
```
## Common error scenarios
### Connection errors
```rust theme={null}
let result = BroccoliQueue::builder("redis://invalid:6379")
.build()
.await;
match result {
Err(BroccoliError::Broker(msg)) => {
eprintln!("Failed to connect: {}", msg);
// Handle reconnection or fallback
}
_ => {}
}
```
### Serialization errors
```rust theme={null}
// This would fail if Job doesn't implement Serialize
let result = queue.publish("jobs", None, &invalid_job, None).await;
match result {
Err(BroccoliError::Serialization(e)) => {
eprintln!("Failed to serialize message: {}", e);
}
_ => {}
}
```
### Timeout errors
```rust theme={null}
match result {
Err(BroccoliError::ConnectionTimeout(retries)) => {
eprintln!("Connection timed out after {} retries", retries);
}
_ => {}
}
```
### Feature not implemented
```rust theme={null}
// Some operations aren't available on all brokers
match queue.some_operation().await {
Err(BroccoliError::NotImplemented) => {
eprintln!("This feature isn't supported by your broker");
}
_ => {}
}
```
## Error recovery patterns
### Retry with backoff
```rust theme={null}
async fn publish_with_retry(
queue: &BroccoliQueue,
job: &Job,
max_retries: u32,
) -> Result<(), BroccoliError> {
let mut attempts = 0;
loop {
match queue.publish("jobs", None, job, None).await {
Ok(_) => return Ok(()),
Err(BroccoliError::Broker(_)) if attempts < max_retries => {
attempts += 1;
let delay = std::time::Duration::from_millis(100 * 2_u64.pow(attempts));
tokio::time::sleep(delay).await;
}
Err(e) => return Err(e),
}
}
}
```
### Graceful degradation
```rust theme={null}
async fn process_with_fallback(job: &Job) -> Result<(), BroccoliError> {
match primary_processing(job).await {
Ok(_) => Ok(()),
Err(BroccoliError::Job(_)) => {
// Try fallback processing
fallback_processing(job).await
}
Err(e) => Err(e), // Propagate other errors
}
}
```
## Logging errors
```rust theme={null}
use log::{error, warn};
queue.process_messages("jobs", Some(4), None, |msg: BrokerMessage| async move {
match process_job(&msg.payload).await {
Ok(_) => Ok(()),
Err(e) => {
error!(
"Job {} failed (attempt {}): {:?}",
msg.task_id, msg.attempts, e
);
Err(e)
}
}
}).await?;
```
## Custom error types
If you need richer error information in your handlers:
```rust theme={null}
#[derive(Debug)]
enum JobError {
ValidationFailed(String),
ExternalServiceError(String),
ResourceNotFound(String),
}
impl From for BroccoliError {
fn from(e: JobError) -> Self {
BroccoliError::Job(format!("{:?}", e))
}
}
async fn process_job(job: &Job) -> Result<(), JobError> {
if !is_valid(job) {
return Err(JobError::ValidationFailed("Invalid fields".into()));
}
Ok(())
}
// In handler
queue.process_messages("jobs", Some(4), None, |msg| async move {
process_job(&msg.payload).await.map_err(Into::into)
}).await?;
```
# Messages
Source: https://densumesh-broccoli-27.localhost:3000/api/messages
API reference for BrokerMessage and related types
## BrokerMessage
A wrapper for messages that includes metadata for processing.
```rust theme={null}
use broccoli_queue::brokers::broker::BrokerMessage;
```
### Structure
```rust theme={null}
pub struct BrokerMessage {
/// Unique identifier for the message
pub task_id: uuid::Uuid,
/// The actual message content
pub payload: T,
/// Number of processing attempts made
pub attempts: u8,
/// Disambiguator for message fairness
pub disambiguator: Option,
}
```
### Fields
| Field | Type | Description |
| --------------- | ---------------- | ---------------------------------------------------- |
| `task_id` | `Uuid` | Unique message identifier, auto-generated on publish |
| `payload` | `T` | Your custom message data |
| `attempts` | `u8` | Number of times this message has been processed |
| `disambiguator` | `Option` | Optional identifier for fairness queue routing |
### Creating messages
Messages are automatically created when publishing:
```rust theme={null}
let job = JobPayload { id: "1".into(), task: "process".into() };
// publish() creates and returns the BrokerMessage
let message = queue.publish("jobs", None, &job, None).await?;
println!("Task ID: {}", message.task_id);
```
### Accessing message data
```rust theme={null}
queue.process_messages("jobs", Some(1), None, |msg: BrokerMessage| async move {
// Access metadata
println!("ID: {}", msg.task_id);
println!("Attempts: {}", msg.attempts);
println!("Disambiguator: {:?}", msg.disambiguator);
// Access payload
let job = &msg.payload;
println!("Job: {} - {}", job.id, job.task);
Ok(())
}).await?;
```
***
## Payload requirements
Your message payload type must implement:
* `Clone`
* `serde::Serialize`
* `serde::Deserialize`
### Example payload
```rust theme={null}
use serde::{Serialize, Deserialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JobPayload {
pub id: String,
pub task_name: String,
pub parameters: serde_json::Value,
pub created_at: chrono::DateTime,
}
```
### Complex payload
```rust theme={null}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmailJob {
pub recipient: String,
pub subject: String,
pub body: String,
pub attachments: Vec,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Attachment {
pub filename: String,
pub content_type: String,
#[serde(with = "base64_serde")]
pub data: Vec,
}
```
***
## InternalBrokerMessage
Internal message representation used by broker implementations. You typically don't interact with this directly.
```rust theme={null}
pub struct InternalBrokerMessage {
pub task_id: String,
pub payload: String, // JSON serialized
pub attempts: u8,
pub disambiguator: Option,
}
```
***
## BrokerConfig
Configuration options for broker behavior.
```rust theme={null}
pub struct BrokerConfig {
/// Maximum retry attempts (default: 3)
pub retry_attempts: Option,
/// Whether to retry failed messages (default: true)
pub retry_failed: Option,
/// Connection pool size (default: 10)
pub pool_connections: Option,
/// Enable message scheduling (default: false)
pub enable_scheduling: Option,
}
```
### Default values
```rust theme={null}
impl Default for BrokerConfig {
fn default() -> Self {
Self {
retry_attempts: Some(3),
retry_failed: Some(true),
pool_connections: Some(10),
enable_scheduling: Some(false),
}
}
}
```
***
## BrokerType
Enum representing supported broker types.
```rust theme={null}
pub enum BrokerType {
#[cfg(feature = "redis")]
Redis,
#[cfg(feature = "rabbitmq")]
RabbitMQ,
#[cfg(feature = "surrealdb")]
SurrealDB,
}
```
***
## Broker trait
The `Broker` trait defines the interface that all broker implementations must satisfy. This is internal to Broccoli but useful for understanding the abstraction.
```rust theme={null}
#[async_trait]
pub trait Broker: Send + Sync {
async fn connect(&mut self, broker_url: &str) -> Result<(), BroccoliError>;
async fn publish(
&self,
queue_name: &str,
disambiguator: Option,
message: &[InternalBrokerMessage],
options: Option,
) -> Result, BroccoliError>;
async fn consume(
&self,
queue_name: &str,
options: Option,
) -> Result;
async fn try_consume(
&self,
queue_name: &str,
options: Option,
) -> Result