Chapter 8. Using Transactions

Table of Contents

Initializing the Transactional Subsystem
Transactionally Protecting Container Operations
Transactions Considerations
Transaction Disk I/O
Transaction and Lock Contention
Index Operations and Transactions

Transactions allow you to treat one or more operations on one or more containers as a single unit of work. The BDB XML transactional subsystem is simply a wrapper around Berkeley DB's transactional subsystem. This means that you BDB XML offers the same, full, ACID protection as does Berkeley DB. That is, BDB XML transactions offer you:

Transactionally processing is covered in great detail in the Berkeley DB Programmer's Reference Guide. All of the concepts and topics described there are relevant to transactionally protecting an BDB XML application.

The next few sections describe topics that are specific to transactionally protecting a BDB XML application.

Initializing the Transactional Subsystem

In order to use transactions, you must turn on the transactional subsystem. You do this when you open your XmlManager by setting the appropriate flags for the manager. You must also turn on transactions for your container when you open it, again through the use of the appropriate flags.

Note that if you do not enable transactions when you first create your environment, then you cannot subsequently use transactions. Also, if your environment is not opened to support transactions, then your containers cannot be opened to support transactions. Finally, you cannot transactionally protect your container operations unless your environment and containers are configured to support transactions.

One final point: the default XmlManager constructor does not enable the transactional subsystem for its underlying environment, and there is no way to pass the appropriate flags to that environment using the default constructor. Instead, you must construct your own DbEnv object, passing it the flags required to enable transactions, and then hand that DbEnv object to the XmlManager constructor.

In order to enable transactions, you must enable the memory pool (the cache), the logging subsystem, the locking subsystem, and the transactional subsystem. For example:

#include "DbXml.hpp"
...

using namespace DbXml;
int main(void)
{
    u_int32_t env_flags = DB_CREATE     |  // If the environment does not
                                           // exist, create it.
                          DB_INIT_LOCK  |  // Initialize locking
                          DB_INIT_LOG   |  // Initialize logging
                          DB_INIT_MPOOL |  // Initialize the cache
                          DB_INIT_TXN;     // Initialize transactions

    std::string envHome("/export1/testEnv");
    DbEnv myEnv(0);
    XmlManager *myManager = NULL;
                                                                                                                                  
    try {
        myEnv.open(envHome.c_str(), env_flags, 0);
        myManager = new XmlManager(myEnv, 0); 
    } catch(DbException &e) {
        std::cerr << "Error opening database environment: "
                  << envHome << std::endl;
        std::cerr << e.what() << std::endl;
    } catch(XmlException &e) {
        std::cerr << "Error opening database environment: "
                  << envHome 
                  << " or opening XmlManager." << std::endl;
        std::cerr << e.what() << std::endl;
    } 

    try {
        if (myManager != NULL) {
            delete myManager;
        }
        myEnv.close(0);
    } catch(DbException &e) {
        std::cerr << "Error closing database environment: "
                << envHome << std::endl;
        std::cerr << e.what() << std::endl;
    } catch(XmlException &e) {
        std::cerr << "Error closing database environment: "
                << envHome << std::endl;
        std::cerr << e.what() << std::endl;
    }
} 

Once you have enabled transactions for your environment and your manager, you must enable transactions for the containers that you open. You do this by providing the DBXML_TRANSACTIONAL flag when you create or open the container.

The following code updates the previous example to also open a container. The new code is shown in bold.

#include "DbXml.hpp"
...

using namespace DbXml;
int main(void)
{
    u_int32_t env_flags = DB_CREATE     |  // If the environment does not
                                           // exist, create it.
                          DB_INIT_LOCK  |  // Initialize locking
                          DB_INIT_LOG   |  // Initialize logging
                          DB_INIT_MPOOL |  // Initialize the cache
                          DB_INIT_TXN;     // Initialize transactions

    std::string envHome("/export1/testEnv");
    DbEnv myEnv(0);
    XmlManager *myManager = NULL;
                                                                                                                                  
    try {
        myEnv.open(envHome.c_str(), env_flags, 0);
        myManager = new XmlManager(myEnv); 

        u_int32_t containerFlags = 
              DB_CREATE |        // If the container does not exist, 
                                 // create it.
              DB_TRANSACTIONAL;  // Enable transactions.

        std::string containerName = "myContainer.dbxml";
        XmlContainer myContainer = 
            myManager.openContainer(containerName, containerFlags); 

    } catch(DbException &e) {
        std::cerr << "Error opening database environment: "
                  << envHome << std::endl;
        std::cerr << e.what() << std::endl;
    } catch(XmlException &e) {
        std::cerr << "Error opening database environment: "
                  << envHome 
                  << " or opening XmlManager." << std::endl;
        std::cerr << e.what() << std::endl;
    } 

    try {
        if (myManager != NULL) {
            delete myManager;
        }
        myEnv.close(0);
    } catch(DbException &e) {
        std::cerr << "Error closing database environment: "
                << envHome << std::endl;
        std::cerr << e.what() << std::endl;
    } catch(XmlException &e) {
        std::cerr << "Error closing database environment: "
                << envHome << std::endl;
        std::cerr << e.what() << std::endl;
    }
}