---
title: Core+ C++ Client
sidebar_label: Usage
---

The Deephaven Core+ C++ client allows you to create and connect to [Persistent Queries](../crash-course/develop-query/deephaven-ide.md#develop-a-persistent-query) using the Community Core engine in the Deephaven Enterprise system. This page provides an overview of how to use the C++ client.

## Create a `SessionManager`

A `SessionManager` object allows the client to keep connections to a Deephaven Enterprise Controller and Authentication services.
First, we need to create a `SessionManager` object, which will create the connections and keep them alive for as long as the `SessionManager` object is valid (not destroyed).

```cpp
#include <string>

#include "deephaven/dhcore/utility/utility.h"
#include "deephaven_enterprise/session/session_manager.h"

using deephaven::dhcore::utility::Basename;
using deephaven::dhcore::utility::GetTidAsString;
using deephaven_enterprise::session::SessionManager;

int main(int argc, char *argv[]) {
  // ...
  const std::string descriptive_name =
      std::string(Basename(argv[0])) +
      " tid=" + GetTidAsString();
  const std::string json_url = "https://server1.mydomain.com:8000/iris/connection.json";
  SessionManager session_manager = SessionManager::FromUrl(descriptive_name, json_url);
  // ...
```

> [!NOTE]
> The code block above connects to a Deephaven server at port `8000`. Use port `8000` for servers with an Envoy service and port `8123` for servers without an Envoy service.

The `SessionManager::FromUrl` factory method takes two arguments.

1. A descriptive name that will be used on the server and client sides to log information about operations performed by this client.
2. A URL pointing to a file in JSON format containing information about connectivity parameters for the Deephaven installation. Such a file is provided by a Deephaven installation under `iris/connection.json`.

The descriptive name can, in principle, be any string of our choosing. However, it is important to select a value that allows us to distinguish our client in logs. It makes sense to include the process and/or thread ID where the client was created; the function `GetTidAsString` is provided for that purpose.

> [!NOTE]
> The client implementation will append information about the host where this client is running, so there is no need to include that in the provided `descriptive_name`.

Three network connections are made in the order indicated below during the creation of the `SessionManager` object.

1. The `connection.json` file is downloaded from the provided URL. This is a temporary connection that only lives until we get the file.
2. A connection is made to the Deephaven Enterprise Authentication service.
3. A connection is made to the Deephaven Enterprise Controller service.

The connections to Authentication and Controller are made based on the information downloaded from `connection.json`. These two connections are maintained as long as the `SessionManager` object is valid.

All these operations must succeed for `SessionManager` to be properly initialized, or an error results. When experiencing connectivity issues, or if the information contained in the `connection.json` file downloaded is wrong, you may see an error similar to:

```
Error: std::string deephaven_enterprise::utility::GetUrl(const string&)@/opt/deephaven/src/iris/DhcInDhe/cpp-client/utility/src/net.cc:121: Timeout was reached
```

> [!NOTE]
> See the [appendix](#appendix) at the end of this document for more information about Deephaven client connectivity and some tools to debug network issues.

More information about Core+ clients and their connectivity can be found in the [Core+ Clients](../sys-admin/architecture/communications.md#core-clients) section of our Communications Protocols documentation.

## Authenticate

Once a `SessionManager` is created, we need to authenticate it. We have two options: password authentication (providing a user and password) or private key authentication (providing a private key file).

```cpp
bool auth_result = false;
if (use_password_authentication) {
  // use password authentication
  auth_result =
      session_manager.PasswordAuthentication(user, password, operate_as);
} else {
  // use private key authentication
  auth_result =
      session_manager.PrivateKeyAuthentication(private_key_filename);
}
```

> [!NOTE]
> The `operate_as` argument for the `PasswordAuthentication` method is only interesting to system administrators; under normal circumstances, you would pass this argument as the same value provided for `user`.

An authenticated `SessionManager` connects to the PQ controller, which then creates a new connection to an existing Persistent Query (PQ) or a new PQ. In either case, the result of a connection is a `DndClient` object.

The `DndClient` object can get results from the PQ.

## Connect to an existing Persistent Query

To connect to an existing PQ, we need to know either the string name or the numeric serial of the PQ we want to connect to.

```cpp
const std::string pq_name = "my_favorite_pq";
DndClient client1 = session_manager.ConnectToPqByName(pq_name, false);

using deephaven_enterprise::session::pqserial_t;  // a signed numeric type
const pqserial_t pq_serial = 9876543210L;
DndClient client2 = session_manager.ConnectToPqBySerial(pq_serial, false);
```

## Create a new temporary Persistent Query and connect to it

To create a temporary PQ, we first need to create a PQ configuration. We can use a `PqConfigBuilder` object for that.
The config object comes with predefined defaults for most parameters. Consult the class documentation for `PqConfigBuilder` for the different options. At a minimum, we have to set the desired heap size for our worker in gigabytes.

```cpp
const std::string pq_name = "my_new_pq";
PqConfigBuilder pq_builder = session_manager.MakeTempPqConfigBuilder(pq_name);
pq_builder.SetMaxHeapSizeGb(4);
pq_builder.SetScriptLanguage("groovy");
```

Once we have a `PqConfigBuilder` ready with our options, we can call its `Build` method to obtain the configuration, and pass it to the `AddQueryAndConnect` method of our `SessionManager`.

```cpp
DndClient client3 = session_manager.AddQueryAndConnect(pq_builder.Build());
```

## Send a query to a `DndClient` and get the result

To interact with the tables in a PQ from the client, we first need to create a `DndTableHandleManager` object from the `DndClient`.

```cpp
// DndClient client = ...
DndTableHandleManager table_manager = client.GetManager();
```

With a `DndTableHandleManager` on hand, we can run a script on the server (PQ). In the example below, we create a table on the server (PQ) and then fetch that table on the client using a `TableHandle` object.

A `TableHandle` object can perform additional operations on the table from the client side. In our example, we print the table to standard output.

```cpp
// "PT1S" is an ISO-8601 duration, it specifies a duration of 1 second.
table_manager.RunScript(
    "my_table = io.deephaven.engine.util.TableTools.timeTable(\"PT1S\")");
sleep(2);  // requires <unistd.h>
TableHandle table_handle = table_manager.FetchTable("my_table");
std::cout << "*** Table my_table is \n"
          << table_handle.Stream(true) << "\n";
```

> [!NOTE]
> The code given to `DndTableHandleManager::RunScript` should match the script language configured for the PQ when it was created. For the case where a new PQ is being created, `PqConfigBuilder::SetScriptLanguage` sets the script language of the new PQ. This method accepts either `"groovy"` or `"python"` as a string argument.

## Close `DndClient` and `SessionManager` objects

When `DndClient` and `SessionManager` objects are destroyed, they are automatically closed. This closes network connections and releases all server-side resources associated with these objects. You can call the `Close` method before the object is destroyed to force it to close right away.

```cpp
client1.Close();
client2.Close();
client3.Close();
session_manager.Close();
```

> [!CAUTION]
> Once the `Close` method is called on either of these objects, any subsequent operation results in an error.

Ensuring these objects are closed promptly supports the best utilization of system resources.

## Complete example

Here's a complete example that connects to a server, creates a temporary PQ, runs a query, and prints the result:

```cpp
#include <iostream>
#include <string>
#include <unistd.h>

#include "deephaven/dhcore/utility/utility.h"
#include "deephaven/client/client.h"
#include "deephaven_enterprise/session/dnd_client.h"
#include "deephaven_enterprise/session/session_manager.h"

using deephaven::client::TableHandle;
using deephaven::dhcore::utility::Basename;
using deephaven::dhcore::utility::GetTidAsString;
using deephaven_enterprise::session::DndClient;
using deephaven_enterprise::session::DndTableHandleManager;
using deephaven_enterprise::session::PqConfigBuilder;
using deephaven_enterprise::session::SessionManager;

int main(int argc, char *argv[]) {
  try {
    // Create session manager
    const std::string descriptive_name =
        std::string(Basename(argv[0])) + " tid=" + GetTidAsString();
    const std::string json_url =
        "https://deephaven-host:8000/iris/connection.json";

    std::cout << "Creating session manager...\n";
    SessionManager session_manager =
        SessionManager::FromUrl(descriptive_name, json_url);

    // Authenticate
    std::cout << "Authenticating...\n";
    if (!session_manager.PasswordAuthentication("user", "password", "user")) {
      std::cerr << "Authentication failed!\n";
      return 1;
    }
    std::cout << "Authenticated successfully.\n";

    // Create a temporary PQ
    std::cout << "Creating temporary PQ...\n";
    PqConfigBuilder pq_builder =
        session_manager.MakeTempPqConfigBuilder("cpp_example_pq");
    pq_builder.SetMaxHeapSizeGb(4);
    pq_builder.SetScriptLanguage("python");

    DndClient client =
        session_manager.AddQueryAndConnect(pq_builder.Build());
    std::cout << "Connected to PQ.\n";

    // Get table manager and run a script
    DndTableHandleManager table_manager = client.GetManager();

    std::cout << "Running script...\n";
    table_manager.RunScript(
        "from deephaven import time_table\n"
        "result = time_table('PT1S').update('X = i')");

    // Wait for some data
    sleep(3);

    // Fetch and display the table
    TableHandle table = table_manager.FetchTable("result");
    std::cout << "\n*** Result table:\n"
              << table.Stream(true) << "\n";

    // Clean up
    client.Close();
    session_manager.Close();
    std::cout << "Done.\n";

  } catch (const std::exception &e) {
    std::cerr << "Error: " << e.what() << "\n";
    return 1;
  }

  return 0;
}
```

To compile this program, use a command like:

```bash
g++ -std=c++20 -I$PREFIX/include -L$PREFIX/lib complete_example.cc -ldhcore -ldhclient -ldhe_client
```

## Appendix

<!-- TODO: Update when DH-22686 is merged.
### Test scripts

When the [generated build bundle](./install-cpp-client.md#unpack-the-build-bundle) is deployed, code examples can be found in `$PREFIX/src/iris/DhcInDhe/cpp-client/examples`, where `$PREFIX` is the `--prefix` path passed into the build script. The examples are already built as binaries that can be run.

The quickest way to verify that the installation is working properly is the `tiny_session_manager` example. It takes a URL pointing to a Deephaven [connection.json](../sys-admin/architecture/connection-json.md) file. When run, it prompts for a username/password. If the connection and credentials are correct, it prints `Session manager services authenticated`:

[!NOTE]
This command only works if you used the --install-tests-and-examples flag when installing the CPP client.

```bash
# Assuming $PREFIX was set to /opt/deephaven
source /opt/deephaven/env.sh  # This defines LD_LIBRARY_PATH - required for the client
/opt/deephaven/bin/tiny_session_manager https://<deephaven_enterprise_url>:8000/iris/connection.json
cd /opt/deephaven/src/iris/DhcInDhe/cpp-client/build/examples/session_manager_examples
# For an Enterprise instance running with Envoy. If Envoy is not in use, the port is 8123.
./tiny_session_manager https://<deephaven_enterprise_url>:8000/iris/connection.json
Creating session manager...
Session manager created.
Enter user: deephaven_user
enter deephaven_user's password:
Authenticating with the session manager services...
Session manager services authenticated.
```
-->

### Connectivity and tools to debug network issues

Deephaven clients use [gRPC](https://grpc.io/), an open-source RPC framework from Google, to implement communications with Deephaven servers. Part of the nature of how gRPC works implies treating network errors as potentially transient: network failures are based on a timeout model. This implies that the wrong host address or a server that is down does not immediately cause a connection attempt to fail; instead, gRPC [keeps trying to connect](https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md), in the assumption that a new host name registration may appear or a server may restart and make the expected port available.

Eventually, if the network issue is not resolved before a timeout window expires, the connection attempt is considered failed, and an error results.

> [!IMPORTANT]
> The default timeout used by Deephaven in its clients is 2 minutes.

Network connectivity issues may appear opaque due to the two-minute timeout. However, treating network connectivity issues always as potentially transient failures is very useful in terms of making clients and servers resilient. It does, however, result in some opaqueness for users: the client may look like it just 'sits there' for two minutes before raising an error.

> [!TIP]
> Setting the `GRPC_VERBOSITY` environment variable before starting a client program can help debug network and connectivity issues. The possible values are `info` and `debug` (`debug` provides more details while being more noisy/chatty).
>
> Setting `GRPC_VERBOSITY` logs details of `gRPC` connection establishment, including SSL authentication and certificate validation to standard output.

## Related documentation

- [Install the Core+ C++ client](./install-cpp-client.md)
- [Core+ Clients](../sys-admin/architecture/communications.md#core-clients)
- [UI Query Management](../query-management/ui-queries.md)
