How to Configure io_uring for Asynchronous I/O in Turso on Linux

Enable the io_uring Cargo feature at compile time and select the "io_uring" VFS at runtime to replace Turso's default syscall backend with Linux's high-performance ring-based asynchronous I/O interface.

Turso, the edge-optimized database from tursodatabase/turso, provides an optional io_uring backend that replaces the standard synchronous VFS with a high-performance, ring-based interface. This Linux-only implementation, centered on the UringIO type in core/io/io_uring.rs, reduces system-call overhead through batched submissions and zero-copy buffers. Configuring it requires both compile-time feature selection and runtime VFS activation.

Prerequisites and Feature Gating

The io_uring backend is strictly Linux-only and gated behind the io_uring Cargo feature. In core/Cargo.toml, this feature pulls in the external io-uring crate and enables the rustix/io_uring implementation. The code is wrapped in conditional compilation: #[cfg(all(target_os = "linux", feature = "io_uring", not(miri)))].

If the target platform is not Linux, the code is excluded entirely. Attempting to use the "io_uring" VFS on non-Linux platforms results in a runtime error: "io_uring is only available on Linux targets" (see sdk-kit/src/rsapi.rs).

How the io_uring Backend Works

The architecture centers on the UringIO struct defined in core/io/io_uring.rs. When initialized via UringIO::new() (lines 95-134), it creates an io_uring::IoUring instance, registers a sparse buffer arena for fixed-buffer I/O, and probes kernel opcode support.

Submission and Completion Flow

All file operations—open_file, pread, pwrite, pwritev, sync, and truncate—build io_uring submission queue entries (SQEs) and push them into a shared RingState via submit_entry. A single leader thread serializes the submit_and_wait syscall while draining completion queue entries (CQEs).

When a CQE arrives, the user_data key maps back to a Completion via completion_from_key, waking the associated future. Write-vectors (pwritev) requiring multiple kernel calls are automatically resubmitted, and the future is woken with wake_user_data.

Performance Characteristics

The ring batches up to 512 entries (ENTRIES = 512) and waits for up to 4 completions (MAX_WAIT = 4) per syscall, significantly cutting io_uring_enter syscalls. Fixed-buffer registration allows the kernel to operate directly on pre-registered memory, eliminating extra copies for reads and writes.

Enabling the io_uring Backend

Follow these steps to configure the backend:

  1. Compile with the feature flag. Build Turso using cargo build --features=io_uring or add io_uring = true to your workspace profile. On non-Linux targets, this code is excluded automatically.

  2. Select the VFS at runtime. Pass the string "io_uring" to the database builder. In core/lib.rs, the factory maps this string to Arc::new(UringIO::new()?). The SDK exposes this as builder.with_io("io_uring".to_string()), while the CLI accepts tursodb --vfs io_uring.

  3. Verify initialization. Check the debug logs for the message Using IO backend 'io-uring' emitted by UringIO::new. If this line does not appear, the system has fallen back to the default syscall VFS.

Fallback Behavior

If the kernel lacks support for specific opcodes (e.g., IORING_OP_FTRUNCATE), Turso logs a warning and executes the operation via synchronous POSIX syscalls, ensuring correctness on older kernels.

Code Examples

Rust SDK Configuration

use turso_sdk_kit::DatabaseBuilder;

fn main() -> turso_sdk_kit::Result<()> {
    // Selects the io_uring VFS registered in core/lib.rs
    let db = DatabaseBuilder::new()
        .with_path("/var/lib/turso/my.db")
        .with_io("io_uring".to_string())
        .open()?;
    
    // All subsequent I/O uses the io_uring ring via UringIO
    let rows = db.query("SELECT * FROM users", ())?;
    Ok(())
}

CLI Usage

tursodb \
    --path /var/lib/turso/my.db \
    --vfs io_uring \
    repl

Integration Testing

// From tests/integration/query_processing/test_write_path.rs
let db = TestBuilder::new()
    .with_io_uring(true)
    .open();
db.execute("PRAGMA journal_mode=WAL").unwrap();
db.execute("INSERT INTO t (id) VALUES (1)").unwrap();
// Internally uses UringIO::pwritev for the write path

Summary

  • The io_uring backend is available only on Linux and requires the io_uring Cargo feature.
  • The implementation lives in core/io/io_uring.rs and registers the VFS name "io_uring" in core/lib.rs and core/io/mod.rs under BUILTIN_VFS_NAMES.
  • Runtime selection uses .with_io("io_uring") in the SDK or --vfs io_uring in the CLI.
  • The backend batches up to 512 submissions and uses fixed buffers to minimize syscall overhead, gracefully falling back to POSIX syscalls for unsupported kernel opcodes.

Frequently Asked Questions

Is io_uring available on macOS or Windows?

No. The io_uring backend is strictly Linux-only. Attempting to use "io_uring" on other platforms triggers a runtime error stating that "io_uring is only available on Linux targets" as implemented in sdk-kit/src/rsapi.rs. Windows users should use the experimental_win_iocp VFS instead.

What happens if my kernel does not support io_uring?

If the kernel lacks support for specific io_uring opcodes (detected via the Probe during UringIO::new), Turso logs a warning and falls back to synchronous POSIX syscalls for those operations. The database remains functional but without the performance benefits of the ring-based interface.

How do I verify that Turso is actually using io_uring?

Check the debug logs for the message Using IO backend 'io-uring' emitted by UringIO::new in core/io/io_uring.rs. If this line does not appear, verify that you compiled with the io_uring feature and explicitly selected the "io_uring" VFS at runtime.

Does io_uring improve performance for all workloads?

io_uring primarily benefits workloads with high concurrent I/O or large batch operations by reducing syscall overhead and enabling zero-copy transfers via fixed-buffer registration. Single-threaded, low-concurrency workloads may see minimal improvement, though the batching of submissions still reduces context switches.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →