Send data to Deephaven from a Java client

This guide shows how to send data to Deephaven from an external Java application using the Deephaven Java client and input tables. Input tables allow a client to add, update, and delete rows in a Deephaven table.

Note

If your data source can run directly on the Deephaven server, consider using server-side input tables or DynamicTableWriter instead. Server-side ingestion is generally more efficient because it avoids network overhead.

When to use client-side input tables

Use client-side input tables when:

  • Managing reference data: Upload configuration, lookup tables, or static datasets that may need updates.
  • Tracking state: Maintain the latest status per entity (e.g., device status, order state, user preferences).
  • Interactive editing: Allow users or external systems to add, update, or remove records.
  • Forwarding external data: Relay data from message queues, APIs, or sensors running in separate JVMs.

Setup

Add the Deephaven Java client dependencies to your project. Replace <version> with the version that matches your Deephaven server (e.g., 0.36.1). For Gradle:

JVM configuration

On Java 9+, Arrow requires reflective access to java.nio.Buffer. Add this JVM option to avoid InaccessibleObjectException:

For Gradle's application plugin, configure the run task:

Or for a specific JavaExec task:

Basic pattern

The basic pattern for sending data from a Java client is:

  1. Create a FlightSession to connect to the Deephaven server.
  2. Define an input table spec using InMemoryKeyBackedInputTable, InMemoryAppendOnlyInputTable, or BlinkInputTable.
  3. Execute the spec to create the input table on the server.
  4. Publish the input table to the query scope so it's accessible and the server maintains a reference.
  5. Close the handle to avoid leaking client-side resources (the server-side table persists via the scope).
  6. Build data rows using ColumnHeader and NewTable.
  7. Add to the input table via ScopeId with FlightSession.addToInputTable. For keyed tables, this inserts new rows or updates existing ones.

Repeat steps 6-7 as needed.

Complete example

The following example tracks device status using a keyed input table. Each device has a unique ID, and updates replace the previous status for that device.

Input table types

The Java client supports three types of input tables:

Keyed

Rows are identified by key columns. Adding a row with an existing key replaces that row. Deletion is supported.

With keyed tables, you can delete rows by providing just the key values:

Append-only

Rows are added to the end of the table. No key columns, no updates, no deletion. Use this when you need a simple log or event stream.

Rows are visible for only one update graph cycle, then disappear. Use this for event streams where you only need to process the latest batch.

Resource management

TableHandle lifecycle

TableHandle represents a managed export and implements Closeable. Unclosed handles leak server-side resources for the lifetime of the client. The recommended pattern is:

  1. Execute the spec to create the input table.
  2. Publish it to the query scope (creates a server-side reference).
  3. Close the handle immediately.
  4. Send data via ScopeId.

This ensures the server-side table persists (via the scope) while avoiding client-side resource leaks.

Data upload lifecycle

The FlightSession.addToInputTable method handles data upload automatically:

  1. Uploads the NewTable to the server as a temporary export.
  2. Calls the input table service to add the data.
  3. Releases the temporary export when complete.

If you need more control, you can use the lower-level Session API:

Batch uploads

For better performance, batch multiple rows into a single upload:

Working examples

The Deephaven repository includes complete working examples: