Instance Cache

Requires the duckdb-1-5 feature flag (DuckDB 1.5.0+).

An InstanceCache wraps DuckDB's duckdb_instance_cache, which lets several database handles share one underlying DuckDB instance per database path. Opening the same path twice through the cache returns handles backed by the same instance, which avoids the "database is already open in another instance" conflict and saves the cost of re-initialising the database.

This is primarily useful for extensions or host integrations that open secondary databases on behalf of a query.

Opening through the cache

#![allow(unused)]
fn main() {
use quack_rs::instance_cache::InstanceCache;

fn demo() -> Result<(), quack_rs::error::ExtensionError> {
let cache = InstanceCache::new();

// Returns a duckdb_database the caller OWNS and must close with duckdb_close.
let db = cache.get_or_create(c"analytics.db", None)?;
let _ = db;
Ok(())
}
}

Pass a DbConfig to control how a freshly created instance is configured. When an instance already exists for the path, the config must match the one it was created with — a different one is an error ("Can't open a connection to same database file with a different configuration than existing connections"), not silently ignored. None means DuckDB's defaults, so it too conflicts with an instance created with a custom config. An empty path or :memory: is never cached: each call creates a separate in-memory database.

#![allow(unused)]
fn main() {
use quack_rs::instance_cache::InstanceCache;
use quack_rs::config::DbConfig;

fn demo() -> Result<(), quack_rs::error::ExtensionError> {
let cache = InstanceCache::new();
let config = DbConfig::new()?;
// configure `config` as needed...
let db = cache.get_or_create(c"analytics.db", Some(&config))?;
let _ = db;
Ok(())
}
}

API

MethodDescription
InstanceCache::new()Create a new, empty cache
get_or_create(path, config)Open path, creating the instance if needed
as_raw()The raw duckdb_instance_cache handle

get_or_create returns Result<duckdb_database, ExtensionError>; on failure the error carries DuckDB's message.

Threads

InstanceCache is Send + Sync, so one cache can serve several threads: DuckDB's DBInstanceCache guards its map with a mutex and serialises creation of each database. One ordering hazard remains, and it is DuckDB's: while the last handle to a cached file database is being closed, a concurrent get_or_create of the same path can fail with "Unique file handle conflict" because the closing instance still holds the file. Keep one handle open while other threads may open the path, or retry.

Ownership

InstanceCache is RAII and destroys the cache on drop. The duckdb_database returned by get_or_create is, however, owned by the caller: close it with duckdb_close when finished. The cache holds only a weak reference to each instance. An instance stays alive while at least one handle opened through the cache is open, and a handle stays valid after the cache itself is dropped. Once the last handle is closed, the next get_or_create for that path creates a fresh instance.

  • config — DbConfig, the RAII configuration builder accepted here