Link Search Menu Expand Document
Start for Free

Transactions

This page discusses transactions in Stardog.

Page Contents
  1. ACID Transactions
    1. Atomicity
    2. Consistency
    3. Isolation
    4. Durability
  2. Begin Transaction
  3. Commit Transaction
  4. Rollback Transaction
  5. List All Open Transactions on a Database

ACID Transactions

This section discusses Stardog’s transactional semantics and guarantees. Generically speaking, Stardog supports ACID transactions. A good general purpose discussion of these issues in context of J2EE is this beginner’s guide.

Atomicity

Databases may guarantee atomicity – groups of database actions (i.e. mutations) are irreducible and indivisible: either all the changes happen or none of them happens. Stardog’s transacted writes are atomic.

Stardog does not support nested transactions.

Consistency

Data stored should be valid according to the data model (in this case, RDF) and to the guarantees offered by the database, as well as to any application-specific integrity constraints that may exist. Stardog’s transactions are guaranteed not to violate integrity constraints during execution. A transaction that would leave a database in an inconsistent or invalid state is aborted.

See the Data Quality Constraints section for a more detailed consideration of Stardog’s integrity constraint mechanism.

Isolation

A Stardog connection will run in SNAPSHOTisolation level if it has not started an explicit transaction and will run in SNAPSHOT or SERIALIZABLE isolation level depending on the value of the database configuration option transaction.isolation. In any of these modes, uncommitted changes will only be visible to the connection that made the changes: no other connection can see those values before they are committed. Thus, “dirty reads” can never occur. Additionally, a transaction will only see changes which were committed before the transaction began, so there are no “non-repeatable reads”.

SNAPSHOT isolation does suffer from the write skew anomaly, which poses a problem when operating under external logical constraints. We illustrate this with the following example, where the database initially has two triples :a :val 100 and :b :val 50, and the application imposes the constraint that the total can never be less than 0.

Example of write-skew anomaly:

Time Connection 1 Connection 2 Connection 3
0 *BEGIN TX* *BEGIN TX*  
1 SELECT ?val {:a :val ?val} <= 100 SELECT ?val {:b :val ?val} <= 50  
2 INSERT {:a :val 0} INSERT {:b :val 0}  
3 *COMMIT* *COMMIT*  
4     *BEGIN TX*
5     SELECT ?val {?a :val ?val} <= 0
6     SELECT ?val {?b :val ?val} <= 0

At the end of this scenario, Connection 1 believes the state of the database to be :a :val 0 and :b :val 50, so the constraint is not violated. Similarly, Connection 2 believes the state of the database to be :a :val 100 and :b :val 0, which also does not violate the constraint. However, Connection 3 sees :a :val 0 and :b :val 0 which violates the logical constraint.

No locks are taken, or any conflict resolution performed, for concurrent transactions in SNAPSHOT isolation level. If there are conflicting changes, the transaction with the highest commit timestamp (functionally, the transaction which committed “last”) will be the result held in the database. This may yield unexpected results since every transaction reads from a snapshot that was created at the time its transaction started.

Consider the following query being executed by two concurrent threads in READ COMMITTED SNAPSHOT isolation level against a database having the triple :counter :val 1 initially:

INSERT { 
    :counter :val ?newValue 
}
DELETE { 
    :counter :val ?oldValue 
}
WHERE  { 
    :counter :val ?oldValue
     BIND (?oldValue+1 AS ?newValue) 
}

Since each transaction will read the current value from its snapshot, it is possible that both transactions will read the value 1 and insert the value 2 even though we expect the final value to be 3.

Isolation level SERIALIZABLE can be used to avoid these situations. In SERIALIZABLE mode an exclusive lock needs to be acquired before a transaction begins. This ensures concurrent updates cannot interfere with each other, but as a result update throughput will decrease since only one transaction can run at a time.

Durability

By default Stardog’s transacted writes are durable and no other actions are required.

Begin Transaction

