Interprocess communication¶
The server runs on the left; client commands run on the right.
Keep a table in one PeachQ process and query it from another. This guide uses two terminals to show synchronous requests, asynchronous updates and the handlers that receive connections and messages. The examples use deterministic trade data so you can check each result.
See the IPC reference for related operations and the Python API recipe for Python clients.
Start a server and a client¶
Start with PeachQ installed and two terminals. In the first terminal, start the server:
q -q -p 5011
-p 5011 starts an IPC listener. -q suppresses the startup banner. Leave this
process running. Use an unused port and keep the example on a trusted machine or
network; the default handlers execute incoming q expressions.
In the second terminal, start the client:
q -q
The client does not need its own listening port.
Query a remote table¶
Create a small table and a function on the server:
On the client, open a connection. hopen returns a handle; store it in h.
The connection string names the host, port, username and password. Here we use
userjim and the illustrative password password. Supplying credentials does
not by itself make the server validate them. We will inspect the username
later inside a remote call.
q)h:hopen `:localhost:5011:userjim:password
q)h "2+2"
4
q)h "select from trade"
| sym | price | size |
| symbol | float | long |
|--------|-------|------|
| AAPL | 185.5 | 10 |
| MSFT | 420 | 20 |
Applying the positive handle to a string sends a synchronous request. The
server evaluates the expression and the client waits for its result. trade
exists on the server; the returned table is a value in the client process.
You can retain a local copy with localTrade:h "trade".
Send functions and arguments¶
Strings are convenient at the console. A message can also be a list whose first item is a function or a function name, followed by its arguments. This avoids building a query string just to pass values.
Run both forms on the client:
The symbol `double asks the server to call its named function. The second
message supplies the function itself. Both calls execute on the server and
return their results to the client.
Send an asynchronous update¶
Use the negative handle to send a message without requesting a result. On the client, insert one trade, then make a synchronous request to check it:
q)neg[h] (`insert;`trade;(`AAPL;186.0;5));
q)h "count trade"
3
q)h "select volume:sum size by sym from trade"
| sym | volume |
| symbol | long |
|========|--------|
| AAPL | 15 |
| MSFT | 20 |
The semicolon suppresses the local expression's return value. The asynchronous send alone is not an acknowledgement that the update succeeded. The following synchronous request on the same connection checks the server after that message.
Enter trade on the server to inspect all three rows independently:
q)trade
| sym | price | size |
| symbol | float | long |
|--------|-------|------|
| AAPL | 185.5 | 10 |
| MSFT | 420 | 20 |
| AAPL | 186 | 5 |
Inspect the caller¶
Within a remote call, .z.w identifies the connection, .z.a gives the caller's
IPv4 address as an integer, and .z.u gives its username. On the client,
enter h "(.z.w;.z.a;.z.u)".
The connection handle varies between sessions. For this localhost connection,
the remaining values are 2130706433i (127.0.0.1) and `userjim. These values
are useful when recording which connection made a request.
Close this connection with hclose h before installing the handlers in the
next section.
Observe connection and message handlers¶
PeachQ calls these functions for IPC events:
| Handler | Event | Argument |
|---|---|---|
.z.po |
Connection opened | Connection handle |
.z.pg |
Synchronous message | Received message |
.z.ps |
Asynchronous message | Received message |
.z.pc |
Connection closed | Connection handle |
On the server, create an event log and install four handlers:
events:([]event:`symbol$();handle:`int$())
.z.po:{`events insert (`open;x);}
.z.pc:{`events insert (`close;x);}
.z.pg:{`events insert (`sync;.z.w);value x}
.z.ps:{`events insert (`async;.z.w);value x;}
The message handlers append to events and then call value x to evaluate the
incoming message. The synchronous handler returns that value to the caller.
The asynchronous handler ends with a semicolon because no reply is requested.
Replacing a handler also replaces its normal behaviour: omitting value x
would stop these handlers from evaluating the message.
On the client, connect again, request a total, update one row and read it back:
q)h:hopen `:localhost:5011:userjim:password
q)h "sum trade.size"
35
q)neg[h] "update size:30 from `trade where sym=`MSFT";
q)h "select from trade where sym=`MSFT"
| sym | price | size |
| symbol | float | long |
|--------|-------|------|
| MSFT | 420 | 30 |
q)hclose h
Back on the server, inspect events. It contains five rows in this order:
open, sync, async, sync, close, all for the same connection handle.
The second synchronous request both returns the updated row and produces another
sync event. Check the sequence directly:
The 1b result confirms that all four handlers ran. Enter exit 0 in each
terminal when finished. Restart the server for a fresh demo; the table and
custom handlers belong to this process.