Skip to main content

BosonBuilder

Struct BosonBuilder 

Source
pub struct BosonBuilder { /* private fields */ }
Expand description

Builder for Boson.

Required: a queue backend (queue_backend or queue_backend_from_global) and an execution_context_factory. For #[task] handlers on a worker process, call auto_registry so inventory entries from linked crates are collected.

Topology (see the boson crate Getting started):

ModeBuilder posture
Mode 1 — Embeddedauto_registry + build (or build_manual in tests)
Mode 2 — Enqueue hostauto_registry + without_worker + build + configure
Mode 2 — Workerworker_id + lease_ttl_secs (> 0) + auto_registry + build

Optional: ops_log / ops_log_console for telemetry (OpsLog).

§Examples

§Mode 1 — embedded (enqueue + worker in one process)

After build, call configure if callers use macro send_with (not required when holding Boson and calling Boson::enqueue directly).

use std::sync::Arc;

use boson_backend_mem::MemQueueBackend;
use boson_core::JsonExecutionContextFactory;
use boson_runtime::{configure, Boson};

let boson = Boson::builder()
    .queue_backend(Arc::new(MemQueueBackend::new()))
    .execution_context_factory(JsonExecutionContextFactory)
    .auto_registry()
    .build()?;
configure(boson);

§Mode 2 — enqueue-only host

use std::sync::Arc;

use boson_backend_mem::MemQueueBackend;
use boson_core::JsonExecutionContextFactory;
use boson_runtime::{configure, Boson};

// Production Mode 2: use Sqlite/Postgres/Redis/NATS — mem cannot cross processes.
let boson = Boson::builder()
    .queue_backend(Arc::new(MemQueueBackend::new()))
    .execution_context_factory(JsonExecutionContextFactory)
    .auto_registry()
    .without_worker()
    .build()?;
configure(boson);

Implementations§

Source§

impl BosonBuilder

Source

pub fn build(self) -> Result<Boson>

Build Boson and optionally spawn the background worker loop.

When without_worker was not set (default), a Tokio task polls queued jobs, claims them, dispatches registered handlers (from auto_registry or registry), and applies retry policy — Mode 1 embedded or Mode 2 worker.

When without_worker was set, no claim loop starts; use this for Mode 2 enqueue hosts, then configure + send_with.

Enqueue with Boson::enqueue or macro send_with after configure. For step-driven tests, use build_manual instead.

Getting started: Mode 1 / Mode 2.

§Example — Mode 1 embedded
use std::sync::Arc;

use boson_backend_mem::MemQueueBackend;
use boson_core::JsonExecutionContextFactory;
use boson_runtime::{configure, Boson};

let boson = Boson::builder()
    .queue_backend(Arc::new(MemQueueBackend::new()))
    .execution_context_factory(JsonExecutionContextFactory)
    .auto_registry()
    .build()?; // worker loop runs in the background
configure(boson);
§Errors

Returns BosonError::InvalidConfig if no execution context factory was set, or an error if the queue backend cannot be resolved.

Source

pub fn build_manual(self) -> Result<(Boson, ManualWorker)>

Build without a background worker; returns ManualWorker for step-driven execution.

Prefer this in tests when you want to enqueue then call ManualWorker::try_run_next. For Mode 2 enqueue-only production hosts that never drain locally, prefer without_worker

  • build instead (no ManualWorker handle needed).
§Example — enqueue then drain one job
use std::sync::Arc;

use boson_backend_mem::MemQueueBackend;
use boson_core::{ExecutionContext, JsonExecutionContextFactory};
use boson_macros::task;
use boson_runtime::{configure, Boson, ManualWorker};

#[task(name = "ping")]
async fn ping(_ctx: Box<dyn ExecutionContext>, n: u32) -> boson_core::Result<()> {
    let _ = n;
    Ok(())
}

let (boson, manual): (_, ManualWorker) = Boson::builder()
    .queue_backend(Arc::new(MemQueueBackend::new()))
    .execution_context_factory(JsonExecutionContextFactory)
    .auto_registry()
    .without_worker()
    .build_manual()?;
configure(boson);
Ping::send_with(serde_json::json!({}), PingParams { n: 1 }).await?;
assert!(manual.try_run_next().await);
§Errors

Returns BosonError::InvalidConfig if no execution context factory was set, or an error if the queue backend cannot be resolved.

Source§

impl BosonBuilder

Source

pub fn worker_id(self, worker_id: impl Into<String>) -> Self

Worker identity for lease claims (default: INSTANCE_ID / BOSON_WORKER_ID / boson-worker-1).

Required to be unique per process in Mode 2 (multiple workers sharing a backend). See WorkerSettings and the Mode 2 section on the boson crate.

§Example
use std::sync::Arc;

use boson_backend_mem::MemQueueBackend;
use boson_core::JsonExecutionContextFactory;
use boson_runtime::Boson;

let _boson = Boson::builder()
    .queue_backend(Arc::new(MemQueueBackend::new()))
    .execution_context_factory(JsonExecutionContextFactory)
    .worker_id("worker-a")
    .lease_ttl_secs(30)
    .auto_registry()
    .build()?;
Source

