From 176255d64af4d0dfff18651abfa58556ea1d4806 Mon Sep 17 00:00:00 2001 From: Michael Oleske Date: Thu, 3 Jan 2019 16:51:45 -0800 Subject: [PATCH 1/4] GEODE-6210: Add 'transaction' example for cpp - demonstrates commit and rollback of transactions - handles exceptions Co-authored-by: Michael Oleske Co-authored-by: Blake Bender Co-authored-by: Sai Boorlagaddda --- examples/cpp/CMakeLists.txt | 9 +- examples/cpp/CMakeLists.txt.in | 3 +- examples/cpp/transaction/README.md | 66 ++++++++++++++ examples/cpp/transaction/main.cpp | 113 ++++++++++++++++++++++++ examples/cpp/transaction/startserver.sh | 31 +++++++ examples/cpp/transaction/stopserver.sh | 31 +++++++ 6 files changed, 249 insertions(+), 4 deletions(-) create mode 100644 examples/cpp/transaction/README.md create mode 100644 examples/cpp/transaction/main.cpp create mode 100755 examples/cpp/transaction/startserver.sh create mode 100755 examples/cpp/transaction/stopserver.sh diff --git a/examples/cpp/CMakeLists.txt b/examples/cpp/CMakeLists.txt index a6e8b80e98..b87067d0ea 100644 --- a/examples/cpp/CMakeLists.txt +++ b/examples/cpp/CMakeLists.txt @@ -36,6 +36,9 @@ add_example(NAME continuousquery add_example(NAME dataserializable SOURCE main.cpp Order.cpp Order.hpp) +add_example(NAME function-execution + SOURCE main.cpp) + add_example(NAME pdxserializable SOURCE main.cpp Order.cpp Order.hpp) @@ -45,12 +48,12 @@ add_example(NAME pdxserializer add_example(NAME put-get-remove SOURCE main.cpp) -add_example(NAME function-execution - SOURCE main.cpp) - add_example(NAME remotequery SOURCE main.cpp Order.cpp Order.hpp) +add_example(NAME transaction + SOURCE main.cpp) + install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} DESTINATION examples/ PATTERN "./*.in" EXCLUDE diff --git a/examples/cpp/CMakeLists.txt.in b/examples/cpp/CMakeLists.txt.in index a1854c00c0..a9852a18d3 100644 --- a/examples/cpp/CMakeLists.txt.in +++ b/examples/cpp/CMakeLists.txt.in @@ -19,8 +19,9 @@ project(@PRODUCT_DLL_NAME@.Cpp.Examples LANGUAGES NONE) add_subdirectory(continuousquery) add_subdirectory(dataserializable) +add_subdirectory(function-execution) add_subdirectory(pdxserializable) add_subdirectory(pdxserializer) add_subdirectory(put-get-remove) -add_subdirectory(function-execution) add_subdirectory(remotequery) +add_subdirectory(transaction) diff --git a/examples/cpp/transaction/README.md b/examples/cpp/transaction/README.md new file mode 100644 index 0000000000..1979e69ab0 --- /dev/null +++ b/examples/cpp/transaction/README.md @@ -0,0 +1,66 @@ +# Transaction example +This is a very simple example showing how to use Transaction Manger. This example shows +how to begin a transaction, commit a transaction, and rollback a transaction while showing +exception handling. We commit two keys and rollback adding a third key and destroying an +existing key while showing how to handle exceptions. + +## Prerequisites +* An installation of Apache Geode. +* Apache Geode Native, built and installed. +* Apache Geode Native examples, built and installed. +* A `GEODE_HOME` environment variable set to the location of the Apache Geode installation. +* `GEODE_HOME/bin` in the execution path. + +## Running +1. Set the current directory to the `transaction` directory in your example workspace. + + ``` + $ cd workspace/examples/cpp/transaction + ``` + +1. Run the `startserver.sh` script to start the Geode server, create a region, and populate the region with sample data. + + ``` + $ sh ./startserver.sh + /Users/user/geode/bin/gfsh + + (1) Executing - start locator --name=locator + ... + (2) Executing - start server --name=server + ... + (3) Executing - create region --name=exampleRegion --type=PARTITION + + Member | Status + ------ | ---------------------------------------------- + server | Region "/exampleRegion" created on "server" + ``` + +1. Execute `transaction`: + + ``` + $ build/transaction + Created cache + Created region 'exampleRegion' + Began transaction #1 + Committed transaction #1 + Obtained the first entry from the Region + Obtained the second entry from the Region + Began transaction #2 + Rolled back transaction #2 + Obtained the first entry from the Region + Obtained the second entry from the Region + Third entry not found + ``` + +1. Stop the server + + ``` + $ sh ./stopserver.sh + /Users/user/geode/bin/gfsh + (1) Executing - connect + ... + (2) Executing - stop server --name=server + ... + (3) Executing - stop locator --name=locator + .... + ``` diff --git a/examples/cpp/transaction/main.cpp b/examples/cpp/transaction/main.cpp new file mode 100644 index 0000000000..86b1005a53 --- /dev/null +++ b/examples/cpp/transaction/main.cpp @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +using namespace apache::geode::client; + +int main(int argc, char** argv) { + try { + auto cacheFactory = CacheFactory(); + cacheFactory.set("log-level", "none"); + auto cache = cacheFactory.create(); + auto poolFactory = cache.getPoolManager().createFactory(); + + std::cout << "Created cache" << std::endl; + + poolFactory.addLocator("localhost", 10334); + auto pool = poolFactory.create("pool"); + auto regionFactory = cache.createRegionFactory(RegionShortcut::PROXY); + auto region = regionFactory.setPoolName("pool").create("exampleRegion"); + + std::cout << "Created region 'exampleRegion'" << std::endl; + + auto txManager = cache.getCacheTransactionManager(); + + txManager->begin(); + + std::cout << "Began transaction #1" << std::endl; + + region->put("Key1", "Value1"); + region->put("Key2", "Value2"); + + try { + txManager->commit(); + std::cout << "Committed transaction #1" << std::endl; + } catch (const CommitConflictException&) { + std::cout << "Transaction #1 CommitConflictException!" << std::endl; + return 1; + } + + if (region->containsKeyOnServer(CacheableKey::create("Key1"))) { + std::cout << "Obtained the first entry from the Region" << std::endl; + } + else { + std::cout << "ERROR: First entry not found" << std::endl; + } + + if (region->containsKeyOnServer(CacheableKey::create("Key2"))) { + std::cout << "Obtained the second entry from the Region" << std::endl; + } + else { + std::cout << "ERROR: Second entry not found" << std::endl; + } + + txManager->begin(); + + std::cout << "Began transaction #2" << std::endl; + + region->put("Key3", "Value3"); + + region->destroy("Key1"); + + txManager->rollback(); + + std::cout << "Rolled back transaction #2" << std::endl; + + if (region->containsKeyOnServer(CacheableKey::create("Key1"))) { + std::cout << "Obtained the first entry from the Region" << std::endl; + } + else { + std::cout << "ERROR: second entry not found!" << std::endl; + } + + if (region->containsKeyOnServer(CacheableKey::create("Key2"))) { + std::cout << "Obtained the second entry from the Region" << std::endl; + } + else { + std::cout << "ERROR: second entry not found!" << std::endl; + } + + if (region->containsKeyOnServer(CacheableKey::create("Key3"))) { + std::cout << "ERROR: Obtained the third entry from the Region" << std::endl; + } + else { + std::cout << "Third entry not found" << std::endl; + } + + cache.close(); + } + catch (const Exception& ex) { + std::cout << "Transaction Geode Exception: " << ex.getMessage() << std::endl; + } +} diff --git a/examples/cpp/transaction/startserver.sh b/examples/cpp/transaction/startserver.sh new file mode 100755 index 0000000000..fce5f21fdc --- /dev/null +++ b/examples/cpp/transaction/startserver.sh @@ -0,0 +1,31 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#!/usr/bin/env bash +GFSH_PATH="" +which gfsh 2> /dev/null + +if [ $? -eq 0 ]; then + GFSH_PATH="gfsh" +else + if [ "$GEODE_HOME" == "" ]; then + echo "Could not find gfsh. Please set the GEODE_HOME path." + echo "e.g. export GEODE_HOME=" + else + GFSH_PATH=$GEODE_HOME/bin/gfsh + fi +fi + +$GFSH_PATH -e "start locator --name=locator" -e "start server --name=server" -e "create region --name=exampleRegion --type=PARTITION" diff --git a/examples/cpp/transaction/stopserver.sh b/examples/cpp/transaction/stopserver.sh new file mode 100755 index 0000000000..67a0f85ba3 --- /dev/null +++ b/examples/cpp/transaction/stopserver.sh @@ -0,0 +1,31 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#!/usr/bin/env bash +GFSH_PATH="" +which gfsh 2> /dev/null + +if [ $? -eq 0 ]; then + GFSH_PATH="gfsh" +else + if [ "$GEODE_HOME" == "" ]; then + echo "Could not find gfsh. Please set the GEODE_HOME path." + echo "e.g. export GEODE_HOME=" + else + GFSH_PATH=$GEODE_HOME/bin/gfsh + fi +fi + +$GFSH_PATH -e "connect" -e "stop server --name=server" -e "stop locator --name=locator" From 58847d24f69dab5634a63b3b1f2fe49a54d84c19 Mon Sep 17 00:00:00 2001 From: Blake Bender Date: Fri, 4 Jan 2019 10:39:04 -0800 Subject: [PATCH 2/4] GEODE-6201: PR Feedback - Make transaction more "real life" than just manually rollback and check - Incorporate retry logic Co-authored-by: Michael Oleske --- examples/cpp/transaction/README.md | 19 ++--- examples/cpp/transaction/main.cpp | 129 ++++++++++++----------------- 2 files changed, 60 insertions(+), 88 deletions(-) diff --git a/examples/cpp/transaction/README.md b/examples/cpp/transaction/README.md index 1979e69ab0..de5cf6e112 100644 --- a/examples/cpp/transaction/README.md +++ b/examples/cpp/transaction/README.md @@ -1,5 +1,5 @@ # Transaction example -This is a very simple example showing how to use Transaction Manger. This example shows +This is a very simple example showing how to use TransactionManager. This example shows how to begin a transaction, commit a transaction, and rollback a transaction while showing exception handling. We commit two keys and rollback adding a third key and destroying an existing key while showing how to handle exceptions. @@ -39,17 +39,12 @@ existing key while showing how to handle exceptions. ``` $ build/transaction - Created cache - Created region 'exampleRegion' - Began transaction #1 - Committed transaction #1 - Obtained the first entry from the Region - Obtained the second entry from the Region - Began transaction #2 - Rolled back transaction #2 - Obtained the first entry from the Region - Obtained the second entry from the Region - Third entry not found + Created cache + Created region 'exampleRegion' + Rolled back transaction - retrying(4) + Rolled back transaction - retrying(3) + Rolled back transaction - retrying(2) + Committed transaction - exiting ``` 1. Stop the server diff --git a/examples/cpp/transaction/main.cpp b/examples/cpp/transaction/main.cpp index 86b1005a53..ba0c0bbed1 100644 --- a/examples/cpp/transaction/main.cpp +++ b/examples/cpp/transaction/main.cpp @@ -23,91 +23,68 @@ #include #include -using namespace apache::geode::client; - -int main(int argc, char** argv) { - try { - auto cacheFactory = CacheFactory(); - cacheFactory.set("log-level", "none"); - auto cache = cacheFactory.create(); - auto poolFactory = cache.getPoolManager().createFactory(); +using apache::geode::client::Cache; +using apache::geode::client::CacheFactory; +using apache::geode::client::CacheTransactionManager; +using apache::geode::client::RegionShortcut; + +auto keys = { + "Key1", + "Key2", + "Key3", + "Key4", + "Key5", + "Key6", + "Key7", + "Key8", + "Key9", + "Key10" +}; + +void initExternalSystem() { + srand (time(NULL)); +} - std::cout << "Created cache" << std::endl; +int32_t getValueFromExternalSystem() { + auto value = rand() % 10; + if (!value) { + throw "failed to get from external system"; + } - poolFactory.addLocator("localhost", 10334); - auto pool = poolFactory.create("pool"); - auto regionFactory = cache.createRegionFactory(RegionShortcut::PROXY); - auto region = regionFactory.setPoolName("pool").create("exampleRegion"); + return value; +} - std::cout << "Created region 'exampleRegion'" << std::endl; +int main(int argc, char** argv) { + initExternalSystem(); + auto cache = CacheFactory().set("log-level", "none").create(); + auto poolFactory = cache.getPoolManager().createFactory(); - auto txManager = cache.getCacheTransactionManager(); + std::cout << "Created cache" << std::endl; - txManager->begin(); + poolFactory.addLocator("localhost", 10334); + auto pool = poolFactory.create("pool"); + auto regionFactory = cache.createRegionFactory(RegionShortcut::PROXY); + auto region = regionFactory.setPoolName("pool").create("exampleRegion"); - std::cout << "Began transaction #1" << std::endl; + std::cout << "Created region 'exampleRegion'" << std::endl; - region->put("Key1", "Value1"); - region->put("Key2", "Value2"); + auto transactionManager = cache.getCacheTransactionManager(); + auto retries = 5; + while (retries--) { + transactionManager->begin(); try { - txManager->commit(); - std::cout << "Committed transaction #1" << std::endl; - } catch (const CommitConflictException&) { - std::cout << "Transaction #1 CommitConflictException!" << std::endl; - return 1; - } - - if (region->containsKeyOnServer(CacheableKey::create("Key1"))) { - std::cout << "Obtained the first entry from the Region" << std::endl; - } - else { - std::cout << "ERROR: First entry not found" << std::endl; - } - - if (region->containsKeyOnServer(CacheableKey::create("Key2"))) { - std::cout << "Obtained the second entry from the Region" << std::endl; - } - else { - std::cout << "ERROR: Second entry not found" << std::endl; - } - - txManager->begin(); - - std::cout << "Began transaction #2" << std::endl; - - region->put("Key3", "Value3"); - - region->destroy("Key1"); - - txManager->rollback(); - - std::cout << "Rolled back transaction #2" << std::endl; - - if (region->containsKeyOnServer(CacheableKey::create("Key1"))) { - std::cout << "Obtained the first entry from the Region" << std::endl; - } - else { - std::cout << "ERROR: second entry not found!" << std::endl; - } - - if (region->containsKeyOnServer(CacheableKey::create("Key2"))) { - std::cout << "Obtained the second entry from the Region" << std::endl; + for (auto& key : keys) { + auto value = getValueFromExternalSystem(); + region->put(key, value); + } + transactionManager->commit(); + std::cout << "Committed transaction - exiting" << std::endl; + break; + } catch ( ... ) { + transactionManager->rollback(); + std::cout << "Rolled back transaction - retrying(" << retries << ")" << std::endl; } - else { - std::cout << "ERROR: second entry not found!" << std::endl; - } - - if (region->containsKeyOnServer(CacheableKey::create("Key3"))) { - std::cout << "ERROR: Obtained the third entry from the Region" << std::endl; - } - else { - std::cout << "Third entry not found" << std::endl; - } - - cache.close(); - } - catch (const Exception& ex) { - std::cout << "Transaction Geode Exception: " << ex.getMessage() << std::endl; } } + From 88586acd987854ee3f6cbbe41e64060899b6fe11 Mon Sep 17 00:00:00 2001 From: Blake Bender Date: Fri, 4 Jan 2019 11:19:57 -0800 Subject: [PATCH 3/4] GEODE-6210: PR Feedback redux - Use C++ 11 random number generation - Move TransactionManager::begin into try/catch - NC sometimes throws IllegalStateException when calling this in a retry Co-authored-by: Michael Oleske --- examples/cpp/transaction/main.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/examples/cpp/transaction/main.cpp b/examples/cpp/transaction/main.cpp index ba0c0bbed1..3c98a43f8b 100644 --- a/examples/cpp/transaction/main.cpp +++ b/examples/cpp/transaction/main.cpp @@ -16,6 +16,7 @@ */ #include +#include #include #include @@ -41,12 +42,13 @@ auto keys = { "Key10" }; -void initExternalSystem() { - srand (time(NULL)); -} -int32_t getValueFromExternalSystem() { - auto value = rand() % 10; +int getValueFromExternalSystem() { + std::random_device rd{}; + static std::default_random_engine e{rd()}; + static std::uniform_int_distribution d{0, 9}; + auto value = d(e); + if (!value) { throw "failed to get from external system"; } @@ -55,7 +57,6 @@ int32_t getValueFromExternalSystem() { } int main(int argc, char** argv) { - initExternalSystem(); auto cache = CacheFactory().set("log-level", "none").create(); auto poolFactory = cache.getPoolManager().createFactory(); @@ -72,8 +73,8 @@ int main(int argc, char** argv) { auto retries = 5; while (retries--) { - transactionManager->begin(); try { + transactionManager->begin(); for (auto& key : keys) { auto value = getValueFromExternalSystem(); region->put(key, value); From 52f7a8dd1fcc95736e837d33c2a029f4d0a7080c Mon Sep 17 00:00:00 2001 From: Blake Bender Date: Fri, 4 Jan 2019 13:04:21 -0800 Subject: [PATCH 4/4] GEODE-6210: Fix questionable use of statics Co-authored-by: Michael Oleske --- examples/cpp/transaction/main.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/cpp/transaction/main.cpp b/examples/cpp/transaction/main.cpp index 3c98a43f8b..910464486e 100644 --- a/examples/cpp/transaction/main.cpp +++ b/examples/cpp/transaction/main.cpp @@ -44,10 +44,8 @@ auto keys = { int getValueFromExternalSystem() { - std::random_device rd{}; - static std::default_random_engine e{rd()}; - static std::uniform_int_distribution d{0, 9}; - auto value = d(e); + static thread_local std::default_random_engine generator(std::random_device{}()); + auto value = std::uniform_int_distribution{0, 9}(generator); if (!value) { throw "failed to get from external system";