You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by mc...@apache.org on 2018/11/16 00:56:16 UTC

[geode] branch release/1.8.0 updated (411ff86 -> 35f7a43)

This is an automated email from the ASF dual-hosted git repository.

mcmellawatt pushed a change to branch release/1.8.0
in repository https://gitbox.apache.org/repos/asf/geode.git.


    from 411ff86  GEODE-5993: Eliminate race in monitorQueryThread() (#2818)
     new e54e33d  GEODE-5884: Added new command and restored pre 1.8 region function behavior (#2829)
     new 35f7a43  GEODE-5884: Adding to function exception list if cause is FunctionInvocationTargetException (#2809)

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../client/internal/ExecuteRegionFunctionOp.java   | 31 ++++++++--
 .../cache/tier/sockets/CommandInitializer.java     |  7 ++-
 .../sockets/command/ExecuteRegionFunction66.java   |  7 +--
 .../command/ExecuteRegionFunctionGeode18.java      | 62 ++++++++++++++++++++
 .../internal/ExecuteRegionFunctionOpTest.java      | 63 +++++++++++++++++++++
 .../command/ExecuteRegionFunction66Test.java       | 22 ++++----
 ....java => ExecuteRegionFunctionGeode18Test.java} | 42 +++++++-------
 ...Query.java => RollingUpgradeNonHAFunction.java} | 66 ++++++++++++++++++++--
 8 files changed, 250 insertions(+), 50 deletions(-)
 create mode 100644 geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/command/ExecuteRegionFunctionGeode18.java
 create mode 100644 geode-core/src/test/java/org/apache/geode/cache/client/internal/ExecuteRegionFunctionOpTest.java
 copy geode-core/src/test/java/org/apache/geode/internal/cache/tier/sockets/command/{ExecuteRegionFunction66Test.java => ExecuteRegionFunctionGeode18Test.java} (84%)
 copy geode-core/src/upgradeTest/java/org/apache/geode/internal/cache/rollingupgrade/{RollingUpgradeTracePRQuery.java => RollingUpgradeNonHAFunction.java} (61%)


[geode] 02/02: GEODE-5884: Adding to function exception list if cause is FunctionInvocationTargetException (#2809)

Posted by mc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

mcmellawatt pushed a commit to branch release/1.8.0
in repository https://gitbox.apache.org/repos/asf/geode.git

commit 35f7a43a2ed822d7cdb0fc6536726e76be519106
Author: Jason Huynh <hu...@gmail.com>
AuthorDate: Thu Nov 8 15:18:19 2018 -0800

    GEODE-5884: Adding to function exception list if cause is FunctionInvocationTargetException (#2809)
---
 .../client/internal/ExecuteRegionFunctionOp.java   | 31 +++++++++--
 .../internal/ExecuteRegionFunctionOpTest.java      | 63 ++++++++++++++++++++++
 2 files changed, 89 insertions(+), 5 deletions(-)

diff --git a/geode-core/src/main/java/org/apache/geode/cache/client/internal/ExecuteRegionFunctionOp.java b/geode-core/src/main/java/org/apache/geode/cache/client/internal/ExecuteRegionFunctionOp.java
index 3dbf079..5f4f41f 100644
--- a/geode-core/src/main/java/org/apache/geode/cache/client/internal/ExecuteRegionFunctionOp.java
+++ b/geode-core/src/main/java/org/apache/geode/cache/client/internal/ExecuteRegionFunctionOp.java
@@ -335,6 +335,7 @@ public class ExecuteRegionFunctionOp {
 
     private FunctionException functionException;
 
+
     public ExecuteRegionFunctionOpImpl(String region, Function function,
         ServerRegionFunctionExecutor serverRegionExecutor, ResultCollector rc, byte hasResult,
         Set<String> removedNodes) {
@@ -378,6 +379,22 @@ public class ExecuteRegionFunctionOp {
       this.isHA = function.isHA();
     }
 
+    // For testing only
+    ExecuteRegionFunctionOpImpl() {
+      super(MessageType.EXECUTE_REGION_FUNCTION,
+          0);
+      resultCollector = null;
+      function = null;
+      isReExecute = (byte) 0;
+      regionName = "";
+      executor = null;
+      hasResult = (byte) 0;
+      failedNodes = null;
+      functionId = null;
+      executeOnBucketSet = true;
+      isHA = true;
+    }
+
     public ExecuteRegionFunctionOpImpl(String region, String function,
         ServerRegionFunctionExecutor serverRegionExecutor, ResultCollector rc, byte hasResult,
         Set<String> removedNodes, boolean isHA, boolean optimizeForWrite,
@@ -640,13 +657,13 @@ public class ExecuteRegionFunctionOp {
       return null;
     }
 
-    private void addFunctionException(final FunctionException result) {
-      if (result instanceof FunctionInvocationTargetException) {
+    void addFunctionException(final FunctionException result) {
+      if (result.getCause() instanceof FunctionInvocationTargetException) {
         if (this.functionException == null) {
-          this.functionException = new FunctionException(result);
+          this.functionException = result;
         }
-        this.functionException.addException(result);
-      } else if (result instanceof InternalFunctionInvocationTargetException) {
+        this.functionException.addException(result.getCause());
+      } else if (result instanceof FunctionInvocationTargetException) {
         if (this.functionException == null) {
           this.functionException = new FunctionException(result);
         }
@@ -659,6 +676,10 @@ public class ExecuteRegionFunctionOp {
       }
     }
 
+    FunctionException getFunctionException() {
+      return functionException;
+    }
+
     @Override
     protected boolean isErrorResponse(int msgType) {
       return msgType == MessageType.EXECUTE_REGION_FUNCTION_ERROR;
diff --git a/geode-core/src/test/java/org/apache/geode/cache/client/internal/ExecuteRegionFunctionOpTest.java b/geode-core/src/test/java/org/apache/geode/cache/client/internal/ExecuteRegionFunctionOpTest.java
new file mode 100644
index 0000000..4629ce9
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/cache/client/internal/ExecuteRegionFunctionOpTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.cache.client.internal;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import org.apache.geode.cache.execute.FunctionException;
+import org.apache.geode.cache.execute.FunctionInvocationTargetException;
+import org.apache.geode.internal.cache.execute.InternalFunctionInvocationTargetException;
+import org.apache.geode.test.junit.categories.ClientServerTest;
+
+@Category({ClientServerTest.class})
+public class ExecuteRegionFunctionOpTest {
+
+  @Test
+  public void addFunctionExceptionWithFunctionTargetInvocationExceptionWrapsInPlainFunctionException() {
+    FunctionInvocationTargetException exception = mock(FunctionInvocationTargetException.class);
+    ExecuteRegionFunctionOp.ExecuteRegionFunctionOpImpl op =
+        new ExecuteRegionFunctionOp.ExecuteRegionFunctionOpImpl();
+    op.addFunctionException(exception);
+    assertThat(op.getFunctionException()).isInstanceOf(FunctionException.class);
+    assertThat(op.getFunctionException()).isNotInstanceOf(FunctionInvocationTargetException.class);
+  }
+
+  @Test
+  public void addFunctionExceptionWithInternalFunctionTargetInvocationExceptionWrapsInPlainFunctionException() {
+    FunctionInvocationTargetException exception =
+        mock(InternalFunctionInvocationTargetException.class);
+    ExecuteRegionFunctionOp.ExecuteRegionFunctionOpImpl op =
+        new ExecuteRegionFunctionOp.ExecuteRegionFunctionOpImpl();
+    op.addFunctionException(exception);
+    assertThat(op.getFunctionException()).isInstanceOf(FunctionException.class);
+    assertThat(op.getFunctionException())
+        .isNotInstanceOf(InternalFunctionInvocationTargetException.class);
+  }
+
+  @Test
+  public void addFunctionExceptionWithCauseFunctionTargetInvocationExceptionAddsToListOfException() {
+    FunctionInvocationTargetException cause = mock(FunctionInvocationTargetException.class);
+    FunctionException exception = new FunctionException(cause);
+    ExecuteRegionFunctionOp.ExecuteRegionFunctionOpImpl op =
+        new ExecuteRegionFunctionOp.ExecuteRegionFunctionOpImpl();
+    op.addFunctionException(exception);
+    assertThat(op.getFunctionException()).isInstanceOf(FunctionException.class);
+    assertThat(op.getFunctionException().getExceptions()).contains(cause);
+  }
+}


[geode] 01/02: GEODE-5884: Added new command and restored pre 1.8 region function behavior (#2829)

Posted by mc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

mcmellawatt pushed a commit to branch release/1.8.0
in repository https://gitbox.apache.org/repos/asf/geode.git

commit e54e33d19135af64177145d96799d9974812d45d
Author: Jason Huynh <hu...@gmail.com>
AuthorDate: Tue Nov 13 15:16:48 2018 -0800

    GEODE-5884: Added new command and restored pre 1.8 region function behavior (#2829)
    
    * Due to the refactor that caused us to lose wrapping of exceptions,
      the fix to GEODE-5884 cannot be applied to GEODE 1.0-1.7 clients
    * Adding new command class will allow clients to identify which function class
      it is expecting to execute and what behavior to expect
---
 .../cache/tier/sockets/CommandInitializer.java     |   7 +-
 .../sockets/command/ExecuteRegionFunction66.java   |   7 +-
 .../command/ExecuteRegionFunctionGeode18.java      |  62 +++++++++
 .../command/ExecuteRegionFunction66Test.java       |  22 ++-
 ....java => ExecuteRegionFunctionGeode18Test.java} |  42 +++---
 .../RollingUpgradeNonHAFunction.java               | 150 +++++++++++++++++++++
 6 files changed, 250 insertions(+), 40 deletions(-)

diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/CommandInitializer.java b/geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/CommandInitializer.java
index 70857c7..f0cdd55 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/CommandInitializer.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/CommandInitializer.java
@@ -43,6 +43,7 @@ import org.apache.geode.internal.cache.tier.sockets.command.ExecuteFunction70;
 import org.apache.geode.internal.cache.tier.sockets.command.ExecuteRegionFunction;
 import org.apache.geode.internal.cache.tier.sockets.command.ExecuteRegionFunction65;
 import org.apache.geode.internal.cache.tier.sockets.command.ExecuteRegionFunction66;
+import org.apache.geode.internal.cache.tier.sockets.command.ExecuteRegionFunctionGeode18;
 import org.apache.geode.internal.cache.tier.sockets.command.ExecuteRegionFunctionSingleHop;
 import org.apache.geode.internal.cache.tier.sockets.command.GatewayReceiverCommand;
 import org.apache.geode.internal.cache.tier.sockets.command.Get70;
@@ -329,8 +330,12 @@ public class CommandInitializer {
     ALL_COMMANDS.put(Version.GEODE_150, commands);
     ALL_COMMANDS.put(Version.GEODE_160, commands);
     ALL_COMMANDS.put(Version.GEODE_170, commands);
-    ALL_COMMANDS.put(Version.GEODE_180, commands);
 
+    Map<Integer, Command> geode18Commands = new HashMap<Integer, Command>();
+    geode18Commands.putAll(ALL_COMMANDS.get(Version.GEODE_170));
+    geode18Commands.put(MessageType.EXECUTE_REGION_FUNCTION,
+        ExecuteRegionFunctionGeode18.getCommand());
+    ALL_COMMANDS.put(Version.GEODE_180, geode18Commands);
   }
 
   public static Map<Integer, Command> getCommands(Version version) {
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/command/ExecuteRegionFunction66.java b/geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/command/ExecuteRegionFunction66.java
index 166d470..d6c701f 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/command/ExecuteRegionFunction66.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/command/ExecuteRegionFunction66.java
@@ -60,7 +60,7 @@ public class ExecuteRegionFunction66 extends BaseCommand {
     return singleton;
   }
 
-  private ExecuteRegionFunction66() {}
+  ExecuteRegionFunction66() {}
 
   @Override
   public void cmdExecute(final Message clientMessage, final ServerConnection serverConnection,
@@ -396,7 +396,6 @@ public class ExecuteRegionFunction66 extends BaseCommand {
     if (function instanceof String) {
       switch (functionState) {
         case AbstractExecution.NO_HA_HASRESULT_NO_OPTIMIZEFORWRITE:
-          execution.setWaitOnExceptionFlag(true);
           execution.execute((String) function).getResult();
           break;
         case AbstractExecution.HA_HASRESULT_NO_OPTIMIZEFORWRITE:
@@ -406,14 +405,10 @@ public class ExecuteRegionFunction66 extends BaseCommand {
           execution.execute((String) function).getResult();
           break;
         case AbstractExecution.NO_HA_HASRESULT_OPTIMIZEFORWRITE:
-          execution.setWaitOnExceptionFlag(true);
           execution.execute((String) function).getResult();
           break;
       }
     } else {
-      if (!functionObject.isHA()) {
-        execution.setWaitOnExceptionFlag(true);
-      }
       execution.execute(functionObject).getResult();
     }
   }
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/command/ExecuteRegionFunctionGeode18.java b/geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/command/ExecuteRegionFunctionGeode18.java
new file mode 100644
index 0000000..386cd55
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/command/ExecuteRegionFunctionGeode18.java
@@ -0,0 +1,62 @@
+/*
+ * 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.internal.cache.tier.sockets.command;
+
+
+import org.apache.geode.cache.execute.Function;
+import org.apache.geode.internal.cache.execute.AbstractExecution;
+import org.apache.geode.internal.cache.tier.Command;
+
+/**
+ * @since Geode 1.8
+ */
+public class ExecuteRegionFunctionGeode18 extends ExecuteRegionFunction66 {
+
+  private static final ExecuteRegionFunctionGeode18 singleton = new ExecuteRegionFunctionGeode18();
+
+  public static Command getCommand() {
+    return singleton;
+  }
+
+  private ExecuteRegionFunctionGeode18() {}
+
+  void executeFunctionWithResult(Object function, byte functionState,
+      Function<?> functionObject, AbstractExecution execution) {
+    if (function instanceof String) {
+      switch (functionState) {
+        case AbstractExecution.NO_HA_HASRESULT_NO_OPTIMIZEFORWRITE:
+          execution.setWaitOnExceptionFlag(true);
+          execution.execute((String) function).getResult();
+          break;
+        case AbstractExecution.HA_HASRESULT_NO_OPTIMIZEFORWRITE:
+          execution.execute((String) function).getResult();
+          break;
+        case AbstractExecution.HA_HASRESULT_OPTIMIZEFORWRITE:
+          execution.execute((String) function).getResult();
+          break;
+        case AbstractExecution.NO_HA_HASRESULT_OPTIMIZEFORWRITE:
+          execution.setWaitOnExceptionFlag(true);
+          execution.execute((String) function).getResult();
+          break;
+      }
+    } else {
+      if (!functionObject.isHA()) {
+        execution.setWaitOnExceptionFlag(true);
+      }
+      execution.execute(functionObject).getResult();
+    }
+  }
+
+}
diff --git a/geode-core/src/test/java/org/apache/geode/internal/cache/tier/sockets/command/ExecuteRegionFunction66Test.java b/geode-core/src/test/java/org/apache/geode/internal/cache/tier/sockets/command/ExecuteRegionFunction66Test.java
index f7d369e..24be2e9 100644
--- a/geode-core/src/test/java/org/apache/geode/internal/cache/tier/sockets/command/ExecuteRegionFunction66Test.java
+++ b/geode-core/src/test/java/org/apache/geode/internal/cache/tier/sockets/command/ExecuteRegionFunction66Test.java
@@ -67,37 +67,36 @@ public class ExecuteRegionFunction66Test {
   }
 
   @Test
-  public void executingFunctionByStringWithNoHAShouldSetWaitOnException() throws Exception {
+  public void executingFunctionInPreGeode18ByStringWithNoHAShouldNotSetWaitOnException() {
     AbstractExecution execution = mock(AbstractExecution.class);
     String functionName = "functionName";
     when(execution.execute(functionName)).thenReturn(mock(ResultCollector.class));
     this.executeRegionFunction66.executeFunctionWithResult(functionName,
         AbstractExecution.NO_HA_HASRESULT_NO_OPTIMIZEFORWRITE, functionObject, execution);
-    verify(execution, times(1)).setWaitOnExceptionFlag(true);
+    verify(execution, times(0)).setWaitOnExceptionFlag(true);
   }
 
   @Test
-  public void executingFunctionByStringWithNoHAWithOptimizeForWriteShouldSetWaitOnException()
-      throws Exception {
+  public void executingFunctionInPreGeode18ByStringWithNoHAWithOptimizeForWriteShouldNotSetWaitOnException() {
     AbstractExecution execution = mock(AbstractExecution.class);
     String functionName = "functionName";
     when(execution.execute(functionName)).thenReturn(mock(ResultCollector.class));
     this.executeRegionFunction66.executeFunctionWithResult(functionName,
         AbstractExecution.NO_HA_HASRESULT_OPTIMIZEFORWRITE, functionObject, execution);
-    verify(execution, times(1)).setWaitOnExceptionFlag(true);
+    verify(execution, times(0)).setWaitOnExceptionFlag(true);
   }
 
   @Test
-  public void executeFunctionObjectShouldSetWaitOnException() throws Exception {
+  public void executingFunctionObjectInPreGeode18ShouldNotSetWaitOnException() {
     AbstractExecution execution = mock(AbstractExecution.class);
     when(execution.execute(functionObject)).thenReturn(mock(ResultCollector.class));
     this.executeRegionFunction66.executeFunctionWithResult(functionObject,
         AbstractExecution.NO_HA_HASRESULT_OPTIMIZEFORWRITE, functionObject, execution);
-    verify(execution, times(1)).setWaitOnExceptionFlag(true);
+    verify(execution, times(0)).setWaitOnExceptionFlag(true);
   }
 
   @Test
-  public void generateNullArgumentMessageIfRegionIsNull() throws Exception {
+  public void generateNullArgumentMessageIfRegionIsNull() {
     AbstractExecution execution = mock(AbstractExecution.class);
     when(execution.execute(functionObject)).thenReturn(mock(ResultCollector.class));
     assertEquals("The input region for the execute function request is null",
@@ -105,7 +104,7 @@ public class ExecuteRegionFunction66Test {
   }
 
   @Test
-  public void generateNullArgumentMessageIfFunctionIsNullAndRegionIsNotNull() throws Exception {
+  public void generateNullArgumentMessageIfFunctionIsNullAndRegionIsNotNull() {
     AbstractExecution execution = mock(AbstractExecution.class);
     when(execution.execute(functionObject)).thenReturn(mock(ResultCollector.class));
     assertEquals("The input function for the execute function request is null",
@@ -165,7 +164,7 @@ public class ExecuteRegionFunction66Test {
   }
 
   @Test
-  public void getAuthorizedExecuteFunctionReturnsNullIfAuthorizationIsNull() throws Exception {
+  public void getAuthorizedExecuteFunctionReturnsNullIfAuthorizationIsNull() {
     AbstractExecution execution = mock(AbstractExecution.class);
     when(execution.execute(functionObject)).thenReturn(mock(ResultCollector.class));
     String functionName = "functionName";
@@ -177,8 +176,7 @@ public class ExecuteRegionFunction66Test {
   }
 
   @Test
-  public void getAuthorizedExecuteFunctionReturnsExecutionContextIfAuthorizeRequestIsNotNull()
-      throws Exception {
+  public void getAuthorizedExecuteFunctionReturnsExecutionContextIfAuthorizeRequestIsNotNull() {
     AbstractExecution execution = mock(AbstractExecution.class);
     when(execution.execute(functionObject)).thenReturn(mock(ResultCollector.class));
     String functionName = "functionName";
diff --git a/geode-core/src/test/java/org/apache/geode/internal/cache/tier/sockets/command/ExecuteRegionFunction66Test.java b/geode-core/src/test/java/org/apache/geode/internal/cache/tier/sockets/command/ExecuteRegionFunctionGeode18Test.java
similarity index 84%
copy from geode-core/src/test/java/org/apache/geode/internal/cache/tier/sockets/command/ExecuteRegionFunction66Test.java
copy to geode-core/src/test/java/org/apache/geode/internal/cache/tier/sockets/command/ExecuteRegionFunctionGeode18Test.java
index f7d369e..cbeb5d6 100644
--- a/geode-core/src/test/java/org/apache/geode/internal/cache/tier/sockets/command/ExecuteRegionFunction66Test.java
+++ b/geode-core/src/test/java/org/apache/geode/internal/cache/tier/sockets/command/ExecuteRegionFunctionGeode18Test.java
@@ -46,20 +46,21 @@ import org.apache.geode.internal.security.AuthorizeRequest;
 import org.apache.geode.test.junit.categories.ClientServerTest;
 
 @Category({ClientServerTest.class})
-public class ExecuteRegionFunction66Test {
+public class ExecuteRegionFunctionGeode18Test {
   private static final String FUNCTION_ID = "function_id";
 
   @Mock
   private Function functionObject;
 
-  private ExecuteRegionFunction66 executeRegionFunction66;
+  private ExecuteRegionFunctionGeode18 executeRegionFunctionGeode18;
 
   @Rule
   public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties();
 
   @Before
   public void setUp() throws Exception {
-    this.executeRegionFunction66 = (ExecuteRegionFunction66) ExecuteRegionFunction66.getCommand();
+    this.executeRegionFunctionGeode18 =
+        (ExecuteRegionFunctionGeode18) ExecuteRegionFunctionGeode18.getCommand();
 
     this.functionObject = mock(Function.class);
     when(this.functionObject.getId()).thenReturn(FUNCTION_ID);
@@ -67,49 +68,48 @@ public class ExecuteRegionFunction66Test {
   }
 
   @Test
-  public void executingFunctionByStringWithNoHAShouldSetWaitOnException() throws Exception {
+  public void executingFunctionByStringWithNoHAShouldSetWaitOnException() {
     AbstractExecution execution = mock(AbstractExecution.class);
     String functionName = "functionName";
     when(execution.execute(functionName)).thenReturn(mock(ResultCollector.class));
-    this.executeRegionFunction66.executeFunctionWithResult(functionName,
+    this.executeRegionFunctionGeode18.executeFunctionWithResult(functionName,
         AbstractExecution.NO_HA_HASRESULT_NO_OPTIMIZEFORWRITE, functionObject, execution);
     verify(execution, times(1)).setWaitOnExceptionFlag(true);
   }
 
   @Test
-  public void executingFunctionByStringWithNoHAWithOptimizeForWriteShouldSetWaitOnException()
-      throws Exception {
+  public void executingFunctionByStringWithNoHAWithOptimizeForWriteShouldSetWaitOnException() {
     AbstractExecution execution = mock(AbstractExecution.class);
     String functionName = "functionName";
     when(execution.execute(functionName)).thenReturn(mock(ResultCollector.class));
-    this.executeRegionFunction66.executeFunctionWithResult(functionName,
+    this.executeRegionFunctionGeode18.executeFunctionWithResult(functionName,
         AbstractExecution.NO_HA_HASRESULT_OPTIMIZEFORWRITE, functionObject, execution);
     verify(execution, times(1)).setWaitOnExceptionFlag(true);
   }
 
   @Test
-  public void executeFunctionObjectShouldSetWaitOnException() throws Exception {
+  public void executeFunctionObjectShouldSetWaitOnException() {
     AbstractExecution execution = mock(AbstractExecution.class);
     when(execution.execute(functionObject)).thenReturn(mock(ResultCollector.class));
-    this.executeRegionFunction66.executeFunctionWithResult(functionObject,
+    this.executeRegionFunctionGeode18.executeFunctionWithResult(functionObject,
         AbstractExecution.NO_HA_HASRESULT_OPTIMIZEFORWRITE, functionObject, execution);
     verify(execution, times(1)).setWaitOnExceptionFlag(true);
   }
 
   @Test
-  public void generateNullArgumentMessageIfRegionIsNull() throws Exception {
+  public void generateNullArgumentMessageIfRegionIsNull() {
     AbstractExecution execution = mock(AbstractExecution.class);
     when(execution.execute(functionObject)).thenReturn(mock(ResultCollector.class));
     assertEquals("The input region for the execute function request is null",
-        this.executeRegionFunction66.generateNullArgumentMessage(null, null));
+        this.executeRegionFunctionGeode18.generateNullArgumentMessage(null, null));
   }
 
   @Test
-  public void generateNullArgumentMessageIfFunctionIsNullAndRegionIsNotNull() throws Exception {
+  public void generateNullArgumentMessageIfFunctionIsNullAndRegionIsNotNull() {
     AbstractExecution execution = mock(AbstractExecution.class);
     when(execution.execute(functionObject)).thenReturn(mock(ResultCollector.class));
     assertEquals("The input function for the execute function request is null",
-        this.executeRegionFunction66.generateNullArgumentMessage("someRegion", null));
+        this.executeRegionFunctionGeode18.generateNullArgumentMessage("someRegion", null));
   }
 
   @Test
@@ -132,7 +132,7 @@ public class ExecuteRegionFunction66Test {
     when(clientMessage.getPart(8)).thenReturn(part2);
     when(clientMessage.getPart(9)).thenReturn(part3);
     int filterSize = 3;
-    Set filter = this.executeRegionFunction66.populateFilters(clientMessage, filterSize);
+    Set filter = this.executeRegionFunctionGeode18.populateFilters(clientMessage, filterSize);
     assertSame(filterSize, filter.size());
     assertTrue(filter.contains(object1));
     assertTrue(filter.contains(object2));
@@ -158,27 +158,27 @@ public class ExecuteRegionFunction66Test {
     when(clientMessage.getPart(7)).thenReturn(part1);
     when(clientMessage.getPart(8)).thenReturn(part2);
     when(clientMessage.getPart(9)).thenReturn(part3);
-    Set nodes = this.executeRegionFunction66.populateRemovedNodes(clientMessage, 3, 6);
+    Set nodes = this.executeRegionFunctionGeode18.populateRemovedNodes(clientMessage, 3, 6);
     assertTrue(nodes.contains(object1));
     assertTrue(nodes.contains(object2));
     assertTrue(nodes.contains(object3));
   }
 
   @Test
-  public void getAuthorizedExecuteFunctionReturnsNullIfAuthorizationIsNull() throws Exception {
+  public void getAuthorizedExecuteFunctionReturnsNullIfAuthorizationIsNull() {
     AbstractExecution execution = mock(AbstractExecution.class);
     when(execution.execute(functionObject)).thenReturn(mock(ResultCollector.class));
     String functionName = "functionName";
     String regionPath = "regionPath";
     ExecuteFunctionOperationContext context =
-        executeRegionFunction66.getAuthorizedExecuteFunctionOperationContext(null, null, true, null,
+        executeRegionFunctionGeode18.getAuthorizedExecuteFunctionOperationContext(null, null, true,
+            null,
             functionName, regionPath);
     assertNull(context);
   }
 
   @Test
-  public void getAuthorizedExecuteFunctionReturnsExecutionContextIfAuthorizeRequestIsNotNull()
-      throws Exception {
+  public void getAuthorizedExecuteFunctionReturnsExecutionContextIfAuthorizeRequestIsNotNull() {
     AbstractExecution execution = mock(AbstractExecution.class);
     when(execution.execute(functionObject)).thenReturn(mock(ResultCollector.class));
     String functionName = "functionName";
@@ -188,7 +188,7 @@ public class ExecuteRegionFunction66Test {
         .thenReturn(mock(ExecuteFunctionOperationContext.class));
 
     ExecuteFunctionOperationContext context =
-        executeRegionFunction66.getAuthorizedExecuteFunctionOperationContext(null, null, true,
+        executeRegionFunctionGeode18.getAuthorizedExecuteFunctionOperationContext(null, null, true,
             request, functionName, regionPath);
     assertNotNull(context);
   }
diff --git a/geode-core/src/upgradeTest/java/org/apache/geode/internal/cache/rollingupgrade/RollingUpgradeNonHAFunction.java b/geode-core/src/upgradeTest/java/org/apache/geode/internal/cache/rollingupgrade/RollingUpgradeNonHAFunction.java
new file mode 100644
index 0000000..2fc56a4
--- /dev/null
+++ b/geode-core/src/upgradeTest/java/org/apache/geode/internal/cache/rollingupgrade/RollingUpgradeNonHAFunction.java
@@ -0,0 +1,150 @@
+/*
+ * 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.internal.cache.rollingupgrade;
+
+import static org.apache.geode.test.awaitility.GeodeAwaitility.await;
+import static org.apache.geode.test.dunit.Assert.fail;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Properties;
+
+import org.junit.Test;
+
+import org.apache.geode.cache.GemFireCache;
+import org.apache.geode.cache.RegionShortcut;
+import org.apache.geode.cache.execute.Function;
+import org.apache.geode.cache.execute.FunctionContext;
+import org.apache.geode.cache.execute.FunctionInvocationTargetException;
+import org.apache.geode.cache.execute.FunctionService;
+import org.apache.geode.cache.execute.ResultCollector;
+import org.apache.geode.cache30.CacheSerializableRunnable;
+import org.apache.geode.distributed.ConfigurationProperties;
+import org.apache.geode.distributed.internal.DistributionConfig;
+import org.apache.geode.distributed.internal.InternalLocator;
+import org.apache.geode.internal.AvailablePortHelper;
+import org.apache.geode.internal.Version;
+import org.apache.geode.test.dunit.Host;
+import org.apache.geode.test.dunit.NetworkUtils;
+import org.apache.geode.test.dunit.VM;
+import org.apache.geode.test.dunit.standalone.VersionManager;
+
+public class RollingUpgradeNonHAFunction extends RollingUpgrade2DUnitTestBase {
+
+  @Test
+  public void functionExceptionsThrownFromDifferentVersionServerShouldCorrectlyWrapFunctionExceptionCauses()
+      throws Exception {
+    final Host host = Host.getHost(0);
+    VM currentServer1 = host.getVM(VersionManager.CURRENT_VERSION, 0);
+    VM oldServer = host.getVM(oldVersion, 1);
+    VM currentServer2 = host.getVM(VersionManager.CURRENT_VERSION, 2);
+    VM oldServerAndLocator = host.getVM(oldVersion, 3);
+
+    String regionName = "cqs";
+
+    RegionShortcut shortcut = RegionShortcut.PARTITION;
+
+    String serverHostName = NetworkUtils.getServerHostName();
+    int port = AvailablePortHelper.getRandomAvailableTCPPort();
+    try {
+      Properties props = getSystemProperties();
+      props.put(ConfigurationProperties.SERIALIZABLE_OBJECT_FILTER,
+          "org.apache.geode.internal.cache.rollingupgrade.**");
+      props.remove(DistributionConfig.LOCATORS_NAME);
+      invokeRunnableInVMs(invokeStartLocatorAndServer(serverHostName, port, props),
+          oldServerAndLocator);
+
+      // Locators before 1.4 handled configuration asynchronously.
+      // We must wait for configuration configuration to be ready, or confirm that it is disabled.
+      oldServerAndLocator.invoke(
+          () -> await()
+              .untilAsserted(() -> assertTrue(
+                  !InternalLocator.getLocator().getConfig().getEnableClusterConfiguration()
+                      || InternalLocator.getLocator().isSharedConfigurationRunning())));
+
+      props.put(DistributionConfig.LOCATORS_NAME, serverHostName + "[" + port + "]");
+      invokeRunnableInVMs(invokeCreateCache(props), currentServer1, currentServer2, oldServer);
+
+      currentServer1.invoke(invokeAssertVersion(Version.CURRENT_ORDINAL));
+      currentServer2.invoke(invokeAssertVersion(Version.CURRENT_ORDINAL));
+
+      // create region
+      invokeRunnableInVMs(invokeCreateRegion(regionName, shortcut), currentServer1, currentServer2,
+          oldServer, oldServerAndLocator);
+
+      // Locators before 1.4 handled configuration asynchronously.
+      // We must wait for configuration configuration to be ready, or confirm that it is disabled.
+      oldServerAndLocator.invoke(
+          () -> await()
+              .untilAsserted(() -> assertTrue(
+                  !InternalLocator.getLocator().getConfig().getEnableClusterConfiguration()
+                      || InternalLocator.getLocator().isSharedConfigurationRunning())));
+
+      putDataSerializableAndVerify(currentServer1, regionName, 0, 100, currentServer2, oldServer,
+          oldServerAndLocator);
+      runFunction("/" + regionName, currentServer1,
+          currentServer2, oldServer, oldServerAndLocator);
+
+    } finally {
+      invokeRunnableInVMs(invokeCloseCache(), currentServer1, currentServer2, oldServer,
+          oldServerAndLocator);
+    }
+  }
+
+  protected void runFunction(String queryString, VM... vms) {
+    for (VM vm : vms) {
+      vm.invoke(invokeAssertFunctionResults(queryString));
+    }
+  }
+
+  private CacheSerializableRunnable invokeAssertFunctionResults(final String queryString) {
+    return new CacheSerializableRunnable("execute: assertQueryResults") {
+      public void run2() {
+        try {
+          invokeAssertFunctionResult(RollingUpgrade2DUnitTestBase.cache, queryString);
+        } catch (Exception e) {
+          fail("Error asserting query results", e);
+        }
+      }
+    };
+  }
+
+  private static void invokeAssertFunctionResult(GemFireCache cache, String regionName) {
+    ResultCollector rc = null;
+    try {
+      rc = FunctionService.onRegion(cache.getRegion(regionName)).execute(new ExceptionalFunction());
+      rc.getResult();
+      fail("Function executed was expected to throw an exception");
+    } catch (Exception e) {
+      assertNotNull(e.getCause());
+      assertSame(FunctionInvocationTargetException.class, e.getCause().getClass());
+    }
+  }
+
+  private static class ExceptionalFunction implements Function {
+
+    @Override
+    public void execute(FunctionContext context) {
+      throw new FunctionInvocationTargetException("This is an explicitly thrown test exception");
+    }
+
+    @Override
+    public boolean isHA() {
+      return false;
+    }
+  }
+
+}