pub const fn lease_ttl_secs(self, secs: i64) -> Self

Run lease TTL in seconds; when > 0, claim path acquires leases before job claim.

Use 0 (default) for Mode 1 embedded monoliths. Use a positive value for Mode 2 so workers do not double-execute the same run. Env override: BOSON_LEASE_TTL_SECS.

See WorkerSettings.

Source

pub fn runtime_label(self, label: impl Into<String>) -> Self

Telemetry/runtime label (default embedded; bench uses topology slug).

Source

pub fn worker_pools( self, pools: impl IntoIterator<Item = impl Into<String>>, ) -> Self

Restrict this worker to specific pools (comma-free list). Unset = poll all queued pools.

Also available via BOSON_WORKER_POOLS=pool-a,pool-b. Pin workers to disjoint pool sets for shared-nothing scaling (each worker skips distinct_pools_queued fan-out).

Source

pub const fn worker_poll_interval_ms(self, ms: u64) -> Self

Milliseconds between worker poll ticks (default 50; use 0 for bench drain tests).

Source

pub const fn idempotency_mode(self, mode: IdempotencyMode) -> Self

Default enqueue idempotency mode when a task does not override it.

boson_core::IdempotencyMode::Lwt (default) is exactly-once under concurrent enqueue. boson_core::IdempotencyMode::None is at-least-once and skips coordination (higher throughput).

Source

pub fn queue_backend(self, backend: Arc<dyn QueueBackend>) -> Self

Inject queue persistence backend explicitly.

Pick the backend for your topology: MemQueueBackend for Mode 1 only; SQLite/Postgres/Redis/NATS when processes share a queue. See the boson crate backend table.

Source

pub fn queue_backend_from_global(self) -> Self

Use global QueueRouter default backend.

Source

pub fn execution_context_factory( self, factory: impl ExecutionContextFactory + 'static, ) -> Self

Identity factory for handler dispatch.

Maps stored actor_json to Box<dyn ExecutionContext> when a worker runs a job. For examples and smoke tests, pass JsonExecutionContextFactory; production apps typically implement ExecutionContextFactory.

§Example
use std::sync::Arc;

use boson_backend_mem::MemQueueBackend;
use boson_core::JsonExecutionContextFactory;
use boson_runtime::Boson;

let _boson = Boson::builder()
    .queue_backend(Arc::new(MemQueueBackend::new()))
    .execution_context_factory(JsonExecutionContextFactory)
    .build()?;
Source

pub fn execution_context_factory_arc( self, factory: Arc<dyn ExecutionContextFactory>, ) -> Self

Identity factory from existing Arc.

Source

pub fn ops_log(self, log: impl OpsLog + 'static) -> Self

Install ops log adapter (default boson_telemetry::NoOpsLog).

Prefer this or ops_log_console over installing an OpsLog ad hoc. See boson_telemetry::OpsLog for adapter choices.

Source

pub fn ops_log_console(self) -> Self

Use console stderr ops log (ConsoleOpsLog).

Source

pub fn registry(self, registry: Arc<TaskRegistry>) -> Self

Use an existing task registry (e.g. testkit manual registration).

Source

pub const fn auto_registry(self) -> Self

Discover tasks registered via Quark inventory (for example #[boson::task]).

Worker processes need this (or registry) so handlers are available to the claim loop. Enqueue hosts also need it (or a manual registry) because send_with / Boson::enqueue resolve task descriptors for priority, pool, and policies — they do not run handlers.

The binary must link every crate that defines inventory submissions; otherwise tasks defined in library crates will not appear in the registry. Add the task-owning crate as a dependency (for example use my_worker as _;).

Getting started: Mode 1 / Mode 2.

§Example
use std::sync::Arc;

use boson_backend_mem::MemQueueBackend;
use boson_core::{ExecutionContext, JsonExecutionContextFactory};
use boson_macros::task;
use boson_runtime::{configure, Boson};

#[task(name = "ping")]
async fn ping(_ctx: Box<dyn ExecutionContext>) -> boson_core::Result<()> {
    Ok(())
}

// When handlers live in a library crate, link it from `main`:
// use my_worker as _;

let boson = Boson::builder()
    .queue_backend(Arc::new(MemQueueBackend::new()))
    .execution_context_factory(JsonExecutionContextFactory)
    .auto_registry()
    .build()?;
configure(boson);
Source

pub const fn without_worker(self) -> Self

Do not spawn the background worker loop.

Use for:

  • Mode 2 enqueue hosts — this process only configures and calls send_with; a separate worker binary drains the shared backend
  • Tests — pair with build_manual and ManualWorker

Getting started: Mode 2 — enqueue binary.

§Example — enqueue-only process
use std::sync::Arc;

use boson_backend_mem::MemQueueBackend;
use boson_core::JsonExecutionContextFactory;
use boson_runtime::{configure, Boson};

let boson = Boson::builder()
    .queue_backend(Arc::new(MemQueueBackend::new()))
    .execution_context_factory(JsonExecutionContextFactory)
    .auto_registry()
    .without_worker()
    .build()?;
configure(boson);

Trait Implementations§

Source§

impl Default for BosonBuilder

Source§

fn default() -> BosonBuilder

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.