Merge branch 'release/1.6.0'
diff --git a/KEYS b/KEYS
index 4886ba9..b085a1b 100644
--- a/KEYS
+++ b/KEYS
@@ -1025,3 +1025,62 @@
A4DF9y+6usA=
=O5OI
-----END PGP PUBLIC KEY BLOCK-----
+pub rsa4096 2018-04-12 [SC] [expires: 2022-04-12]
+ 876331B45A97E382D1BDFB4444820F9CABF4396F
+uid [ultimate] Mike Stolz <mikestolz@apache.org>
+sig 3 44820F9CABF4396F 2018-04-12 Mike Stolz <mikestolz@apache.org>
+sub rsa4096 2018-04-12 [E] [expires: 2022-04-12]
+sig 44820F9CABF4396F 2018-04-12 Mike Stolz <mikestolz@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+3Uy9JDi1mbJK
+=IZaJ
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/README.md b/README.md
index 2aad532..7718197 100644
--- a/README.md
+++ b/README.md
@@ -76,7 +76,7 @@
* [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 @@
### Advanced
+* [Lucene Spatial Indexing](luceneSpatial/README.md)
* WAN Gateway
* Durable subscriptions
* Delta propagation
diff --git a/build.gradle b/build.gradle
index d748a98..d148d2c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -105,12 +105,13 @@
environment 'PATH', geodePath
ignoreExitValue true
commandLine 'sh', '-c', "" +
- "TIMEOUT=10 ;" +
+ "TIMEOUT=120 ;" +
"echo \"Waiting at most \$TIMEOUT seconds for all members to shut down...\" ;" +
- "while pgrep -f \"(Server|Locator)Launcher\" > /dev/null ; do printf \".\" ; " +
+ "while pgrep -f \"(Server|Locator)Launcher\" > /dev/null ; do" +
+ " printf \".\" ; " +
" sleep 1 ;" +
" TIMEOUT=\$((\$TIMEOUT - 1)) ;" +
- " if [[ \$TIMEOUT -eq 0 ]] ; then" +
+ " if [ \$TIMEOUT -eq 0 ] ; then" +
" echo \"\" ;" +
" exit 10 ;" +
" fi ;" +
@@ -131,7 +132,8 @@
environment 'GEODE_HOME', installDir
environment 'PATH', geodePath
ignoreExitValue true
- commandLine 'sh', '-c', "pgrep -f \"(Server|Locator)Launcher\" > /dev/null ; "
+ commandLine 'sh', '-c', "echo \"Looking for existing member processes...\" ; " +
+ "pgrep -f \"(Server|Locator)Launcher\" ; "
doLast {
if (execResult.exitValue == 0) {
throw new GradleException("Existing members detected. Examples expect a clean environment in which to run.")
diff --git a/gradle.properties b/gradle.properties
index 054dabb..82d5d8c 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -14,8 +14,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-version = 1.5.0
-geodeVersion = 1.5.0
+version = 1.6.0
+geodeVersion = 1.6.0
# release properties, set these on the command line to validate against
# a release candidate
diff --git a/gradle/release.gradle b/gradle/release.gradle
index 526ebeb..5b1d677 100644
--- a/gradle/release.gradle
+++ b/gradle/release.gradle
@@ -30,7 +30,6 @@
it.name.toLowerCase().contains("dist")
}.each { archive ->
archive.doLast {
- ant.checksum file:"${archive.archivePath}", algorithm:"md5", format: 'MD5SUM'
ant.checksum file:"${archive.archivePath}", algorithm:"sha-256", format: 'MD5SUM', fileext:".sha256"
signing {
required { project.hasProperty("signArchives") }
diff --git a/settings.gradle b/settings.gradle
index d23bb7e..86ebb89 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -33,3 +33,4 @@
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.
+ }
+ }
+ }
+ }
+}