Multiple environments
Run isolated CocoIndex environments side by side with explicit Environment builders and separate db_paths — for multi-tenancy, library development, and testing.
Environment::from_registered() or a builder. You can create multiple isolated environments when you need:
- Library development: Libraries that depend on CocoIndex can use their own environment to avoid sharing state with other libraries or the application
- Database isolation: Different apps using separate databases
- Multi-tenant deployments: Isolated data per tenant
- Testing: Isolated test environments that don’t interfere with production
Creating an Environment
Create an explicit environment with Environment::builder() and a db_path:
use cocoindex::Environment;
let env = Environment::builder()
.db_path("./my_db.db")
.build()
.await?;An Environment built this way reads no environment variables. Environment::from_registered() is the path that runs the #[cocoindex::lifespan] functions and falls back to COCOINDEX_DB; it is what the cocoindex CLI uses.
The builder also has lmdb_max_dbs(..) and lmdb_map_size(..) for tuning the internal LMDB database. See Internal Storage for details.
An Environment has no name of its own: the name a handler sees in ExceptionContext::env_name is the app’s name.
Associating Apps with an Environment
Create the app from the environment:
let app = env.app("MyApp").await?;Several apps can share one environment: each name is its own LMDB namespace, and they share the environment’s provided resources. App::open(name, db_path) and App::builder(name) build a single-app environment for you.
There is no implicit default environment: an App always comes from an Environment you built, or from Environment::from_registered(), which runs the #[cocoindex::lifespan] functions and reads COCOINDEX_DB when no lifespan set a db path.
Closing an Environment
An environment holds its database open, and LMDB allows one environment per db_path per process: while one is open, building another on the same db_path fails with an error that says the database “is already open in this process”.
Call env.shutdown().await once you’re done with it to close the database:
env.shutdown().await;
let env = Environment::builder().db_path("./my_db.db").build().await?;shutdown() runs the on_shutdown hooks registered on the builder, most recently registered first, then closes the state db. Await every running update or drop before calling it.
Example: Multiple Environments
This example creates two apps in different environments, each with its own database:
use cocoindex::prelude::*;
#[cocoindex::function]
async fn build1(_ctx: &Ctx) -> Result<()> {
// ... pipeline logic for env1 ...
Ok(())
}
#[cocoindex::function]
async fn build2(_ctx: &Ctx) -> Result<()> {
// ... pipeline logic for env2 ...
Ok(())
}
// Two environments with separate databases
let env1 = Environment::builder().db_path("./db1/cocoindex.db").build().await?;
let env2 = Environment::builder().db_path("./db2/cocoindex.db").build().await?;
// Apps in different environments
let app1 = env1.app("App1").await?;
let app2 = env2.app("App2").await?;
app1.run(|ctx| async move { build1(&ctx).await }).await?;
app2.run(|ctx| async move { build2(&ctx).await }).await?;Same App Name in Different Environments
Apps with the same name can coexist in different environments. This is useful for multi-tenant scenarios or running the same pipeline against different databases: env_alpha.app("MyApp") and env_default.app("MyApp") are two apps with their own state, one per environment.
The cocoindex CLI for Rust drives the one environment that Environment::from_registered() builds, so it has no @env_name syntax.
Testing with Isolated Environments
For tests, create isolated environments to avoid interference between test runs:
async fn temp_app(name: &str) -> (App, tempfile::TempDir) {
let dir = tempfile::tempdir().unwrap();
let app = App::builder(name)
.db_path(dir.path().join(".cocoindex_db"))
.build()
.await
.unwrap();
(app, dir)
}
#[tokio::test]
async fn my_pipeline() {
let (app, _dir) = temp_app("TestApp").await;
app.update(|ctx| async move { my_main_fn(&ctx).await }).await.unwrap();
// ... assertions ...
}Keep the TempDir alive for the test: dropping it deletes the database directory.
When to Use Multiple Environments
| Use Case | Approach |
|---|---|
| Single app, single database | App::open(name, db_path) or App::builder(name) (a single-app environment) |
| Multiple apps sharing state | One environment, several env.app(..) |
| Apps needing separate databases | Explicit environments with different db_path |
| Multi-tenant isolation | One environment per tenant |
| Testing | Temporary isolated environments |