
Listening to the pulse of transaction processing
Data writes in evitaDB are specific, as it treats data on disk as immutable. All changes are appended incrementally to the end of files. This approach has its advantages and disadvantages. One drawback is the need for data compaction, as files gradually fill with outdated records. Compaction happens synchronously during transaction processing once certain conditions indicating an excessive amount of outdated data are met. This can impact transaction commit latency. This article discusses how clients can respond to this scenario.

Let's start with the simplest form of transactional data writing in evitaDB:
The second example, equivalent to the first, explicitly states that the client wishes to wait synchronously after the session/transaction ends until changes propagate to the shared state of the database, becoming visible to all clients. An asynchronous version of this example would look like this:
Transaction processing phases
- WAIT_FOR_CONFLICT_RESOLUTION: Completes when the server confirms the transaction doesn't conflict with parallel transactions that completed earlier.
- WAIT_FOR_WAL_PERSISTENCE: Completes when the server safely stores transaction changes on disk and completes an fSync. After this phase, the client can rely on the fact that data will eventually reach the shared state.
- WAIT_FOR_CHANGES_VISIBLE: Completes when all transaction changes are propagated to the database's shared state and become visible to other clients.
It will make more sense when the newly planned stages are added to transaction processing:
- WAIT_FOR_CHANGES_VISIBLE_TO_MAJORITY: This completes when the server confirms that the changes are visible on the majority of replicas. At this point, you can be sure that the changes are safe, even if the master node crashes.
- WAIT_FOR_CHANGES_VISIBLE_TO_ALL: Completes when the server confirms that the changes are visible on all the replicas. At this point, you can be sure that you will always receive the latest data, regardless of which replica you land on.
Issues with timeout in the gRPC protocol
Transaction/session termination operations are implemented as unary calls in gRPC—clients send a request and wait for a response. If the server doesn’t respond within the timeout, the client receives an error. If a client awaits propagation to the shared state with a strict timeout, the client might not receive a response in time, resulting in uncertainty about whether the transaction was successfully processed. The transaction might have been successfully accepted, confirmed, and logged to WAL, yet not propagated into the shared state. The client may decide to retry the transaction unnecessarily, as changes might have already been applied.
For complete control over transaction processing, there's a third variant:
The above example outputs these messages to the console:
Conclusion
If the returned catalog version is lesser, you know changes aren't visible yet and can decide whether to wait or continue working with the current data.
In many scenarios, such transparency is beneficial — but detailed knowledge of server-side processes isn't necessary for everyday database usage. Most clients are likely satisfied with synchronous write operations without considering detailed transaction processing. However, it's good to know you can dive deeper and maintain full control over what's happening on the server.