C++ client¶
The ami_client_common library. The entire public API is one header, include it and use the ami
namespace.
The library is C++20 and links ASIO and OpenSSL. ami::Client is the single entry point.
Connect¶
Create a cluster, fill in the configs your program uses, and add it. Each config's third positional
argument is the failover-endpoints list, pass {} for a single server.
The full DatabaseConfig signature is DatabaseConfig(port, ssl, failover, username, password,
query_timeout_ms = 30000, query_limit = 10000, center_name = "").
Run SQL¶
sql returns a std::unique_ptr<TableAccessor>. Check has_result_set(), then advance the cursor.
A column is an ami::Column variant; read a cell with std::visit (or std::get<T> when you know the
type). The async form takes a callback:
One JDBC request at a time per cluster, capped at 10,000 rows
The SQL socket does not queue concurrent requests, so serialize them or use separate clusters. Results default to 10,000 rows; raise the limit on the config or narrow the query.
Subscribe¶
Subclass ClientListener, override the callbacks you need, and attach it before adding the cluster.
CenterIncrementalUpdate fields: seqnum_, action_ (1=add, 2=update, 3=delete), id_,
table_name_, object_id_, and the parallel field_names_ / row_data_. An update carries only the
changed fields, merge by id_, the stable numeric row identity. object_id_ is the Relay-publish
key and is empty on subscription updates for SQL-created rows, so do not merge on it.
Callbacks run on the client's I/O threads
The string_views and data in a CenterIncrementalUpdate are valid only for the duration of the
callback: copy anything you keep. Keep the callback fast and run blocking work,
and any same-connection synchronous call, on your own thread rather than in the callback. Guard
your own shared state.
Knowing when the initial load is done. on_subscribe fires when the request reaches the server,
before the initial rows, which arrive as on_update with action_ == 1 (add). No listener callback
marks the snapshot complete. Pass sync=true to subscribe to block until the initial snapshot has
been received.
Server-side filter and projection¶
Use a CenterSubscription to push a WHERE filter and a column list to the Center:
unsubscribe(cluster, tables) and get_table_schema(cluster, table) are available in C++.
Publish¶
Build the field names and row, then publish. Publishing is asynchronous; an outbox replays across reconnects.
publish_object(cluster, table, object_id, fields, row, seqnum = 0, expires_in_ms = 0). For the
pre-serialized hot path, use serialize_object(...) once and publish_raw(...) many times.
Bulk insert¶
Build a Table column-by-column with ColumnBuilder<T> (the type sets the wire type), then
sql_insert or sql_replace.
For types the compiler cannot map on its own, UUID, BigDecimal, BigInteger, declare them explicitly
with add_column_as:
sql_replace creates the table if it is missing, otherwise deletes all its rows, then inserts the rows passed; its suffix argument passes a persist-engine
clause.
Commands (bidirectional)¶
Register a named command that the platform or another client can invoke; handle it in on_command and
reply with send_command_response. See the risk and P&L use case.
TLS and mutual TLS¶
Set the SSL options on the config's shard before adding the cluster:
A secured server requires the client cert (mutual TLS). See Configuration → TLS.
Disconnect and teardown¶
remove_cluster(name, sync=false) is the counterpart to add_cluster: it drops that cluster's JDBC,
Center, and Relay connections and stops its updates. Use it to stop a subscription (alongside
unsubscribe, above) and to shut a cluster down cleanly.
What this does not support¶
- No transactions: SQL runs statement by statement, with no multi-statement unit of work.
- No concurrent JDBC requests on one cluster: the SQL socket does not queue them, so serialize requests or use a second cluster.
- No wire compression: every protocol sends its data uncompressed.