BUG#: 5829
authorkumpf <kumpf>
Wed, 8 Nov 2006 20:42:56 +0000 (20:42 +0000)
committerkumpf <kumpf>
Wed, 8 Nov 2006 20:42:56 +0000 (20:42 +0000)
TITLE: Repository rollback only performed on write operations
DESCRIPTION: Rollback incomplete instance transactions when the CIMRepository is constructed and when an exception is thrown during the transaction processing.

src/Pegasus/Repository/CIMRepository.cpp
src/Pegasus/Repository/CIMRepository.h

index baafae468ff5d3371e357f45911eba9152ff37fd..b2c73620c25c2af26a63e822d500ae9ea6387068 100644 (file)
@@ -540,6 +540,217 @@ void _SaveObject(const String& path, Buffer& objectXml,
     PEG_METHOD_EXIT();
 }
 
+////////////////////////////////////////////////////////////////////////////////
+//
+// _beginInstanceTransaction()
+//
+//      Creates rollback files to allow an incomplete transaction to be voided.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+void _beginInstanceTransaction(
+    const String& indexFilePath,
+    const String& dataFilePath)
+{
+    PEG_METHOD_ENTER(TRC_REPOSITORY, "_beginInstanceTransaction");
+
+    //
+    // Begin the transaction (an incomplete transaction will cause
+    // a rollback the next time an instance-oriented routine is invoked).
+    //
+
+    if (!InstanceIndexFile::beginTransaction(indexFilePath))
+    {
+        PEG_METHOD_EXIT();
+        throw PEGASUS_CIM_EXCEPTION_L(CIM_ERR_FAILED,
+            MessageLoaderParms("Repository.CIMRepository.BEGIN_FAILED",
+                "begin failed"));
+    }
+
+    if (!InstanceDataFile::beginTransaction(dataFilePath))
+    {
+        PEG_METHOD_EXIT();
+        throw PEGASUS_CIM_EXCEPTION_L(CIM_ERR_FAILED,
+            MessageLoaderParms("Repository.CIMRepository.BEGIN_FAILED",
+                "begin failed"));
+    }
+
+    PEG_METHOD_EXIT();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// _commitInstanceTransaction()
+//
+//      Removes the rollback files to complete the transaction.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+void _commitInstanceTransaction(
+    const String& indexFilePath,
+    const String& dataFilePath)
+{
+    PEG_METHOD_ENTER(TRC_REPOSITORY, "_commitInstanceTransaction");
+
+    //
+    // Commit the transaction by removing the rollback files.
+    //
+
+    if (!InstanceIndexFile::commitTransaction(indexFilePath))
+    {
+        PEG_METHOD_EXIT();
+        throw PEGASUS_CIM_EXCEPTION_L(CIM_ERR_FAILED,
+            MessageLoaderParms("Repository.CIMRepository.COMMIT_FAILED",
+                "commit failed"));
+    }
+
+    if (!InstanceDataFile::commitTransaction(dataFilePath))
+    {
+        PEG_METHOD_EXIT();
+        throw PEGASUS_CIM_EXCEPTION_L(CIM_ERR_FAILED,
+            MessageLoaderParms("Repository.CIMRepository.COMMIT_FAILED",
+                "commit failed"));
+    }
+
+    PEG_METHOD_EXIT();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// _rollbackInstanceTransaction()
+//
+//      Restores instance index and data files to void an incomplete operation.
+//      If there are no rollback files, this method has no effect.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+void _rollbackInstanceTransaction(
+    const String& indexFilePath,
+    const String& dataFilePath)
+{
+    PEG_METHOD_ENTER(TRC_REPOSITORY, "_rollbackInstanceTransaction");
+
+    if (!InstanceIndexFile::rollbackTransaction(indexFilePath))
+    {
+        PEG_METHOD_EXIT();
+        throw PEGASUS_CIM_EXCEPTION_L(CIM_ERR_FAILED,
+            MessageLoaderParms("Repository.CIMRepository.ROLLBACK_FAILED",
+                "rollback failed"));
+    }
+
+    if (!InstanceDataFile::rollbackTransaction(dataFilePath))
+    {
+        PEG_METHOD_EXIT();
+        throw PEGASUS_CIM_EXCEPTION_L(CIM_ERR_FAILED,
+            MessageLoaderParms(
+                "Repository.CIMRepository.ROLLBACK_FAILED",
+                "rollback failed"));
+    }
+
+    PEG_METHOD_EXIT();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// InstanceTransactionHandler
+//
+//      This class is used to manage a repository instance transaction.  The
+//      transaction is started when the class is instantiated, committed when
+//      the complete() method is called, and rolled back if the destructor is
+//      called without a prior call to complete().
+//
+//      The appropriate repository write locks must be owned while an
+//      InstanceTransactionHandler instance exists.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+class InstanceTransactionHandler
+{
+public:
+    InstanceTransactionHandler(
+        const String& indexFilePath,
+        const String& dataFilePath)
+    : _indexFilePath(indexFilePath),
+      _dataFilePath(dataFilePath),
+      _isComplete(false)
+    {
+        _rollbackInstanceTransaction(_indexFilePath, _dataFilePath);
+        _beginInstanceTransaction(_indexFilePath, _dataFilePath);
+    }
+
+    ~InstanceTransactionHandler()
+    {
+        if (!_isComplete)
+        {
+            _rollbackInstanceTransaction(_indexFilePath, _dataFilePath);
+        }
+    }
+
+    void complete()
+    {
+        _commitInstanceTransaction(_indexFilePath, _dataFilePath);
+        _isComplete = true;
+    }
+
+private:
+    String _indexFilePath;
+    String _dataFilePath;
+    Boolean _isComplete;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// CIMRepository::_rollbackIncompleteTransactions()
+//
+//      Searches for incomplete instance transactions for all classes in all
+//      namespaces.  Restores instance index and data files to void an
+//      incomplete operation.  If no incomplete instance transactions are
+//      outstanding, this method has no effect.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+void CIMRepository::_rollbackIncompleteTransactions()
+{
+    PEG_METHOD_ENTER(TRC_REPOSITORY,
+        "CIMRepository::_rollbackIncompleteTransactions");
+
+    WriteLock lock(_lock);
+
+    Array<CIMNamespaceName> namespaceNames;
+    _nameSpaceManager.getNameSpaceNames(namespaceNames);
+
+    for (Uint32 i = 0; i < namespaceNames.size(); i++)
+    {
+        Array<CIMName> classNames;
+        _nameSpaceManager.getSubClassNames(
+            namespaceNames[i], CIMName(), true, classNames);
+
+        for (Uint32 j = 0; j < classNames.size(); j++)
+        {
+            //
+            // Get paths of index and data files:
+            //
+
+            String indexFilePath = _getInstanceIndexFilePath(
+                namespaceNames[i], classNames[j]);
+
+            String dataFilePath = _getInstanceDataFilePath(
+                namespaceNames[i], classNames[j]);
+
+            //
+            // Attempt rollback (if there are no rollback files, this will
+            // have no effect). This code is here to rollback uncommitted
+            // changes left over from last time an instance-oriented function
+            // was called.
+            //
+
+            _rollbackInstanceTransaction(indexFilePath, dataFilePath);
+        }
+    }
+
+    PEG_METHOD_EXIT();
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 //
 // CIMRepository
@@ -598,6 +809,8 @@ CIMRepository::CIMRepository(const String& repositoryRoot, const CIMRepository_M
     _isDefaultInstanceProvider = (ConfigManager::getInstance()->getCurrentValue(
         "repositoryIsDefaultInstanceProvider") == "true");
 
+    _rollbackIncompleteTransactions();
+
     PEG_METHOD_EXIT();
 }
 
@@ -649,6 +862,8 @@ CIMRepository::CIMRepository(const String& repositoryRoot)
     _isDefaultInstanceProvider = (ConfigManager::getInstance()->getCurrentValue(
         "repositoryIsDefaultInstanceProvider") == "true");
 
+    _rollbackIncompleteTransactions();
+
     PEG_METHOD_EXIT();
 }
 
@@ -1101,32 +1316,11 @@ void CIMRepository::deleteInstance(
         nameSpace, instanceName.getClassName());
 
     //
-    // Attempt rollback (if there are no rollback files, this will have no
-    // effect). This code is here to rollback uncommitted changes left over
-    // from last time an instance-oriented function was called.
+    // Perform the operation in a transaction scope to enable rollback on
+    // failure.
     //
 
-    if (!InstanceIndexFile::rollbackTransaction(indexFilePath))
-    {
-        PEG_METHOD_EXIT();
-        //l10n
-        //throw PEGASUS_CIM_EXCEPTION(CIM_ERR_FAILED, "rollback failed");
-        throw PEGASUS_CIM_EXCEPTION_L(CIM_ERR_FAILED,
-            MessageLoaderParms("Repository.CIMRepository.ROLLBACK_FAILED",
-                "rollback failed"));
-        //l10n end
-    }
-
-    if (!InstanceDataFile::rollbackTransaction(dataFilePath))
-    {
-        PEG_METHOD_EXIT();
-        //l10n
-        //throw PEGASUS_CIM_EXCEPTION(CIM_ERR_FAILED, "rollback failed");
-        throw PEGASUS_CIM_EXCEPTION_L(CIM_ERR_FAILED,
-            MessageLoaderParms("Repository.CIMRepository.ROLLBACK_FAILED",
-                "rollback failed"));
-        //l10n end
-    }
+    InstanceTransactionHandler transaction(indexFilePath, dataFilePath);
 
     //
     // Lookup instance from the index file (raise error if not found).
@@ -1142,33 +1336,6 @@ void CIMRepository::deleteInstance(
         throw PEGASUS_CIM_EXCEPTION(CIM_ERR_NOT_FOUND, instanceName.toString());
     }
 
-    //
-    // Begin the transaction (any return prior to commit will cause
-    // a rollback next time an instance-oriented routine is invoked).
-    //
-
-    if (!InstanceIndexFile::beginTransaction(indexFilePath))
-    {
-        PEG_METHOD_EXIT();
-        //l10n
-        //throw PEGASUS_CIM_EXCEPTION(CIM_ERR_FAILED, "begin failed");
-        throw PEGASUS_CIM_EXCEPTION_L(CIM_ERR_FAILED,
-            MessageLoaderParms("Repository.CIMRepository.BEGIN_FAILED",
-                "begin failed"));
-        //l10n end
-    }
-
-    if (!InstanceDataFile::beginTransaction(dataFilePath))
-    {
-        PEG_METHOD_EXIT();
-        //l10n
-        //throw PEGASUS_CIM_EXCEPTION(CIM_ERR_FAILED, "begin failed");
-        throw PEGASUS_CIM_EXCEPTION_L(CIM_ERR_FAILED,
-            MessageLoaderParms("Repository.CIMRepository.BEGIN_FAILED",
-                "begin failed"));
-        //l10n end
-    }
-
     //
     // Remove entry from index file.
     //
@@ -1190,31 +1357,7 @@ void CIMRepository::deleteInstance(
         //l10n end
     }
 
-    //
-    // Commit the transaction:
-    //
-
-    if (!InstanceIndexFile::commitTransaction(indexFilePath))
-    {
-        PEG_METHOD_EXIT();
-        //l10n
-        //throw PEGASUS_CIM_EXCEPTION(CIM_ERR_FAILED, "commit failed");
-        throw PEGASUS_CIM_EXCEPTION_L(CIM_ERR_FAILED,
-            MessageLoaderParms("Repository.CIMRepository.COMMIT_FAILED",
-                "commit failed"));
-        //l10n end
-    }
-
-    if (!InstanceDataFile::commitTransaction(dataFilePath))
-    {
-        PEG_METHOD_EXIT();
-        //l10n
-        //throw PEGASUS_CIM_EXCEPTION(CIM_ERR_FAILED, "commit failed");
-        throw PEGASUS_CIM_EXCEPTION_L(CIM_ERR_FAILED,
-            MessageLoaderParms("Repository.CIMRepository.COMMIT_FAILED",
-                "commit failed"));
-        //l10n end
-    }
+    transaction.complete();
 
     //
     // Compact the index and data files if the free count max was
@@ -1554,45 +1697,6 @@ CIMObjectPath CIMRepository::_createInstance(
 
     String errMessage;
 
-    //
-    // Get paths to data and index files:
-    //
-
-    String dataFilePath = _getInstanceDataFilePath(
-        nameSpace, newInstance.getClassName());
-
-    String indexFilePath = _getInstanceIndexFilePath(
-        nameSpace, newInstance.getClassName());
-
-    //
-    // Attempt rollback (if there are no rollback files, this will have no
-    // effect). This code is here to rollback uncommitted changes left over
-    // from last time an instance-oriented function was called.
-    //
-
-    if (!InstanceIndexFile::rollbackTransaction(indexFilePath))
-    {
-        PEG_METHOD_EXIT();
-        //l10n
-        //throw PEGASUS_CIM_EXCEPTION(CIM_ERR_FAILED, "rollback failed");
-        throw PEGASUS_CIM_EXCEPTION_L(CIM_ERR_FAILED,
-            MessageLoaderParms("Repository.CIMRepository.ROLLBACK_FAILED",
-                "rollback failed"));
-        //l10n end
-    }
-
-    if (!InstanceDataFile::rollbackTransaction(dataFilePath))
-    {
-        PEG_METHOD_EXIT();
-        //l10n
-        //throw PEGASUS_CIM_EXCEPTION(CIM_ERR_FAILED, "rollback failed");
-        throw PEGASUS_CIM_EXCEPTION_L(CIM_ERR_FAILED,
-            MessageLoaderParms(
-                "Repository.CIMRepository.ROLLBACK_FAILED",
-                "rollback failed"));
-        //l10n end
-    }
-
     //
     // Resolve the instance. Looks up class and fills out properties but
     // not the qualifiers.
@@ -1646,31 +1750,21 @@ CIMObjectPath CIMRepository::_createInstance(
         _createAssocInstEntries(nameSpace, cimClass, cimInstance, instanceName);
 
     //
-    // Begin the transaction (any return prior to commit will cause
-    // a rollback next time an instance-oriented routine is invoked).
+    // Get paths to data and index files:
     //
 
-    if (!InstanceIndexFile::beginTransaction(indexFilePath))
-    {
-        PEG_METHOD_EXIT();
-        //l10n
-        //throw PEGASUS_CIM_EXCEPTION(CIM_ERR_FAILED, "begin failed");
-        throw PEGASUS_CIM_EXCEPTION_L(CIM_ERR_FAILED,
-            MessageLoaderParms("Repository.CIMRepository.BEGIN_FAILED",
-                "begin failed"));
-        //l10n end
-    }
+    String dataFilePath = _getInstanceDataFilePath(
+        nameSpace, newInstance.getClassName());
 
-    if (!InstanceDataFile::beginTransaction(dataFilePath))
-    {
-        PEG_METHOD_EXIT();
-        //l10n
-        //throw PEGASUS_CIM_EXCEPTION(CIM_ERR_FAILED, "begin failed");
-        throw PEGASUS_CIM_EXCEPTION_L(CIM_ERR_FAILED,
-            MessageLoaderParms("Repository.CIMRepository.BEGIN_FAILED",
-                "begin failed"));
-        //l10n end
-    }
+    String indexFilePath = _getInstanceIndexFilePath(
+        nameSpace, newInstance.getClassName());
+
+    //
+    // Perform the operation in a transaction scope to enable rollback on
+    // failure.
+    //
+
+    InstanceTransactionHandler transaction(indexFilePath, dataFilePath);
 
     //
     // Save instance to file:
@@ -1721,31 +1815,7 @@ CIMObjectPath CIMRepository::_createInstance(
         //l10n end
     }
 
-    //
-    // Commit the changes:
-    //
-
-    if (!InstanceIndexFile::commitTransaction(indexFilePath))
-    {
-        PEG_METHOD_EXIT();
-        //l10n
-        //throw PEGASUS_CIM_EXCEPTION(CIM_ERR_FAILED, "commit failed");
-        throw PEGASUS_CIM_EXCEPTION_L(CIM_ERR_FAILED,
-            MessageLoaderParms("Repository.CIMRepository.COMMIT_FAILED",
-                "commit failed"));
-        //l10n end
-    }
-
-    if (!InstanceDataFile::commitTransaction(dataFilePath))
-    {
-        PEG_METHOD_EXIT();
-        //l10n
-        //throw PEGASUS_CIM_EXCEPTION(CIM_ERR_FAILED, "commit failed");
-        throw PEGASUS_CIM_EXCEPTION_L(CIM_ERR_FAILED,
-            MessageLoaderParms("Repository.CIMRepository.COMMIT_FAILED",
-                "commit failed"));
-        //l10n end
-    }
+    transaction.complete();
 
     Resolver::resolveInstance (cimInstance, _context, nameSpace, cimClass,
         true);
@@ -1871,70 +1941,6 @@ void CIMRepository::modifyInstance(
 
     WriteLock lock(_lock);
 
-    //
-    // Get paths of index and data files:
-    //
-
-    const CIMInstance& instance = modifiedInstance;
-
-    String indexFilePath = _getInstanceIndexFilePath(
-        nameSpace, instance.getClassName());
-
-    String dataFilePath = _getInstanceDataFilePath(
-        nameSpace, instance.getClassName());
-
-    //
-    // First attempt rollback:
-    //
-
-    if (!InstanceIndexFile::rollbackTransaction(indexFilePath))
-    {
-        PEG_METHOD_EXIT();
-        //l10n
-        //throw PEGASUS_CIM_EXCEPTION(CIM_ERR_FAILED, "rollback failed");
-        throw PEGASUS_CIM_EXCEPTION_L(CIM_ERR_FAILED,
-            MessageLoaderParms("Repository.CIMRepository.ROLLBACK_FAILED",
-                "rollback failed"));
-        //l10n end
-    }
-
-    if (!InstanceDataFile::rollbackTransaction(dataFilePath))
-    {
-        PEG_METHOD_EXIT();
-        //l10n
-        //throw PEGASUS_CIM_EXCEPTION(CIM_ERR_FAILED, "rollback failed");
-        throw PEGASUS_CIM_EXCEPTION_L(CIM_ERR_FAILED,
-            MessageLoaderParms("Repository.CIMRepository.ROLLBACK_FAILED",
-                "rollback failed"));
-        //l10n end
-    }
-
-    //
-    // Begin the transaction:
-    //
-
-    if (!InstanceIndexFile::beginTransaction(indexFilePath))
-    {
-        PEG_METHOD_EXIT();
-        //l10n
-        //throw PEGASUS_CIM_EXCEPTION(CIM_ERR_FAILED, "begin failed");
-        throw PEGASUS_CIM_EXCEPTION_L(CIM_ERR_FAILED,
-            MessageLoaderParms("Repository.CIMRepository.BEGIN_FAILED",
-                "begin failed"));
-        //l10n end
-    }
-
-    if (!InstanceDataFile::beginTransaction(dataFilePath))
-    {
-        PEG_METHOD_EXIT();
-        //l10n
-        //throw PEGASUS_CIM_EXCEPTION(CIM_ERR_FAILED, "begin failed");
-        throw PEGASUS_CIM_EXCEPTION_L(CIM_ERR_FAILED,
-            MessageLoaderParms("Repository.CIMRepository.BEGIN_FAILED",
-                "begin failed"));
-        //l10n end
-    }
-
     //
     // Do this:
     //
@@ -2172,6 +2178,20 @@ void CIMRepository::modifyInstance(
         //l10n end
     }
 
+    //
+    // Get paths of index and data files:
+    //
+
+    String indexFilePath = _getInstanceIndexFilePath(
+        nameSpace, modifiedInstance.getClassName());
+
+    String dataFilePath = _getInstanceDataFilePath(
+        nameSpace, modifiedInstance.getClassName());
+
+    //
+    // Look up the specified instance
+    //
+
     Uint32 oldSize;
     Uint32 oldIndex;
     Uint32 newSize;
@@ -2184,6 +2204,13 @@ void CIMRepository::modifyInstance(
         throw PEGASUS_CIM_EXCEPTION(CIM_ERR_NOT_FOUND, instanceName.toString());
     }
 
+    //
+    // Perform the operation in a transaction scope to enable rollback on
+    // failure.
+    //
+
+    InstanceTransactionHandler transaction(indexFilePath, dataFilePath);
+
     //
     // Modify the data file:
     //
@@ -2233,31 +2260,7 @@ void CIMRepository::modifyInstance(
         //l10n end
     }
 
-    //
-    // Commit the transaction:
-    //
-
-    if (!InstanceIndexFile::commitTransaction(indexFilePath))
-    {
-        PEG_METHOD_EXIT();
-        //l10n
-        //throw PEGASUS_CIM_EXCEPTION(CIM_ERR_FAILED, "commit failed");
-        throw PEGASUS_CIM_EXCEPTION_L(CIM_ERR_FAILED,
-            MessageLoaderParms("Repository.CIMRepository.COMMIT_FAILED",
-                "commit failed"));
-        //l10n end
-    }
-
-    if (!InstanceDataFile::commitTransaction(dataFilePath))
-    {
-        PEG_METHOD_EXIT();
-        //l10n
-        //throw PEGASUS_CIM_EXCEPTION(CIM_ERR_FAILED, "commit failed");
-        throw PEGASUS_CIM_EXCEPTION_L(CIM_ERR_FAILED,
-            MessageLoaderParms("Repository.CIMRepository.COMMIT_FAILED",
-                "commit failed"));
-        //l10n end
-    }
+    transaction.complete();
 
     //
     // Compact the index and data files if the free count max was
index 04dcc97f42ace34daa0d98f696330cad2e2b6d7f..efeef339852c9653e87ba9b7c2eff5dc4a008c3e 100644 (file)
@@ -448,6 +448,14 @@ protected:
 
 private:
 
+    /**
+        Searches for incomplete instance transactions for all classes in all
+        namespaces.  Restores instance index and data files to void an
+        incomplete operation.  If no incomplete instance transactions are
+        outstanding, this method has no effect.
+     */
+    void _rollbackIncompleteTransactions();
+
     void _createAssocInstEntries(
         const CIMNamespaceName& nameSpace,
         const CIMConstClass& cimClass,
@@ -577,26 +585,6 @@ private:
         Uint32& newIndex,
         Uint32& newSize);
 
-    /** Renames the temporary instance and instance index files back to the
-        original files.  The temporary files were created for an insert,
-        remove, or modify operation (to avoid data inconsistency between
-        the two files in case of unexpected system termination or failure).
-        This method is called after a successful insert, remove, or modify
-        operation on BOTH the index file and the instance file.  Returns
-        true on success.
-
-        @param   indexFilePath   the file path of the instance index file
-        @param   instancePath    the file path of the instance file
-
-        @return  true      if successful
-                 false     if an error occurs in removing the original files
-                           or renaming the temporary files.
-     */
-    Boolean _renameTempInstanceAndIndexFiles(
-        const String& indexFilePath,
-        const String& instanceFilePath);
-
-
     String _repositoryRoot;
     NameSpaceManager _nameSpaceManager;