Back to Asynchronous Callbacks

Interprocess Communication (IPC) · Asynchronous Callbacks · Practice Question

If You Send a Message Asynchronously, How Can You Verify the Other Process Received It?

Sign in to track your progress

Question

When sending a message asynchronously with neg[h], there is no return value and no blocking. How can you verify that the receiving process actually received and processed the message?

Solution

/ Server — stores received messages and exposes a count
\p 5010
received: 0
.z.ps:{[x] received+:1; value x}

/ Client
serverHandle: hopen `:localhost:5010

/ Method 1: send async, then a synchronous "chaser" query
neg[serverHandle] "a:42"               / async — returns immediately
serverHandle "received"                / sync — runs only after "a:42" has been processed

/ Method 2: request-callback pattern
/ The client defines a callback function, and asks the server to call it when done
.client.ack:{[msg] show "Server acknowledged: ", string msg}
neg[serverHandle] "neg[.z.w] (`.client.ack; `done)"

Explanation

Asynchronous messages are fire-and-forget from the sender's perspective. neg[h] "expression" puts the message in the TCP send buffer and returns immediately. The sender receives no confirmation and has no way to know if the message was received, processed, or caused an error on the server.

Three approaches to verification:

1. Follow-up synchronous query. After sending the async message, send a synchronous query on the same handle. Messages on one handle are processed in the order they were sent, so when the sync query returns, the earlier async message has already been processed. This is simple and reliable, but the sync query blocks the client while it waits.

neg[h] (`.u.upd; `trade; data)    / async update
h "count trade"                    / sync verify — blocks until server responds

2. Callback pattern. The client asks the server to call it back when it has processed the message. On the server, .z.w is the handle of the connection the current message arrived on, so the server can reply to whoever sent it with an async acknowledgement:

/ Server's process function calls back when done
.server.process:{[data]
  / ... process data ...
  neg[.z.w] (`.client.ack; `ok)    / async ack back to the caller
 }

/ Client sends the request asynchronously
neg[serverH] (`.server.process; data)

/ Client's ack handler
.client.ack:{[status] show "Server processed: ", string status}

3. Deferred response. Here the client sends a normal synchronous request, but the server does not reply straight away. Inside .z.pg it calls -30!(::) to defer the reply, carries on handling other messages, and later sends the response with -30!(handle;0b;result) (or an error with -30!(handle;1b;errorMsg)). The client is blocked until the reply arrives, but the server stays free to process other messages in the meantime.

In practice, most tick system designs accept that async messages are not individually acknowledged — a tickerplant may publish millions of updates a day and cannot wait for an acknowledgement from each subscriber. Instead, a slow subscriber shows up as a growing outgoing queue for its handle (visible in .z.W on the publisher), which production systems monitor and act on — for example by disconnecting the slow consumer.