Examples and Scenarios

This document covers the following topics:


Scenario 1: Read-only Scenario without Consistency Requirements

In this scenario documents are read from Tamino with the lowest possible consistency requirements. Documents may be read whose content may contain uncommitted changes of other concurrent transactions. This may lead to dirty reads and non-repeatable reads as described above. For an explanation of dirty reads and non-repeatable reads refer to section Concurrency Phenomena in DBMS of this document.

Start of instruction setCursor-free Approach

  • If no cursoring is required, single read requests (direct reads or queries) may be issued in a session-less connection to Tamino without an explicit transaction context. Just choose the default isolation level for session-less context, namely "uncommittedDocument". This avoids the cost of setting up and maintaining sessions, so this is the fastest alternative.

    This choice does not automatically suppress setting locks.

    An example Java application which only reads data efficiently in a session-less context without consistency requirements may for instance look like this example:

    • Open a Tamino connection in auto-commit mode by invoking the newConnection method of the class TConnection on an instance which had been created using TConnectionFactory's method getInstance.

      TConnection connection = null;
      String taminoURI = "http://localhost/tamino/myDB";
      String collection = "encyclopedia";
      // Establish the Tamino connection (by default in auto-commit mode)
      connection = TConnectionFactory.getInstance().newConnection( taminoURI );
      ...
      
    • Create an appropriate accessor object by invoking the newXMLObjectAccessor method of TConnection.

      ...
      // Obtain a TXMLObjectAccessor with a DOM object model
      TXMLObjectAccessor accessor =
       connection.newXMLObjectAccessor(
      		TAccessLocation.newInstance( collection ),
      		TDOMObjectModel.getInstance() );
      ...
      
    • You may additionally set the lock mode associated to this accessor object to the value "unprotected" in order to avoid any locks. In this manner the highest possible performance for read operations can be achieved.

      ...
      // Set the lock mode to unprotected to avoid any locks
      accessor.setLockMode(TLockMode.UNPROTECTED);
      ...
      
    • Perform a read access using the class TQuery and display the result of the query.

      ...
      // execute a query
      TQuery query = TQuery.newInstance("/jazzMusician[./instrument=\"trumpet\"]/name/first");
      TResponse response = accessor.query(query);
      // display result
      TXMLObject xmlObject = response.getFirstXMLObject();
      System.out.println("result: " 
      + ((Element)xmlObject.getElement()).getFirstChild().getNodeValue());
      ...
      

    Or:

    Cursor-based Approach

    Alternatively, if cursoring is required you can implement this scenario also with explicit transactions. A session is therefore required. You need to explicitly specify the lowest possible isolation level "uncommittedDocument" to be used as it is not the default for session context.

    Locks will still be set and cannot always be avoided:

    • X-Query allows using the lockmode "unprotected".

    Note:
    Dirty reads are possible for both XQuery and X-Query. However, for some join operations, XQuery (but not X-Query) might have to read a document more than once. If the document has been changed between the read operations (which is possible since the query is not using locks), the XQuery processing detects an inconsistent state and returns an error 6312.

    For instance, an example Java application which only reads data efficiently in a session based context without consistency requirements could work in the following manner:

    • It opens a Tamino connection similarly to the explanation given in the previous example (Cursor-free Approach).

    • it switches this connection to session mode in order to work with explicit transactions.

      ...
      // Establish a transaction context and therefore create a session
      TLocalTransaction transaction = connection.useLocalTransactionMode();
      ...
      

      and creates a new session implicitly.

      These two steps are accomplished by invoking the useLocalTransactionMode method of the class TConnection.

    • It sets the isolation level of this new session to the value "UNCOMMITTED_DOCUMENT" by invoking the setIsolationDegree method of the class TConnection.

      ...
      // Set isolation level to UNCOMMITTED_DOCUMENT
      connection.setIsolationDegree(TIsolationDegree.UNCOMMITTED_DOCUMENT);
      ...
      
    • It creates an appropriate accessor object by invoking the newXMLObjectAccessor method of TConnection.

    • It performs a read access using TQuery and displays the result.

      ...
      // execute a query
      TQuery query = TQuery.newInstance("/jazzMusician[./instrument=\"trumpet\"]/name/first");
      TResponse response = accessor.query(query,5);
      // display result
      TXMLObjectIterator xmlObjectIterator = response.getXMLObjectIterator();
      while (xmlObjectIterator.hasNext()) {
      	TXMLObject xmlObject = xmlObjectIterator.next();
      	System.out.println("result: " 
      + ((Element)xmlObject.getElement()).getFirstChild().getNodeValue());
      ...
      

    The degree of locking is very low in all versions of this scenario. It is in fact very close to the minimum possible degree of locking at all. Processing with very high performance can therefore be expected.

This scenario is not adequate in the following situations:

  • For data with high or medium consistency requirements.

  • For data with frequent changes.

  • If any changes to the data are required.

Important:
This is the scenario with the lowest degree of consistency of all the scenarios discussed here.

Scenario 2: Read-only Scenario with Consistency Requirements

This scenario uses the isolation level "committedCommand" which protects against dirty reads, but not against non-repeatable reads.

The characteristics of this scenario are:

  • No lost updates.

  • No dirty writes.

  • No dirty reads.

  • Only results of committed transactions can be read.

  • Non-repeatable read may still occur.

  • Shared locks are used for query execution. They are released after command execution has finished.

Tip:
This scenario is recommended for reading access to data with medium consistency requirements that does not change very frequently. The risk of non-repeatable read must not be high if this scenario shall be used.

This scenario is not recommended in the following situations:

  • For data with high consistency requirements.

  • For data with frequent changes.

  • If any changes to the data are required.

Start of instruction setCursor-free Approach

  • In case no cursoring is required, single read requests (direct reads or queries) can be issued in a session-less connection to Tamino without an explicit transaction context. Just choose the correct isolation level for session-less context, namely "committedCommand". No sessions need to be maintained in this case.

    Transaction 1 only uses shared locks which are released after the read command is completely executed.

    For instance, a Java application which only reads already committed data in a session-less context with basic consistency requirements could look like this:

    • First the Java application opens a Tamino connection either in auto-commit mode or in local mode depending on your specific requirements as already described in the previous scenario.

    • It sets the isolation level of the connection object to the value "committedCommand" by invoking the setIsolationDegree method of the class TConnection.

      ...
      // Set the isolation level to COMMITTED_COMMAND
      connection.setIsolationDegree(TIsolationDegree.COMMITTED_COMMAND);
      ...
      
    • It creates an appropriate accessor object by invoking the newXMLObjectAccessor method of TConnection.

    • It performs a read access using the class TQuery.

    • It displays the result of the query.

      ...
      // display result
      TXMLObject xmlObject = response.getFirstXMLObject();
      System.out.println("result: " + ((Element)xmlObject.getElement()).getFirstChild().getNodeValue());
      ...
      

    Transactions belonging to this scenario will only read data which has been already committed by other transactions. In this scenario some locks are set that may lead to transactions having to wait for each other. But locks are only kept per command thus keeping the wait time low. Non-repeatable read situations may still occur but there will definitely be no dirty reads anymore.

Non-repeatable Read Operation

A non-repeatable read operation may happen in the following manner:

The following assumptions are made:

Transaction 1 is the transaction considered in this scenario using the isolation level "committedCommand". Transaction 2 can run on an arbitrary isolation level and use any possible locking mode.

  1. Transaction 1 reads document x

  2. Transaction 2 writes document x

  3. Transaction 2 performs a commit operation.

  4. Transaction 1 reads document x again.

Then transaction 1 performs a non-repeatable read operation.

The second read operation will be performed successfully, but the delivered result will be different from the result obtained in the first step. This is due to the fact that there was a commit operation performed between the two different read attempts, and the chosen isolation level is "committedCommand" which is equivalent to showing all committed results to other concurrent transactions. The locks set by transaction 1 are relinquished after the first read command completed.

Scenario 3: Single Insert/Blind Update Scenario

In this scenario either new documents are inserted or existing documents are updated regardless of the contents of the database. This means that all database access is done exclusively with the _process and no read access or updates via XQuery occur. Complete documents are replaced in the database without previously checking whether the document is already present or what its content is. This scenario represents the maximum of performance for writing access, but it is of course limited in its applicability.

Execute the _process commands in session-less context. Do not specify any isolation level or lock mode explicitly.

It is recommended to apply this scenario if you have to store large amounts of data independently of conditions.

This scenario cannot be applied if any contents of the database must be read prior to writing to the database.

For instance, a Java application which only reads already committed data in a session-less context with basic consistency requirements could look like this:

  • First the Java application opens a Tamino connection in auto-commit mode as already described in the previous scenario.

    TConnection connection = null;
    String taminoURI = "http://localhost/tamino/myDB";
    String collection = "encyclopedia";
    // Establish the Tamino connection  in auto-commit mode
    connection = TConnectionFactory.getInstance().newConnection( taminoURI );
    ...
    
  • It does not set the isolation level or the lock mode explicitly.

  • It creates an appropriate accessor object by invoking the newXMLObjectAccessor method of TConnection.

  • It performs a sequence of _process commands in session-less context.

    ...
    // process data
    ...
    

Scenario 4: Read-for-Update Scenario

In this scenario a document is first read and then changed afterwards. At the time the document is read it is clear (or at least likely) that an update of the document will follow. In order to prevent deadlock scenarios, the lock required for the update is already set at read time.

The occurrence of deadlock scenarios of the following type can be avoided if the lock mode is set to "protected" before reading the document:

T1   T2  
Read doc1 S on doc1    
    Read doc1 S on doc1
Update doc1 Wait for X on doc1    
    Update doc1 Wait for X on doc1
    Deadlock  

The following situation leads to the deadlock as both transactions wait for an X-lock being granted which will never occur. However, if the an X-lock is set already on reading document 1 (this is enforced by setting the lock mode to "protected" before reading the document) the following occurs:

T1   T2  
Read doc1 X on doc1    
    Read doc1 S on doc1 (will not be granted)
Update doc1 As X-lock is already on doc1    
Commit Relinquish lock on doc1    

Thus the deadlock is resolved as transaction 1 can continue without waiting.

This scenario only makes sense if:

  • Deadlock scenarios as described above exist.

  • It is known or at least very likely that the document read will be updated in the same transaction at the time of the read.

If the conditions above are not met, this may lead to a large number of superfluous X locks. In that case it might be better to avoid possible deadlocks in another way.

Scenario 5: Stable Cursor Scenario

In this scenario the same document is read more than once in the same cursor. The content of the document is guaranteed to be identical.

In order to accomplish this, the isolation level must be set to "stableCursor".

Start of instruction setStable Cursor Scenario - Cursor-based Approach

  • Working with cursors requires a session context and therefore explicit transactions. The isolation level "stableCursor" needs to be specified explicitly as it is not the default for session context.

    Caution:
    In this scenario locks are kept for the duration of the cursor, thus keeping these locks for a potentially long period of time.

    • This choice can be considered to achieve a medium performance.

      For instance, an example Java application which only reads data in a session based context without consistency requirements could work in the following manner:

      • it opens a Tamino connection similarly to the explanation given in the previous examples.

      • it switches this connection to session mode in order to work with real transactions.

        ...
        // Establish a transaction context and therefore create a session
        TLocalTransaction transaction = connection.useLocalTransactionMode();
        ...
        

        and creates a new session implicitly.

        These two steps are accomplished by invoking the useLocalTransactionMode method of the class TConnection.

      • it sets the isolation level of this new session to the value "STABLE_CURSOR" by invoking the setIsolationDegree method of the class TConnection.

        ...
        // Set isolation level to STABLE_CURSOR
        connection.setIsolationDegree(TIsolationDegree.STABLE_CURSOR);
        ...
        
      • it creates an appropriate accessor object by invoking the newXMLObjectAccessor method of TConnection.

        ...
        // Obtain a TXMLObjectAccessor with a DOM object model
        TXMLObjectAccessor accessor =
        	connection.newXMLObjectAccessor(
        		TAccessLocation.newInstance( collection ),
        		TDOMObjectModel.getInstance() );
        ...
        
      • it finally accesses the data.

Scenario 6: Deadlock Prevention Scenario

Lock acquisition in a fixed document order can also be used as a means in order to prevent the occurrence of deadlocks.