@Override
  public void lastResult(Object oneResult) {
    if (!function.hasResult()) {
      throw new IllegalStateException(
          String.format("Cannot %s result as the Function#hasResult() is false",
              "send"));
    }
    // this could be done before doing end result
    // so that client receives all the results before

    if (serverSender != null) { // Client-Server
      if (localLastResultReceived) {
        return;
      }
      if (onlyLocal) {
        checkForBucketMovement(oneResult);
        if (bme != null) {
          clientSend(oneResult, dm.getDistributionManagerId());
          lastClientSend(dm.getDistributionManagerId(), bme);
        } else {
          lastClientSend(dm.getDistributionManagerId(), oneResult);
        }
        rc.endResults();
        localLastResultReceived = true;

      } else {
        // call a synchronized method as local node is also waiting to send lastResult
        lastResult(oneResult, rc, false, true, dm.getDistributionManagerId());
      }
    } else { // P2P

      if (msg != null) {
        checkForBucketMovement(oneResult);
        try {
          if (bme != null) {
            msg.sendReplyForOneResult(dm, pr, time, oneResult, false,
                enableOrderedResultStreming);
            throw bme;
          } else {
            msg.sendReplyForOneResult(dm, pr, time, oneResult, true,
                enableOrderedResultStreming);
          }

        } catch (ForceReattemptException e) {
          throw new FunctionException(e);
        } catch (InterruptedException e) {
          throw new FunctionException(e);
        }
      } else {
        if (localLastResultReceived) {
          return;
        }
        if (onlyLocal) {
          checkForBucketMovement(oneResult);
          if (bme != null) {
            rc.addResult(dm.getDistributionManagerId(), oneResult);
            rc.addResult(dm.getDistributionManagerId(), bme);
          } else {
            rc.addResult(dm.getDistributionManagerId(), oneResult);
          }
          // exception thrown will do end result
          rc.endResults();
          localLastResultReceived = true;
        } else {
          // call a synchronized method as local node is also waiting to send lastResult
          lastResult(oneResult, rc, false, true, dm.getDistributionManagerId());
        }
        functionStatsFunctionProvider.apply(function.getId(), dm.getSystem())
            .incResultsReceived();
      }
      // incrementing result sent stats.
      // Bug : remote node as well as local node calls this method to send
      // the result When the remote nodes are added to the local result collector at that
      // time the stats for the result sent is again incremented : Once the PR team comes with the
      // concept of the Streaming FunctionOperation
      // for the partitioned Region then it will be simple to fix this problem.
      functionStatsFunctionProvider.apply(function.getId(), dm.getSystem())
          .incResultsReturned();
    }
  }
ResultSender needs ResultCollector in which to add results one by one. In case of localExecution
it just adds result to the resultCollector whereas for remote cases it takes help of
PRFunctionExecutionStreamer to send results to the calling node. The results will be received in
the ResultReceiver. ResultSender will be instantiated in executeOnDatastore and set in
FunctionContext.
geode-core/src/main/java/org/apache/geode/internal/cache/execute/PartitionedRegionFunctionResultSender.java