GEODE-8830: Fix exception handling in transactions (#720)
- Exceptions were not correctly handled during transaction operations,
leading to the derived type of the exception to be lost on the
internal source calls.
- Problem was that in the process of capturing an re-throwing the
exceptions, a copy of its more generic type, `Exception` was created
and passed throught, instead of the original exception. This has been
sorted out.
- Also, several integration test were added, in order to ensure that
the correct type of exceptions are thrown when an error occurs while
executing transsactions operations.
diff --git a/cppcache/integration/test/TransactionsTest.cpp b/cppcache/integration/test/TransactionsTest.cpp
index eacaa04..edf81df 100644
--- a/cppcache/integration/test/TransactionsTest.cpp
+++ b/cppcache/integration/test/TransactionsTest.cpp
@@ -32,27 +32,38 @@
using apache::geode::client::CacheableString;
using apache::geode::client::CacheFactory;
using apache::geode::client::CacheTransactionManager;
+using apache::geode::client::CommitConflictException;
+using apache::geode::client::Exception;
+using apache::geode::client::IllegalStateException;
using apache::geode::client::Pool;
using apache::geode::client::Region;
using apache::geode::client::RegionShortcut;
-std::shared_ptr<Cache> createCache() {
- auto cache = CacheFactory().set("log-level", "none").create();
- return std::make_shared<Cache>(std::move(cache));
+const std::string regionName = "region";
+
+Cache createCache() {
+ return CacheFactory()
+ .set("statistic-sampling-enabled", "false")
+ .set("log-level", "none")
+ .create();
}
-std::shared_ptr<Pool> createPool(Cluster& cluster,
- std::shared_ptr<Cache> cache) {
- auto poolFactory = cache->getPoolManager().createFactory();
+std::shared_ptr<Pool> createPool(Cluster& cluster, Cache& cache) {
+ auto poolFactory = cache.getPoolManager().createFactory();
cluster.applyLocators(poolFactory);
poolFactory.setPRSingleHopEnabled(true);
return poolFactory.create("default");
}
-void runClientOperations(std::shared_ptr<Cache> cache,
- std::shared_ptr<Region> region, int minEntryKey,
- int maxEntryKey, int numTx) {
- auto transactionManager = cache->getCacheTransactionManager();
+std::shared_ptr<Region> setupRegion(Cache& cache) {
+ return cache.createRegionFactory(RegionShortcut::PROXY)
+ .setPoolName("default")
+ .create(regionName);
+}
+
+void runClientOperations(Cache& cache, std::shared_ptr<Region> region,
+ int minEntryKey, int maxEntryKey, int numTx) {
+ auto transactionManager = cache.getCacheTransactionManager();
for (int i = 0; i < numTx; i++) {
auto theKey = (rand() % (maxEntryKey - minEntryKey)) + minEntryKey;
@@ -86,15 +97,13 @@
auto cache = createCache();
auto pool = createPool(cluster, cache);
- auto region = cache->createRegionFactory(RegionShortcut::PROXY)
- .setPoolName("default")
- .create("region");
+ auto region = setupRegion(cache);
std::vector<std::thread> clientThreads;
for (int i = 0; i < NUM_THREADS; i++) {
auto minKey = (i * keyRangeSize);
auto maxKey = minKey + keyRangeSize - 1;
- std::thread th(runClientOperations, cache, region, minKey, maxKey,
+ std::thread th(runClientOperations, std::ref(cache), region, minKey, maxKey,
TX_PER_CLIENT);
clientThreads.push_back(std::move(th));
}
@@ -109,4 +118,86 @@
} // TEST
+TEST(TransactionsTest, IlegalStateExceptionNoTx) {
+ Cluster cluster{LocatorCount{1}, ServerCount{1}};
+ cluster.start();
+
+ // Create regions
+ cluster.getGfsh()
+ .create()
+ .region()
+ .withName(regionName)
+ .withType("PARTITION")
+ .execute();
+
+ auto cache = createCache();
+ auto txm = cache.getCacheTransactionManager();
+ auto pool = createPool(cluster, cache);
+ auto region = setupRegion(cache);
+
+ EXPECT_THROW(txm->prepare(), IllegalStateException);
+ EXPECT_THROW(txm->commit(), IllegalStateException);
+ EXPECT_THROW(txm->rollback(), IllegalStateException);
+} // TEST
+
+TEST(TransactionsTest, ExceptionConflictOnPrepare) {
+ Cluster cluster{LocatorCount{1}, ServerCount{1}};
+ cluster.start();
+
+ // Create regions
+ cluster.getGfsh()
+ .create()
+ .region()
+ .withName(regionName)
+ .withType("PARTITION")
+ .execute();
+
+ auto cache = createCache();
+ auto txm = cache.getCacheTransactionManager();
+ auto pool = createPool(cluster, cache);
+ auto region = setupRegion(cache);
+
+ txm->begin();
+ region->put("key", "A");
+ auto& tx_first_id = txm->suspend();
+ txm->begin();
+ region->put("key", "B");
+ txm->prepare();
+ auto& tx_second_id = txm->suspend();
+ txm->resume(tx_first_id);
+
+ EXPECT_THROW(txm->prepare(), CommitConflictException);
+ txm->resume(tx_second_id);
+ txm->rollback();
+
+} // TEST
+
+TEST(TransactionsTest, ExceptionConflictOnCommit) {
+ Cluster cluster{LocatorCount{1}, ServerCount{1}};
+ cluster.start();
+
+ // Create regions
+ cluster.getGfsh()
+ .create()
+ .region()
+ .withName(regionName)
+ .withType("PARTITION")
+ .execute();
+
+ auto cache = createCache();
+ auto txm = cache.getCacheTransactionManager();
+ auto pool = createPool(cluster, cache);
+ auto region = setupRegion(cache);
+
+ txm->begin();
+ region->put("key", "A");
+ auto& tx_id = txm->suspend();
+ txm->begin();
+ region->put("key", "B");
+ txm->commit();
+ txm->resume(tx_id);
+
+ EXPECT_THROW(txm->commit(), CommitConflictException);
+} // TEST
+
} // namespace
diff --git a/cppcache/src/InternalCacheTransactionManager2PCImpl.cpp b/cppcache/src/InternalCacheTransactionManager2PCImpl.cpp
index 0df0512..2ac0d9c 100644
--- a/cppcache/src/InternalCacheTransactionManager2PCImpl.cpp
+++ b/cppcache/src/InternalCacheTransactionManager2PCImpl.cpp
@@ -101,7 +101,7 @@
}
} catch (const Exception& ex) {
LOGERROR("Unexpected exception during commit in prepare %s", ex.what());
- throw ex;
+ throw;
}
}
@@ -200,7 +200,7 @@
} catch (const Exception& ex) {
LOGERROR("Unexpected exception during completing transaction %s",
ex.what());
- throw ex;
+ throw;
}
}
} // namespace client