
Bulk vs. incremental indexing
evitaDB is designed as a fast, transactional, read-optimized database that offloads work from the primary data store, which is usually some kind of relational database. It is therefore expected to operate in two distinct phases: initial indexing of a large dataset and then maintaining the index throughout its lifetime. These two phases have different requirements and, as such, receive special treatment.
Bulk indexing (WARM-UP phase)
Bulk indexing is used for rapid indexing of large volumes of source data from an external data store. At this initial stage of the catalog's lifecycle, we don't require transaction support or concurrency. The only goal is to index as much data as possible in the shortest time possible. This phase has the following characteristics:
- Only a single client (single session) can be open at a time.
- There are no transactions - a group of writes cannot be committed or discarded as a unit. A single entity write is still atomic on its own, so an error part-way through one leaves nothing behind (see Atomicity of individual writes).
- All changes to indexes are kept in memory and written when the session closes; in case of a database crash, everything written since the last session close is lost.
- A failure that cannot be reverted returns the catalog to the last state it published and makes it inactive (see Failures that cannot be reverted).
Incremental indexing (ALIVE phase)
Atomicity of individual writes
If applying an entity mutation fails part-way through — for example because it violates a unique constraint or another consistency rule after some of its index entries have already been written — the engine reverts exactly that entity's partial changes. The index entries already written for it are removed, any unique value it reserved becomes available again, and its stored body goes back to what it was before the call. Nothing half-applied is left behind, such as an orphaned facet or a phantom price.
The failing call throws an exception and the session stays usable. You may catch it, skip or retry the offending entity, and continue writing the rest of your data. Neither compensating on the client side nor rebuilding the catalog is needed because of a single rejected entity.
In the ALIVE phase the enclosing transaction is untouched by the failure: every entity written before the failing one remains valid, and you may commit afterwards — the commit publishes exactly the entities that succeeded. Rolling back still discards everything, as usual.
One thing is deliberately not rewound: the primary key drawn for a failed entity is not returned to the pool. Primary key sequences guarantee uniqueness, not contiguity, so a reverted write leaves a harmless gap in the numbering.
Failures that cannot be reverted
Three failures behave like this:
- A schema change refused by validation. The catalog schema is validated as a whole when a session closes, because a schema is routinely built across several steps whose intermediate states are allowed not to validate — a reflected reference may be declared before the reference it reflects, for instance. By the time the refusal is known, the change has already been applied to every entity collection it touches.
- A failure while writing the collected changes at session close.
- A failed revert of a single entity write, where the revert itself throws.
Moving a catalog to the inactive state is itself an engine-level operation, and one such operation is refused while another is in flight for the same catalog. Where the engine cannot complete the move, the catalog is left refusing every write and every publication, and restarting the engine loads the same published state that activating it would have. The server log says which of the two happened.
Why the recovery is this coarse
The bulk indexing phase earns its speed by leaving out the machinery a narrower recovery would need.
There is no transaction to roll back. A schema change is applied to each entity collection separately, together with the structural work it implies — root nodes for an entity that becomes hierarchical, reference indexes, capability registries — and only then is the catalog validated as a whole. Sending a corrective schema change afterwards repeats that work on top of what already ran rather than reversing it, and nothing guarantees the result matches what a clean path would have produced. Reloading the last published state is the only recovery that is consistent by construction.
It also costs less than it may appear. Nothing written since the last session close was durable in the first place, so what the reload discards is exactly the work you had not yet checkpointed. How large that is, is the trade-off described at the top of this chapter: frequent session closes make each failure cheap and the import slower, while a single long session makes the import as fast as possible and each failure expensive.
