Reference

Migration Guide v0.7 to v0.8

This guide covers migrating from acton-service v0.7.x to v0.8.x. Version 0.8 introduces reactive agent-based pool management, type-safe request IDs, and the BackgroundWorker agent.

Non-Breaking Changes

Most changes in v0.8 are additive and do not require code changes. Your existing v0.7 code should compile and work with v0.8. The reactive agent system works transparently behind the scenes.


What's New in v0.8

Reactive Agent Architecture

Connection pools (database, Redis, NATS) are now managed by internal agents:

  • Automatic health monitoring - Agents report pool health to /ready endpoint
  • Graceful shutdown - Coordinated connection cleanup on shutdown
  • Transparent operation - Pools are still accessed via state.db(), state.redis(), etc.

You don't need to change any code - agents work behind the scenes. See Reactive Architecture for details.

TypeID Request IDs

Request IDs now use the TypeID specification with UUIDv7:

# Old format (UUID)
550e8400-e29b-41d4-a716-446655440000

# New format (TypeID with req_ prefix)
req_01h455vb4pex5vsknk084sn02q

Benefits:

  • Time-sortable (UUIDv7)
  • Type-safe prefix (req_)
  • Better log correlation

See Request IDs for the full API.

BackgroundWorker Agent

New managed background task execution:

use acton_service::agents::{BackgroundWorker, TaskStatus};

let worker = BackgroundWorker::spawn(&mut runtime).await?;

worker.submit("my-task", || async {
    do_work().await?;
    Ok(())
}).await;

let status = worker.get_task_status("my-task").await;

See Background Worker for the complete guide.

Event Broker Support

AppState now includes an optional broker handle for event-driven architectures:

// Access the broker (if configured)
if let Some(broker) = state.broker() {
    broker.send(MyEvent { ... }).await;
}

Migration Steps

Step 1: Update Dependencies

Update Cargo.toml:

[dependencies]
acton-service = "0.8"  # Was: "0.7"

Step 2: Verify Compilation

Run cargo check to verify your code compiles. Most v0.7 code works unchanged:

cargo check

If you get errors, see the "Breaking Changes" section below.

Step 3: Update Pool Access (Optional)

The pool access methods are now async and return Option:

// v0.7 pattern (still works)
async fn handler(State(state): State<AppState>) -> Result<Json<Data>> {
    let db = state.db().await.ok_or(Error::Internal("DB unavailable"))?;
    // ...
}

// v0.8 preferred pattern (identical, but clearer intent)
async fn handler(State(state): State<AppState>) -> Result<Json<Data>> {
    let db = state.db()
        .ok_or_else(|| Error::Internal("Database unavailable".into()))?;
    // ...
}

Step 4: Use TypeID Request IDs (Optional)

If you were manually creating request IDs, update to use RequestId:

// v0.7 - manual UUID
use uuid::Uuid;
let request_id = Uuid::new_v4().to_string();

// v0.8 - TypeID with req_ prefix
use acton_service::ids::RequestId;
let request_id = RequestId::new();  // e.g., req_01h455vb4pex5vsknk084sn02q

Step 5: Test Your Application

Run your tests to verify everything works:

cargo test

Breaking Changes

None in v0.8

Version 0.8 has no breaking changes for typical usage. The following changes are technically breaking but unlikely to affect most users:

Internal Module Reorganization

If you were importing internal types directly, paths may have changed:

// If you were using internal paths (unlikely)
// use acton_service::internal_module::SomeType;

// Use public re-exports instead
use acton_service::prelude::*;

Request ID Header Value Format

The x-request-id header now uses TypeID format. If your clients parse request IDs, they should handle both formats:

// Client-side handling
const requestId = response.headers.get('x-request-id');
// Old: "550e8400-e29b-41d4-a716-446655440000"
// New: "req_01h455vb4pex5vsknk084sn02q"

New Features to Adopt

TypeID Request IDs

Access the typed request ID in handlers:

use acton_service::ids::RequestId;
use axum::Extension;

async fn handler(
    Extension(request_id): Extension<RequestId>,
) -> impl IntoResponse {
    // Time-sortable, typed request ID
    tracing::info!(request_id = %request_id, "Processing");
    // ...
}

BackgroundWorker for Tasks

Replace ad-hoc tokio::spawn with managed workers:

use acton_service::agents::{BackgroundWorker, TaskStatus};

// Setup
let worker = BackgroundWorker::spawn(&mut runtime).await?;

// Submit tasks
worker.submit("cleanup", || async {
    cleanup_old_data().await
}).await;

// Monitor
let status = worker.get_task_status("cleanup").await;

// Cancel if needed
worker.cancel("cleanup").await;

Health Monitoring

Pool health is now automatically monitored. Check aggregated status:

curl http://localhost:8080/ready
# Returns pool health for all configured connections

Dependency Updates

Version 0.8 updates these dependencies:

Dependencyv0.7v0.8
acton-reactive-0.1+
mti (TypeID)-0.1+
dashmap-5.5+
tokio-util0.70.7+

The acton-reactive dependency is new and provides the agent framework.


Configuration Changes

No configuration changes are required. The following new options are available:

ServiceBuilder Changes

ServiceBuilder::build() now automatically spawns pool agents when connections are configured:

// This is unchanged - agents are spawned automatically
ServiceBuilder::new()
    .with_config(config)  // If database/redis/nats configured...
    .with_routes(routes)
    .build()  // ...agents are spawned here
    .serve()
    .await

Rollback Plan

If you encounter issues with v0.8, you can rollback to v0.7:

[dependencies]
acton-service = "0.7"  # Rollback version

Getting Help

Previous
Feature Flags