# How ChaosBlade Uses SQLite to Track Experiment State: Internal Database Mechanisms

> Discover how ChaosBlade uses SQLite to track experiment state. Learn about internal database mechanisms managing experiment lifecycles and statuses in this technical deep dive.

- Repository: [ChaosBlade/chaosblade](https://github.com/chaosblade-io/chaosblade)
- Tags: internals
- Published: 2026-02-27

---

**ChaosBlade persists every chaos experiment in a local SQLite file (`chaosblade.dat`) using the `glebarez/sqlite` driver, tracking lifecycle states through a singleton `data.Source` interface that manages the `experiment` table with columns for UID, status, error messages, and timestamps.**

The `chaosblade-io/chaosblade` project implements a lightweight persistence layer to track the complete lifecycle of chaos engineering experiments. By leveraging a local SQLite database, the tool maintains a single source of truth for experiment state without requiring external database dependencies. This article examines how the SQLite database tracks experiment state through the `data` package, revealing the schema design, state transition logic, and CRUD operations that power ChaosBlade's experiment management.

## SQLite Database Architecture in ChaosBlade

### Database File Location and Configuration

The SQLite database file path is resolved by the `GetDataFilePath()` function in [`data/source.go`](https://github.com/chaosblade-io/chaosblade/blob/main/data/source.go). By default, the system creates a file named `chaosblade.dat` in the working directory. Administrators can override this location by setting the `CHAOSBLADE_DATAFILE_PATH` environment variable before starting the ChaosBlade CLI or server.

### The Data Source Singleton Pattern

ChaosBlade opens the database through the standard `database/sql` package using the `glebarez/sqlite` driver. The `GetSource()` function implements a singleton pattern, ensuring that all components share a single `data.Source` instance. This singleton manages the database connection and provides thread-safe access to the experiment table across the application.

## How the Experiment Table Tracks State

### Table Schema and Initialization

The `experiment` table is created automatically on first run via `CheckAndInitExperimentTable()` in [`data/experiment.go`](https://github.com/chaosblade-io/chaosblade/blob/main/data/experiment.go). The initialization logic executes `CREATE TABLE IF NOT EXISTS experiment` with columns for `uid`, `command`, `sub_command`, `flag`, `status`, `error`, `create_time`, and `update_time`. This schema stores a single row per experiment, capturing the complete configuration and current state.

### Status Column and Lifecycle Values

The `status` column implements the state machine for experiment lifecycle management. Valid values include `Created`, `Running`, `Success`, `Error`, and `Destroyed`. State transitions occur through the `UpdateExperimentModelByUid()` function, which atomically updates both the status and the `update_time` timestamp. This ensures that concurrent operations maintain consistency in the SQLite database track experiment state.

### Error Handling and Audit Trails

When experiments fail, the `error` column stores the error message string alongside the status update. The `create_time` and `update_time` columns use RFC-3339 Nano format timestamps, providing a chronological audit trail for every state change. This design enables post-mortem analysis by preserving the exact sequence of events and any error messages generated during execution.

## Code Examples: Working with Experiment State

### Initializing the Database and Table

```go
src := data.GetSource()               // singleton creation
src.CheckAndInitExperimentTable()    // auto-creates table if absent

```

Relevant source: [`data/source.go`](https://github.com/chaosblade-io/chaosblade/blob/main/data/source.go) lines 50-59.

### Creating a New Experiment Record

```go
model := &data.ExperimentModel{
    Uid:        uid,
    Command:    cmd,
    SubCommand: sub,
    Flag:       flag,
    Status:     "Created",
    CreateTime: time.Now().Format(time.RFC3339Nano),
    UpdateTime: time.Now().Format(time.RFC3339Nano),
}
src.InsertExperimentModel(model)

```

Relevant source: `InsertExperimentModel` in [`data/experiment.go`](https://github.com/chaosblade-io/chaosblade/blob/main/data/experiment.go) lines 42-48.

### Updating Status During Execution

```go
src.UpdateExperimentModelByUid(uid, "Running", "")
// … after execution
src.UpdateExperimentModelByUid(uid, "Success", "")
// or on error
src.UpdateExperimentModelByUid(uid, "Error", errMsg)

```

Relevant source: `UpdateExperimentModelByUid` in [`data/experiment.go`](https://github.com/chaosblade-io/chaosblade/blob/main/data/experiment.go) lines 64-68.

### Querying Experiment State

```go
exp, _ := src.QueryExperimentModelByUid(uid)
fmt.Printf("UID %s – status: %s, error: %s\n", exp.Uid, exp.Status, exp.Error)

```

Relevant source: `QueryExperimentModelByUid` in [`data/experiment.go`](https://github.com/chaosblade-io/chaosblade/blob/main/data/experiment.go) lines 80-98.

### Deleting Completed Experiments

```go
src.DeleteExperimentModelByUid(uid)

```

Relevant source: `DeleteExperimentModelByUid` in [`data/experiment.go`](https://github.com/chaosblade-io/chaosblade/blob/main/data/experiment.go) lines 4-11.

## State Transitions During the Experiment Lifecycle

The SQLite database track experiment state through a well-defined lifecycle managed by the `exec` package executors. The flow follows these stages:

1. **Bootstrap** – The first call to `GetSource()` creates the singleton `Source` instance and opens the SQLite file using `sql.Open("sqlite", GetDataFilePath())`.

2. **Table Initialization** – `CheckAndInitExperimentTable()` verifies existence via `ExperimentTableExists()`; if missing, `InitExperimentTable()` executes the DDL to create the `experiment` table.

3. **Creation** – When users run `blade create`, the system calls `InsertExperimentModel` to persist a row with status `Created`, capturing the command, flags, and timestamps.

4. **Execution** – Executors in `exec/*/executor.go` invoke `UpdateExperimentModelByUid(uid, "Running", "")` before injecting faults. Upon completion, they update to `Success` or `Error` with the error message.

5. **Query** – The `blade status` command uses `QueryExperimentModelByUid` or `QueryExperimentModels` to retrieve current state from the SQLite database track experiment state storage.

6. **Destruction** – After `blade destroy` completes successfully, the status updates to `Destroyed` and `DeleteExperimentModelByUid` may remove the row entirely.

This design ensures that the SQLite database track experiment state remains consistent even if the ChaosBlade process restarts, providing durable persistence for chaos engineering operations.

## Summary

- ChaosBlade stores all experiment data in a local SQLite file (`chaosblade.dat`) managed through the `data.Source` singleton.
- The `experiment` table schema includes columns for UID, command configuration, status, error messages, and RFC-3339 Nano timestamps.
- State transitions (Created → Running → Success/Error/Destroyed) occur through `UpdateExperimentModelByUid` in [`data/experiment.go`](https://github.com/chaosblade-io/chaosblade/blob/main/data/experiment.go).
- The SQLite database track experiment state without external dependencies, enabling both CLI and server modes to share persistent storage.
- All CRUD operations are thread-safe through the singleton pattern, ensuring consistency across concurrent chaos experiments.

## Frequently Asked Questions

### Where is the ChaosBlade SQLite database file located?

By default, ChaosBlade creates the `chaosblade.dat` file in the current working directory. You can override this location by setting the `CHAOSBLADE_DATAFILE_PATH` environment variable before starting the ChaosBlade CLI or server process. The path resolution logic is implemented in the `GetDataFilePath()` function within [`data/source.go`](https://github.com/chaosblade-io/chaosblade/blob/main/data/source.go).

### What are the possible experiment statuses in the SQLite database?

The `status` column in the `experiment` table supports five lifecycle values: `Created` (initial state), `Running` (active execution), `Success` (completed without errors), `Error` (execution failed with error message stored), and `Destroyed` (cleaned up). These states are managed through the `UpdateExperimentModelByUid` function in [`data/experiment.go`](https://github.com/chaosblade-io/chaosblade/blob/main/data/experiment.go).

### How does ChaosBlade ensure thread-safe access to the SQLite database?

ChaosBlade implements a singleton pattern through the `GetSource()` function in [`data/source.go`](https://github.com/chaosblade-io/chaosblade/blob/main/data/source.go), which returns a single `data.Source` instance shared across the application. This singleton manages the `sql.DB` connection pool returned by `sql.Open("sqlite", ...)`, ensuring that all concurrent experiment operations access the SQLite database through a single coordinated entry point.

### Can I query the ChaosBlade experiment state directly via SQL?

Yes, since ChaosBlade uses a standard SQLite file, you can query the `experiment` table directly using the `sqlite3` command-line tool or any SQLite client. The table schema includes `uid`, `command`, `sub_command`, `flag`, `status`, `error`, `create_time`, and `update_time` columns. However, direct modifications are not recommended as they may interfere with ChaosBlade's internal state management logic.