You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by db...@apache.org on 2019/01/17 20:02:22 UTC

[geode-native] branch develop updated: GEODE-4337, GEODE-4346 Geode-native guide: Document function execution examples (#435)

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

dbarnes pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode-native.git


The following commit(s) were added to refs/heads/develop by this push:
     new d52262a  GEODE-4337, GEODE-4346 Geode-native guide: Document function execution examples (#435)
d52262a is described below

commit d52262a18fded4cae4d1f931fcce6a90d802ef6d
Author: Dave Barnes <db...@pivotal.io>
AuthorDate: Thu Jan 17 12:02:18 2019 -0800

    GEODE-4337, GEODE-4346 Geode-native guide: Document function execution examples (#435)
    
    * GEODE-4337, GEODE-4346 Geode-native user guide: Document function execution examples
---
 .../source/subnavs/geode-nc-nav.erb                |   2 +-
 .../function-execution.html.md.erb                 | 300 +++++++++++++++++++++
 .../function-execution.html.md.erb-NOTOC           | 300 +++++++++++++++++++++
 .../function-execution.html.md.erb                 |  23 --
 4 files changed, 601 insertions(+), 24 deletions(-)

diff --git a/docs/geode-native-book/master_middleman/source/subnavs/geode-nc-nav.erb b/docs/geode-native-book/master_middleman/source/subnavs/geode-nc-nav.erb
index d1922ec..859b20a 100644
--- a/docs/geode-native-book/master_middleman/source/subnavs/geode-nc-nav.erb
+++ b/docs/geode-native-book/master_middleman/source/subnavs/geode-nc-nav.erb
@@ -86,7 +86,7 @@ limitations under the License.
     </li>
 
     <li>
-      <a href="/docs/geode-native/<%=vars.product_version_nodot%>/function-execution/function-execution.html">Function Execution</a>
+      <a href="/docs/geode-native/<%=vars.product_version_nodot%>/function-execution.html">Function Execution</a>
     </li>
 
     <li>
diff --git a/docs/geode-native-docs/function-execution.html.md.erb b/docs/geode-native-docs/function-execution.html.md.erb
new file mode 100644
index 0000000..e30dddc
--- /dev/null
+++ b/docs/geode-native-docs/function-execution.html.md.erb
@@ -0,0 +1,300 @@
+---
+title:  Function Execution
+---
+
+<!--
+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.
+-->
+
+A client can invoke a server-resident function, with paramaters, and can collect and operate on the returned results.
+
+## <a id="server_side_requirements"></a>Server-side Requirements
+
+To be callable from your client, a function must be resident on the server and registered as available for client access.
+See [Executing a Function in <%=vars.product_name_long%>](/serverman/developing/function_exec/function_execution.html) 
+in the <%=vars.product_name%> User Guide for details on how to write and register server-resident functions.
+
+## <a id="client_side_requirements"></a>Client-side Requirements
+
+The client must connect to the server through a connection pool in order to invoke a server-side function.
+
+## <a id="how_functions_execute"></a>How Functions Execute
+
+1.  The calling client application runs the `execute` method on the `Execution` object. The object must already be registered on the servers.
+2.  The function is invoked on all servers where it needs to run. The locations are determined by the `FunctionService on*` 
+method calls, region configuration, and any filters.
+3.  If the function has results, the result is returned in a `ResultCollector` object.
+4.  The client collects results using the result collector `getResult`.
+
+In every client where you want to execute the function and process the results:
+
+- Use one of the `FunctionService on*` methods to create an `Execution` object. The `on*` methods,
+`onRegion`, `onServer` and `onServers`, define the highest level where the function is run. If
+you use `onRegion` you can further narrow your run scope by setting key filters. The function run
+using `onRegion` is a data dependent function – the others are data-independent functions.
+
+You can run a data dependent function against custom partitioned and colocated partitioned regions. From the client, provide the appropriate key
+ sets to the function call.
+
+-  Use the `Execution` object as needed for additional function configuration. You can:
+    -   Provide a set of data keys to `withFilter` to narrow the execution scope. This works only for `onRegion` Execution objects.
+    -   Provide function arguments to `withArgs`.
+    -   Define a custom `ResultCollector` to `withCollector`.
+
+-  Call the `Execution` object execute method to run the function.
+
+## <a id="processing_function_results"></a>Processing Function Results
+
+The client may use the default result collector. If the client needs special results handling, code
+a custom `ResultsCollector` implementation to replace the default. Use the
+`Execution::withCollector` method to define the custom collector.
+
+For example, to program your client to get the results from a function, use the result collector returned from the function execution, like this:
+
+```cpp
+ResultCollectorPtr rc = FunctionService::onRegion(region)
+                    ->withArgs(args)
+                    ->withFilter(keySet)
+                    ->withCollector(new MyCustomResultCollector())
+                    .execute(Function);
+CacheableVectorPtr functionResult = rc.getResult();
+```
+
+The `getResult` methods of the default result collector block until all results are received, then return the full result set.
+
+To handle the results in a custom manner:
+
+1.  Write a class that implements the `ResultCollector` interface to handle the results in a custom manner. The methods are of two types: one handles data and information from <%=vars.product_name%> and populates the results set, while the other returns the compiled results to the calling application:
+    -  `addResult` is called when results arrive from the `Function` methods. Use `addResult` to add a single result to the ResultCollector.
+    -  `endResults` is called to signal the end of all results from the function execution.
+    -  `getResult` is available to your executing application (the one that calls `Execution.execute`) to retrieve the results. This may block until all results are available.
+    -  `clearResults` is called to clear partial results from the results collector. This is used only for highly available `onRegion` functions where the calling application waits for the results. If the call fails, before <%=vars.product_name%> retries the execution, it calls `clearResults` to ready the instance for a clean set of results.
+2.  Use the `Execution` object in your executing member to call `withCollector`, passing your custom collector, as shown in the example above.
+
+# <a id="examples"></a>Examples
+
+The native client source release contains examples of function execution written for .NET and
+C++. The examples are located in `../examples/dotnet/FunctionExecutionCs` and
+`../examples/cpp/function-execution`, respectively.
+
+Both examples begin with a server-side script that runs `gfsh` commands to create
+a region, simply called "partition_region", which is preloaded with a JAR file containing the
+server-side Java function code. The function, called "ExampleMultiGetFunction", is defined in the
+`examples/utilities` directory of your distribution. As its input parameter, the function takes an array of keys,
+then performs a `get` on each key and returns an array containing the results.
+The function does not load values into the data store. That is a separate operation, performed in these examples by
+the client, and does not involve the server-side function.
+
+As prerequisites, the client code must be aware of the connection to the server, the name of the function, and the expected type/format 
+of the input parameter and return value.
+
+The client:
+
+- creates an execution object
+- populates the execution object with input parameters
+- invokes the object's execute method to invoke the server-side function.
+
+If the client expects results, it must create a result
+object. Optionally, the client can use a provided result collector which offers some predefined
+methods for iterating over and processing return values.
+
+## <a id="dotnet_example"></a>.NET Example
+This section contains code snippets showing highlights of the .NET function execution example. They are not intended for cut-and-paste execution.
+For the complete source, see the example source directory.
+
+The .NET example creates a cache, then uses it to create a connection pool.
+
+```csharp
+   var cacheFactory = new CacheFactory()
+       .Set("log-level", "none");
+   var cache = cacheFactory.Create();
+
+   var poolFactory = cache.GetPoolFactory()
+       .AddLocator("localhost", 10334);
+   var pool = poolFactory.Create("pool");
+```
+
+The example uses the connection pool to create a region, with the same characteristics and name as the server-side region (`partition_region`).
+
+```csharp
+   var regionFactory = cache.CreateRegionFactory(RegionShortcut.PROXY)
+       .SetPoolName("pool");
+   var region = regionFactory.Create<object, object>("partition_region");
+```
+
+The sample client populates the server's datastore with values, using the API and some sample key-value pairs.
+
+```csharp
+   string rtimmonsKey = "rtimmons";
+   string rtimmonsValue = "Robert Timmons";
+   string scharlesKey = "scharles";
+   string scharlesValue = "Sylvia Charles";
+   region.Put(rtimmonsKey, rtimmonsValue, null);
+   region.Put(scharlesKey, scharlesValue, null);
+```
+
+To confirm that the data has been stored, the client uses the API to retrieve the values and write them to the console.
+This is done without reference to the server-side example function.
+
+```csharp
+   var user1 = region.Get(rtimmonsKey, null);
+   var user2 = region.Get(scharlesKey, null);
+
+   Console.WriteLine(rtimmonsKey + " = " + user1);
+   Console.WriteLine(scharlesKey + " = " + user2);
+```
+
+Next, the client retrieves those same values using the server-side example function.
+The client code creates the input parameter, an array of keys whose values are to be retrieved.
+
+```csharp
+   ArrayList keyArgs = new ArrayList();
+   keyArgs.Add(rtimmonsKey);
+   keyArgs.Add(scharlesKey);
+```
+
+The client creates an execution object using `Client.FunctionService.OnRegion` and specifying the region.
+
+```csharp
+   var exc = Client.FunctionService<object>.OnRegion<object, object>(region);
+```
+
+The client then calls the server side function with its input arguments and stores the results in a Client.IResultCollector.
+
+```csharp
+   Client.IResultCollector<object> rc = exc.WithArgs<object>(keyArgs).Execute("ExampleMultiGetFunction");
+```
+
+It then loops through the results and prints the retrieved values.
+
+```csharp
+   ICollection<object> res = rc.GetResult();
+
+   Console.WriteLine("Function Execution Results:");
+   Console.WriteLine("   Count = {0}", res.Count);
+
+   foreach (List<object> item in res)
+   {
+     foreach (object item2 in item)
+     {
+       Console.WriteLine("   value = {0}", item2.ToString());
+     }
+   }
+```
+
+## <a id="cpp_example"></a>C++ Example
+This section contains code snippets showing highlights of the C++ function execution example. They are not intended for cut-and-paste execution.
+For the complete source, see the example source directory.
+
+The C++ example creates a cache.
+
+```cpp
+Cache setupCache() {
+  return CacheFactory()
+      .set("log-level", "none")
+      .create();
+}
+```
+
+The example client uses the cache to create a connection pool, 
+
+```cpp
+void createPool(const Cache& cache) {
+  auto pool = cache.getPoolManager()
+      .createFactory()
+      .addServer("localhost", EXAMPLE_SERVER_PORT)
+      .create("pool");
+}
+```
+
+Then, using that pool, the client creates a region with the same characteristics and name as the server-side region (`partition_region`).
+
+```cpp
+std::shared_ptr<Region> createRegion(Cache& cache) {
+  auto regionFactory = cache.createRegionFactory(RegionShortcut::PROXY);
+  auto region = regionFactory.setPoolName("pool").create("partition_region");
+
+  return region;
+}
+```
+
+The sample client populates the server's datastore with values, using the API and some sample key-value pairs.
+
+```cpp
+void populateRegion(const std::shared_ptr<Region>& region) {
+  for (int i = 0; i < keys.size(); i++) {
+    region->put(keys[i], values[i]);
+  }
+}
+```
+
+As confirmation that the data has been stored, the sample client uses the API to retrieve the values and write them to the console.
+This is done without reference to the server-side example function.
+
+```cpp
+std::shared_ptr<CacheableVector> populateArguments() {
+  auto arguments = CacheableVector::create();
+  for (int i = 0; i < keys.size(); i++) {
+    arguments->push_back(CacheableKey::create(keys[i]));
+  }
+  return arguments;
+}
+```
+
+Next, the client retrieves those same values using the server-side example function.
+The client code creates the input parameter, an array of keys whose values are to be retrieved.
+
+```cpp
+std::vector<std::string> executeFunctionOnServer(const std::shared_ptr<Region> region,
+    const std::shared_ptr<CacheableVector> arguments) {
+  std::vector<std::string> resultList;
+```
+
+The client creates an execution object using `Client.FunctionService.OnRegion` and specifying the region.
+
+```cpp
+  auto functionService = FunctionService::onServer(region->getRegionService());
+```
+
+The client then calls the server side function with its input arguments and stores the results in a Client.IResultCollector.
+
+```cpp
+  if(auto executeFunctionResult = functionService.withArgs(arguments).execute(getFuncIName)->getResult()) {
+    for (auto &arrayList: *executeFunctionResult) {
+      for (auto &cachedString: *std::dynamic_pointer_cast<CacheableArrayList>(arrayList)) {
+        resultList.push_back(std::dynamic_pointer_cast<CacheableString>(cachedString)->value());
+      }
+    }
+  } else {
+    std::cout << "get executeFunctionResult is NULL\n";
+  }
+
+  return resultList;
+}
+```
+
+It then loops through the results and prints the retrieved values.
+
+```cpp
+void printResults(const std::vector<std::string>& resultList) {
+  std::cout << "Result count = " << resultList.size() << std::endl << std::endl;
+  int i = 0;
+  for (auto &cachedString: resultList) {
+    std::cout << "\tResult[" << i << "]=" << cachedString << std::endl;
+    ++i;
+  }
+```
+
diff --git a/docs/geode-native-docs/function-execution.html.md.erb-NOTOC b/docs/geode-native-docs/function-execution.html.md.erb-NOTOC
new file mode 100644
index 0000000..92ef29b
--- /dev/null
+++ b/docs/geode-native-docs/function-execution.html.md.erb-NOTOC
@@ -0,0 +1,300 @@
+---
+title:  Function Execution
+---
+
+<!--
+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.
+-->
+
+A client can invoke a server-resident function, with paramaters, and can collect and operate on the returned results.
+
+## Server-side Requirements
+
+To be callable from your client, a function must be resident on the server and registered as available for client access.
+See [Executing a Function in <%=vars.product_name_long%>](/serverman/developing/function_exec/function_execution.html) 
+in the <%=vars.product_name%> User Guide for details on how to write and register server-resident functions.
+
+## Client-side Requirements
+
+The client must connect to the server through a connection pool in order to invoke a server-side function.
+
+## How Functions Execute
+
+1.  The calling client application runs the `execute` method on the `Execution` object. The object must already be registered on the servers.
+2.  The function is invoked on all servers where it needs to run. The locations are determined by the `FunctionService on*` 
+method calls, region configuration, and any filters.
+3.  If the function has results, the result is returned in a `ResultCollector` object.
+4.  The client collects results using the result collector `getResult`.
+
+In every client where you want to execute the function and process the results:
+
+- Use one of the `FunctionService on*` methods to create an `Execution` object. The `on*` methods,
+`onRegion`, `onServer` and `onServers`, define the highest level where the function is run. If
+you use `onRegion` you can further narrow your run scope by setting key filters. The function run
+using `onRegion` is a data dependent function – the others are data-independent functions.
+
+You can run a data dependent function against custom partitioned and colocated partitioned regions. From the client, provide the appropriate key
+ sets to the function call.
+
+-  Use the `Execution` object as needed for additional function configuration. You can:
+    -   Provide a set of data keys to `withFilter` to narrow the execution scope. This works only for `onRegion` Execution objects.
+    -   Provide function arguments to `withArgs`.
+    -   Define a custom `ResultCollector` to `withCollector`.
+
+-  Call the `Execution` object execute method to run the function.
+
+## Processing Function Results
+
+The client may use the default result collector. If the client needs special results handling, code
+a custom `ResultsCollector` implementation to replace the default. Use the
+`Execution::withCollector` method to define the custom collector.
+
+For example, to program your client to get the results from a function, use the result collector returned from the function execution, like this:
+
+```cpp
+ResultCollectorPtr rc = FunctionService::onRegion(region)
+                    ->withArgs(args)
+                    ->withFilter(keySet)
+                    ->withCollector(new MyCustomResultCollector())
+                    .execute(Function);
+CacheableVectorPtr functionResult = rc.getResult();
+```
+
+The `getResult` methods of the default result collector block until all results are received, then return the full result set.
+
+To handle the results in a custom manner:
+
+1.  Write a class that implements the `ResultCollector` interface to handle the results in a custom manner. The methods are of two types: one handles data and information from <%=vars.product_name%> and populates the results set, while the other returns the compiled results to the calling application:
+    -  `addResult` is called when results arrive from the `Function` methods. Use `addResult` to add a single result to the ResultCollector.
+    -  `endResults` is called to signal the end of all results from the function execution.
+    -  `getResult` is available to your executing application (the one that calls `Execution.execute`) to retrieve the results. This may block until all results are available.
+    -  `clearResults` is called to clear partial results from the results collector. This is used only for highly available `onRegion` functions where the calling application waits for the results. If the call fails, before <%=vars.product_name%> retries the execution, it calls `clearResults` to ready the instance for a clean set of results.
+2.  Use the `Execution` object in your executing member to call `withCollector`, passing your custom collector, as shown in the example above.
+
+# Examples
+
+The native client source release contains examples of function execution written for .NET and
+C++. The examples are located in `../examples/dotnet/FunctionExecutionCs` and
+`../examples/cpp/function-execution`, respectively.
+
+Both examples begin with a server-side script that runs `gfsh` commands to create
+a region, simply called "partition_region", which is preloaded with a JAR file containing the
+server-side Java function code. The function, called "ExampleMultiGetFunction", is defined in the
+`examples/utilities` directory of your distribution. As its input parameter, the function takes an array of keys,
+then performs a `get` on each key and returns an array containing the results.
+The function does not load values into the data store. That is a separate operation, performed in these examples by
+the client, and does not involve the server-side function.
+
+As prerequisites, the client code must be aware of the connection to the server, the name of the function, and the expected type/format 
+of the input parameter and return value.
+
+The client:
+
+- creates an execution object
+- populates the execution object with input parameters
+- invokes the object's execute method to invoke the server-side function.
+
+If the client expects results, it must create a result
+object. Optionally, the client can use a provided result collector which offers some predefined
+methods for iterating over and processing return values.
+
+## .NET Example
+This section contains code snippets showing highlights of the .NET function execution example. They are not intended for cut-and-paste execution.
+For the complete source, see the example source directory.
+
+The .NET example creates a cache, then uses it to create a connection pool.
+
+```csharp
+   var cacheFactory = new CacheFactory()
+       .Set("log-level", "none");
+   var cache = cacheFactory.Create();
+
+   var poolFactory = cache.GetPoolFactory()
+       .AddLocator("localhost", 10334);
+   var pool = poolFactory.Create("pool");
+```
+
+The example uses the connection pool to create a region, with the same characteristics and name as the server-side region (`partition_region`).
+
+```csharp
+   var regionFactory = cache.CreateRegionFactory(RegionShortcut.PROXY)
+       .SetPoolName("pool");
+   var region = regionFactory.Create<object, object>("partition_region");
+```
+
+The sample client populates the server's datastore with values, using the API and some sample key-value pairs.
+
+```csharp
+   string rtimmonsKey = "rtimmons";
+   string rtimmonsValue = "Robert Timmons";
+   string scharlesKey = "scharles";
+   string scharlesValue = "Sylvia Charles";
+   region.Put(rtimmonsKey, rtimmonsValue, null);
+   region.Put(scharlesKey, scharlesValue, null);
+```
+
+To confirm that the data has been stored, the client uses the API to retrieve the values and write them to the console.
+This is done without reference to the server-side example function.
+
+```csharp
+   var user1 = region.Get(rtimmonsKey, null);
+   var user2 = region.Get(scharlesKey, null);
+
+   Console.WriteLine(rtimmonsKey + " = " + user1);
+   Console.WriteLine(scharlesKey + " = " + user2);
+```
+
+Next, the client retrieves those same values using the server-side example function.
+The client code creates the input parameter, an array of keys whose values are to be retrieved.
+
+```csharp
+   ArrayList keyArgs = new ArrayList();
+   keyArgs.Add(rtimmonsKey);
+   keyArgs.Add(scharlesKey);
+```
+
+The client creates an execution object using `Client.FunctionService.OnRegion` and specifying the region.
+
+```csharp
+   var exc = Client.FunctionService<object>.OnRegion<object, object>(region);
+```
+
+The client then calls the server side function with its input arguments and stores the results in a Client.IResultCollector.
+
+```csharp
+   Client.IResultCollector<object> rc = exc.WithArgs<object>(keyArgs).Execute("ExampleMultiGetFunction");
+```
+
+It then loops through the results and prints the retrieved values.
+
+```csharp
+   ICollection<object> res = rc.GetResult();
+
+   Console.WriteLine("Function Execution Results:");
+   Console.WriteLine("   Count = {0}", res.Count);
+
+   foreach (List<object> item in res)
+   {
+     foreach (object item2 in item)
+     {
+       Console.WriteLine("   value = {0}", item2.ToString());
+     }
+   }
+```
+
+## C++ Example
+This section contains code snippets showing highlights of the C++ function execution example. They are not intended for cut-and-paste execution.
+For the complete source, see the example source directory.
+
+The C++ example creates a cache.
+
+```cpp
+Cache setupCache() {
+  return CacheFactory()
+      .set("log-level", "none")
+      .create();
+}
+```
+
+The example client uses the cache to create a connection pool, 
+
+```cpp
+void createPool(const Cache& cache) {
+  auto pool = cache.getPoolManager()
+      .createFactory()
+      .addServer("localhost", EXAMPLE_SERVER_PORT)
+      .create("pool");
+}
+```
+
+Then, using that pool, the client creates a region with the same characteristics and name as the server-side region (`partition_region`).
+
+```cpp
+std::shared_ptr<Region> createRegion(Cache& cache) {
+  auto regionFactory = cache.createRegionFactory(RegionShortcut::PROXY);
+  auto region = regionFactory.setPoolName("pool").create("partition_region");
+
+  return region;
+}
+```
+
+The sample client populates the server's datastore with values, using the API and some sample key-value pairs.
+
+```cpp
+void populateRegion(const std::shared_ptr<Region>& region) {
+  for (int i = 0; i < keys.size(); i++) {
+    region->put(keys[i], values[i]);
+  }
+}
+```
+
+As confirmation that the data has been stored, the sample client uses the API to retrieve the values and write them to the console.
+This is done without reference to the server-side example function.
+
+```cpp
+std::shared_ptr<CacheableVector> populateArguments() {
+  auto arguments = CacheableVector::create();
+  for (int i = 0; i < keys.size(); i++) {
+    arguments->push_back(CacheableKey::create(keys[i]));
+  }
+  return arguments;
+}
+```
+
+Next, the client retrieves those same values using the server-side example function.
+The client code creates the input parameter, an array of keys whose values are to be retrieved.
+
+```cpp
+std::vector<std::string> executeFunctionOnServer(const std::shared_ptr<Region> region,
+    const std::shared_ptr<CacheableVector> arguments) {
+  std::vector<std::string> resultList;
+```
+
+The client creates an execution object using `Client.FunctionService.OnRegion` and specifying the region.
+
+```cpp
+  auto functionService = FunctionService::onServer(region->getRegionService());
+```
+
+The client then calls the server side function with its input arguments and stores the results in a Client.IResultCollector.
+
+```cpp
+  if(auto executeFunctionResult = functionService.withArgs(arguments).execute(getFuncIName)->getResult()) {
+    for (auto &arrayList: *executeFunctionResult) {
+      for (auto &cachedString: *std::dynamic_pointer_cast<CacheableArrayList>(arrayList)) {
+        resultList.push_back(std::dynamic_pointer_cast<CacheableString>(cachedString)->value());
+      }
+    }
+  } else {
+    std::cout << "get executeFunctionResult is NULL\n";
+  }
+
+  return resultList;
+}
+```
+
+It then loops through the results and prints the retrieved values.
+
+```cpp
+void printResults(const std::vector<std::string>& resultList) {
+  std::cout << "Result count = " << resultList.size() << std::endl << std::endl;
+  int i = 0;
+  for (auto &cachedString: resultList) {
+    std::cout << "\tResult[" << i << "]=" << cachedString << std::endl;
+    ++i;
+  }
+```
+
diff --git a/docs/geode-native-docs/function-execution/function-execution.html.md.erb b/docs/geode-native-docs/function-execution/function-execution.html.md.erb
deleted file mode 100644
index 1595501..0000000
--- a/docs/geode-native-docs/function-execution/function-execution.html.md.erb
+++ /dev/null
@@ -1,23 +0,0 @@
----
-title:  Function Execution
----
-
-<!--
-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.
--->
-
-A function is a body of code that resides on a server and that an application can invoke from a client or from another server without the need to send the function code itself.
-