Skip to main content

Row-level concurrency

Row-level concurrency in OrioleDB is as close as possible to regular PostgreSQL tables. However, there are still some differencies because tables in OrioleDB are index-organized.

Update of the primary key

Update of primary key is typically rare and unusual situation in index-organized table. Unlike regular update, update of primary key is implemented as a sequence of delete and insert. Concurrent update and delete can't follow this update and may result in error. See the example below. Session 2 gets an error due to concurrent primary key update in session 1.

CREATE TABLE tbl
(
id int4 primary key,
value numeric NOT NULL
) USING orioledb;

INSERT INTO tbl VALUES (1, 0.0);
Session 1
> BEGIN;
> UPDATE tbl SET id = 2 WHERE id = 1;
UPDATE 1
Session 2
> BEGIN;
> UPDATE tbl SET value = value + 1 WHERE id = 1;
(waiting)
> COMMIT;
COMMIT
ERROR: tuple to be locked has its primary key changed due to concurrent update
> ROLLBACK;
ROLLBACK

Following the update chain

If some row was deleted and then new row with same primary key value is immediately inserted, then concurrent update or delete may consider the new row as a new version of the old row. See the example below. Session 1 deletes row and then inserts row with same primary key value. Session 2 were intended to update initial row, but finally updates the newly inserted row.

CREATE TABLE tbl
(
id int4 primary key,
value numeric NOT NULL
) USING orioledb;

INSERT INTO tbl VALUES (1, 0.0);
Session 1
> BEGIN;
> DELETE FROM tbl WHERE id = 1;
DELETE 1
> INSERT INTO tbl VALUES (1, 0.0);
INSERT 0 1
Session 2
> BEGIN;
> UPDATE tbl SET value = value + 1 WHERE id = 1;
(waiting)
> COMMIT;
COMMIT
UPDATE 1
> COMMIT;
COMMIT

Transaction ID Allocation and Heap Relations

OrioleDB optimizes concurrency by utilizing virtual transaction IDs (Virtual XIDs) for operations confined exclusively to OrioleDB tables. This bypasses the overhead of allocating a full PostgreSQL Transaction ID (XID) and the associated Write-Ahead Logging (WAL) required for heap relations.

Modifying any PostgreSQL heap relation within an OrioleDB transaction forces the allocation of a full XID. This alters the concurrency path and negates the Virtual XID optimization.

Sequence Generation Overhead

PostgreSQL sequences are backed by heap relations. Using nextval() modifies the sequence relation, triggering a full XID allocation. To preserve OrioleDB's optimized transaction mechanics:

  • Enable Sequence Caching: Configure sequences with a CACHE directive (e.g., CREATE SEQUENCE my_seq CACHE 100;).
  • Mechanism: Caching restricts heap modification (and full XID allocation) to the single nextval() call that fetches the cache block. Subsequent calls retrieve values from session memory.
  • Workload Impact: This optimization can be critical for workloads with high volumes of small transactions. In large transactions, the performance penalty of a single XID allocation is heavily amortized.