To begin a transaction:

  • Use the tx begin command:

    $ stardog tx begin myDatabase
    65e93faa-b26a-43ab-b39d-c5392eb93859
    

    A transaction id (e.g. 65e93faa-b26a-43ab-b39d-c5392eb93859) will be returned. This id can be provided to many CLI commands to indicate that the operation should take place within the transaction. For example, see the --tx option for query execute.

  • curl -u username:password -X POST http://localhost:5820/myDatabase/transaction/begin
    

    Output :

    d2c91a85-5b33-4993-8b67-7d95db815377
    

    A transaction id is returned which can then be provided to multiple different operations. For example, see the API to generate a SHACL a report within a transaction.

  • try (Connection aConn = ConnectionConfiguration
            .to("myDatabase") // the name of the db to connect to
            .server("http://localhost:5820") //server url
            .credentials("admin", "admin") // credentials to use while connecting
            .connect()) {
    
        // begin transaction
        aConn.begin();
    
        // perform other operations and eventually commit/rollback the current transaction
    }
    

    It is required to begin a new transaction before making any updates through the connection. See com.complexible.stardog.api.Connection#begin for more information.

Commit Transaction

To commit a transaction:

  • Provide the transaction id to the tx commit command to be committed:

    stardog tx commit myDatabase <tx-id>
    
  • Provide the transaction id for the transaction to be committed.

    curl -u username:password -X POST http://localhost:5820/myDatabase/transaction/commit/{txid}
    

    See the HTTP API for more information.

  • try (Connection aConn = ConnectionConfiguration
            .to("myDatabase") // the name of the db to connect to
            .server("http://localhost:5820") //server url
            .credentials("admin", "admin") // credentials to use while connecting
            .connect()) {
    
            // begin transaction
            aConn.begin();
    
            //create some data and add to a named graph
            Collection<Statement> aGraph = Collections.singleton(
                Values.statement(Values.iri("urn:subj"),
                                Values.iri("urn:pred"),
                                Values.iri("urn:obj")));
    
            Resource aContext = Values.iri("urn:graph");
            aConn.add().graph(aGraph, aContext);
    
            //commit the  current transaction
            aConn.commit()
    }
    

    See com.complexible.stardog.api.Connection#commit for more information.

Rollback Transaction

To rollback a transaction:

  • Provide the transaction id to the tx rollback command to be committed:

    stardog tx rollback myDatabase <tx-id>
    
  • Provide the transaction id for the transaction to be rolled back.

    curl -u username:password -X POST http://localhost:5820/myDatabase/transaction/rollback/{txid}
    

    See the HTTP API for more information.

  • try (Connection aConn = ConnectionConfiguration
            .to("myDatabase") // the name of the db to connect to
            .server("http://localhost:5820") //server url
            .credentials("admin", "admin") // credentials to use while connecting
            .connect()) {
    
            // begin transaction
            aConn.begin();
    
            //create some data and add to a named graph
            Collection<Statement> aGraph = Collections.singleton(
                Values.statement(Values.iri("urn:subj"),
                                Values.iri("urn:pred"),
                                Values.iri("urn:obj")));
    
            Resource aContext = Values.iri("urn:graph");
            aConn.add().graph(aGraph, aContext);
    
            //rollback the  current transaction
            aConn.rollback()
    }
    

    See com.complexible.stardog.api.Connection#rollback for more information.

List All Open Transactions on a Database

Only superusers can see all open transactions on a database. If a non-superuser attempts to list all open transactions, they will only see the ones they have opened. To list all open transactions on a database:

  • stardog tx list myDatabase
    

    Sample Output:

    +--------------------------------------+-------+-------------------------------+
    |                Tx ID                 | User  |          Start Time           |
    +--------------------------------------+-------+-------------------------------+
    | 4923b094-e1c4-4804-8e3a-d0e8e36134c8 | admin | 2021-07-21T16:03:24.894-04:00 |
    +--------------------------------------+-------+-------------------------------+
    
    1 transaction(s)
    
  • curl -u username:password -X GET http://localhost:5820/myDatabase/transaction
    

    See the HTTP API for more information.

  • try (Connection aConn = ConnectionConfiguration
            .to("myDatabase") // the name of the db to connect to
            .server("http://localhost:5820") //server url
            .credentials("admin", "admin") // credentials to use while connecting
            .connect()) {
    
            for (TxInfo tx : aConn.transactions()){
                System.out.println("TX ID: " + tx.getID());
                System.out.println("USER: " + tx.getUser());
                System.out.println("START TIME: " + tx.getStartTime());
            }
    
    }
    

    See com.complexible.stardog.api.Connection#transactions for more information.