This is an automated email from the ASF dual-hosted git repository. pivotalsarge pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/geode-examples.git
The following commit(s) were added to refs/heads/develop by this push: new 5e6e4f5 GEODE-4233: Add an example for transactions. (#51) 5e6e4f5 is described below commit 5e6e4f5dd054f7ca8ffcff859307bbab390bf16b Author: Michael "Sarge" Dodge <mdo...@pivotal.io> AuthorDate: Tue Mar 6 10:35:35 2018 -0800 GEODE-4233: Add an example for transactions. (#51) * GEODE-4233: Add an example for transactions. * GEODE-4233: Address review comments. * GEODE-4233: Fix classpath to include example JAR. * GEODE-4440: Create an example that demonstrates OQL compact range indexes. (#48) * GEODE-4440: Create an example that demonstrates OQL compact range indexes. * GEODE-4440: Address review comments. * GEODE-4440: Address review comment. * GEODE-4646: Add build-specific instructions. (#49) * GEODE-4646: Add build-specific instructions. * GEODE-4646: Fix references to gradle to use the wrapper. * GEODE-4666: Ensure a clean working environment for examples. (#52) * Add both preliminary check and delay after teardown. * GEODE-4233: Address review comment. --- README.md | 3 +- settings.gradle | 1 + transaction/README.md | 60 +++++++++++ transaction/scripts/start.gfsh | 24 +++++ transaction/scripts/stop.gfsh | 19 ++++ .../apache/geode_examples/transaction/Example.java | 111 +++++++++++++++++++++ .../geode_examples/transaction/Incrementer.java | 70 +++++++++++++ 7 files changed, 287 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2aad532..7718197 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ tutorial. * [Cache Listeners](listener/README.md) * [Async Event Queues & Async Event Listeners](async/README.md) * Continuous Querying -* Transactions +* [Transaction](transaction/README.md) * [Eviction](eviction/README.md) * [Expiration](expiration/README.md) * Overflow @@ -85,6 +85,7 @@ tutorial. ### Advanced +* [Lucene Spatial Indexing](luceneSpatial/README.md) * WAN Gateway * Durable subscriptions * Delta propagation diff --git a/settings.gradle b/settings.gradle index d23bb7e..86ebb89 100644 --- a/settings.gradle +++ b/settings.gradle @@ -33,3 +33,4 @@ include 'eviction' include 'serialization' include 'expiration' include 'indexes' +include 'transaction' diff --git a/transaction/README.md b/transaction/README.md new file mode 100644 index 0000000..c6b28c3 --- /dev/null +++ b/transaction/README.md @@ -0,0 +1,60 @@ +<!-- +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. +--> + +# Geode Transaction Example + +This is a simple example that demonstrates the use of [transactions](https://geode.apache.org/docs/guide/11/developing/transactions/working_with_transactions.html) +to protect consistency during concurrent access and modification of data. Since a region may +configured across multiple servers and multiple clients may interact with that region independent of +each other, data integrity relies on synchronization of modifications between all actors. + +An example of how data can become inconsistent during concurrent interaction is as follows: + 1. Client A gets the value of a key. + 2. Client B gets the value of the same key. + 3. Client A puts a new value for the key based upon the original value. + 4. Client B puts a different new value for the key based upon the original value. +The final value for that key is based upon the original value, _not_ the updated value from Client +A. For the final value to contain all the calculations from both clients, both the access and the +modification of the value would need to happen as an atomic action across the region. + +This example starts five child processes, each of which tries one thousand times to get the current +value of a counter, increment that value, and the put the incremented value back into the region. +To protect data consistency, the incrementing is abandoned and retried if another child has already +incremented the value _or_ if another child is simultaenously trying to increment the value. This +example, which should take about a dozen seconds, reports the final value of the counter to show +that all of the children's increments were consistently applied. + +This example assumes you have installed Java and Geode. + +## Steps + +1. From the `geode-examples/transaction` directory, build the example and + run unit tests. + + $ ../gradlew build + +2. Next start a locator, start a server, and create a region. + + $ gfsh run --file=scripts/start.gfsh + +3. Run the example to demonstrate transactions. + + $ ../gradlew run + +4. Shut down the system. + + $ gfsh run --file=scripts/stop.gfsh diff --git a/transaction/scripts/start.gfsh b/transaction/scripts/start.gfsh new file mode 100755 index 0000000..94e14a5 --- /dev/null +++ b/transaction/scripts/start.gfsh @@ -0,0 +1,24 @@ +# +# 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. +# + +start locator --name=locator --bind-address=127.0.0.1 +start server --name=server1 --locators=127.0.0.1[10334] --server-port=0 --classpath=../build/classes/main +start server --name=server2 --locators=127.0.0.1[10334] --server-port=0 --classpath=../build/classes/main +list members + +create region --name=example-region --type=REPLICATE --skip-if-exists=true +describe region --name=example-region diff --git a/transaction/scripts/stop.gfsh b/transaction/scripts/stop.gfsh new file mode 100755 index 0000000..15cd93c --- /dev/null +++ b/transaction/scripts/stop.gfsh @@ -0,0 +1,19 @@ +# +# 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. +# + +connect --locator=127.0.0.1[10334] +shutdown --include-locators=true diff --git a/transaction/src/main/java/org/apache/geode_examples/transaction/Example.java b/transaction/src/main/java/org/apache/geode_examples/transaction/Example.java new file mode 100644 index 0000000..64428e4 --- /dev/null +++ b/transaction/src/main/java/org/apache/geode_examples/transaction/Example.java @@ -0,0 +1,111 @@ +/* + * 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. + */ +package org.apache.geode_examples.transaction; + +import org.apache.geode.cache.Region; +import org.apache.geode.cache.client.ClientCache; +import org.apache.geode.cache.client.ClientCacheFactory; +import org.apache.geode.cache.client.ClientRegionFactory; +import org.apache.geode.cache.client.ClientRegionShortcut; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class Example { + public static final int INCREMENTS = 1000; + + public static final String REGION_NAME = "example-region"; + + public static final String KEY = "counter"; + + final Region<String, Integer> region; + + final Map<Integer, Process> children = new HashMap<>(); + + static String constructJVMPath() { + StringBuilder builder = new StringBuilder(); + builder.append(System.getProperty("java.home")); + builder.append(File.separator); + builder.append("bin"); + builder.append(File.separator); + builder.append("java"); + if (System.getProperty("os.name").toLowerCase().contains("win")) { + builder.append("w.exe"); + } + return builder.toString(); + } + + public static void main(String[] args) { + // connect to the locator using default port 10334 + ClientCache cache = new ClientCacheFactory().addPoolLocator("127.0.0.1", 10334) + .set("log-level", "WARN").create(); + + // create a local region that matches the server region + ClientRegionFactory<String, Integer> clientRegionFactory = + cache.createClientRegionFactory(ClientRegionShortcut.PROXY); + Region<String, Integer> region = clientRegionFactory.create(REGION_NAME); + + Example example = new Example(region); + example.initializeEntry(); + example.executeChildProcesses(5); + + cache.close(); + } + + Example(Region<String, Integer> region) { + this.region = region; + } + + void executeChildProcess(int id) { + String[] command = new String[5]; + command[0] = constructJVMPath(); + command[1] = "-classpath"; + command[2] = System.getProperty("java.class.path") + ":build/libs/transaction.jar"; + command[3] = "org.apache.geode_examples.transaction.Incrementer"; + command[4] = Integer.toString(id); + try { + children.put(id, Runtime.getRuntime().exec(command)); + System.out.println("Executed child " + id); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + + void executeChildProcesses(int numberOfIncrementers) { + System.out.println("Expected value of counter: " + (numberOfIncrementers * INCREMENTS)); + + for (int i = 0; i < numberOfIncrementers; ++i) { + executeChildProcess(i + 1); + } + + for (Map.Entry<Integer, Process> child : children.entrySet()) { + System.out.println("Waiting for " + child.getKey() + "..."); + try { + child.getValue().waitFor(); + System.out.println("Reaped child " + child.getKey()); + } catch (InterruptedException ie) { + ie.printStackTrace(); + } + } + + System.out.println("Actual value of counter: " + region.get(KEY)); + } + + void initializeEntry() { + region.put(KEY, 0); + } +} diff --git a/transaction/src/main/java/org/apache/geode_examples/transaction/Incrementer.java b/transaction/src/main/java/org/apache/geode_examples/transaction/Incrementer.java new file mode 100644 index 0000000..36e8497 --- /dev/null +++ b/transaction/src/main/java/org/apache/geode_examples/transaction/Incrementer.java @@ -0,0 +1,70 @@ +/* + * 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. + */ +package org.apache.geode_examples.transaction; + +import org.apache.geode.cache.CacheTransactionManager; +import org.apache.geode.cache.CommitConflictException; +import org.apache.geode.cache.Region; +import org.apache.geode.cache.client.ClientCache; +import org.apache.geode.cache.client.ClientCacheFactory; +import org.apache.geode.cache.client.ClientRegionFactory; +import org.apache.geode.cache.client.ClientRegionShortcut; + +public class Incrementer { + final int id; + final ClientCache cache; + final Region<String, Integer> region; + + Incrementer(int id, ClientCache cache, Region<String, Integer> region) { + this.id = id; + this.cache = cache; + this.region = region; + } + + public static void main(String[] args) { + // connect to the locator using default port 10334 + ClientCache cache = new ClientCacheFactory().addPoolLocator("127.0.0.1", 10334) + .set("log-level", "WARN").create(); + + // create a local region that matches the server region + ClientRegionFactory<String, Integer> clientRegionFactory = + cache.createClientRegionFactory(ClientRegionShortcut.PROXY); + Region<String, Integer> region = clientRegionFactory.create(Example.REGION_NAME); + + Incrementer incrementer = new Incrementer(Integer.parseInt(args[0]), cache, region); + incrementer.incrementEntry(); + + cache.close(); + } + + void incrementEntry() { + CacheTransactionManager cacheTransactionManager = cache.getCacheTransactionManager(); + for (int i = 0; i < Example.INCREMENTS; ++i) { + boolean incremented = false; + while (!incremented) { + try { + cacheTransactionManager.begin(); + final Integer oldValue = region.get(Example.KEY); + final Integer newValue = oldValue + 1; + region.put(Example.KEY, newValue); + cacheTransactionManager.commit(); + incremented = true; + } catch (CommitConflictException cce) { + // Do nothing. + } + } + } + } +} -- To stop receiving notification emails like this one, please contact pivotalsa...@apache.org.