ELIPS supports atomic, multi-vault transaction write batching via elips::Transaction and elips::TransactionVault.
Overview
Transactions guarantee Atomicity, Consistency, Isolation, and Durability (ACID) for write operations across multiple vaults. Either all vector insertions and erasures are committed to disk and memory atomically, or none are applied.
Transaction Class
cpp
class Transaction {
public:
explicit Transaction(ElipsInstance& db);
~Transaction(); // Automatically rolls back if commit() was not called
TransactionVault vault(const std::string& name);
void commit();
void rollback() noexcept;
};TransactionVault Class
cpp
class TransactionVault {
public:
RecordID place(
const Vector& vector,
Payload payload = {},
std::optional<RecordID> id = std::nullopt
);
void erase(const RecordID& id);
};Two-Phase Write Protocol
When txn.commit() is invoked:
- Validation Phase: The transaction checks vault write permissions, validates vector dimensions, and prepares undo records.
- WAL Logging: All batched operations are written to the Write-Ahead Log (WAL) with a transaction sequence marker.
- Memory Application: Vectors are inserted into HNSW indices and payload stores across target vaults.
- Rollback Recovery: If any stage fails (e.g. disk write failure), all in-memory changes are rolled back using internal undo buffers.
RAII & Exception Safety
Transaction follows RAII. If a Transaction goes out of scope before commit() completes successfully, its destructor automatically triggers rollback().
Complete Code Example
cpp
#include <elips/elips.hpp>
#include <iostream>
void atomic_transfer(elips::ElipsInstance& db) {
// Begin transaction handle
auto txn = db.begin_transaction();
try {
// Enqueue operations across multiple vaults
auto users = txn.vault("users");
auto logs = txn.vault("logs");
elips::Vector user_vec = {0.1f, 0.2f, 0.3f, 0.4f};
elips::Vector log_vec = {0.9f, 0.8f, 0.7f, 0.6f};
users.place(user_vec, {{"user_id", std::string("u_100")}});
logs.place(log_vec, {{"action", std::string("user_created")}});
// Commit atomically
txn.commit();
std::cout << "Transaction committed successfully!" << std::endl;
} catch (const elips::ElipsException& ex) {
std::cerr << "Transaction failed: " << ex.what() << std::endl;
txn.rollback();
}
}