# Multiple environments

> **CocoIndex v1, Rust SDK (experimental).** This page documents the CocoIndex **v1** Rust SDK (the `cocoindex` crate). The Rust SDK is experimental: any release may change or remove its API, and it is not yet published on crates.io. The Python SDK pages are at https://cocoindex.io/docs/.
>
> Source: https://cocoindex.io/docs/rust/advanced_topics/multiple_environments/ · Rust docs index: https://cocoindex.io/docs/rust/llms.txt · Rust SDK tour: https://github.com/cocoindex-io/cocoindex/blob/main/rust/sdk/SHOWCASE.md · Rust examples: https://github.com/cocoindex-io/cocoindex/tree/main/examples/rust

An environment manages a database and the context values shared by its apps. Most programs build one environment, through `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`:

```rust
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](/docs/rust/advanced_topics/internal_storage/#lmdb-tuning) 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:

```rust
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:

```rust
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:

```rust
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:

```rust
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 |
