# Deferred response¶

Ideally, for concurrency, all messaging would be async. However, sync messaging is a convenient paradigm for client apps. Hence -30!x was added as a feature in V3.6, allowing processing of a sync message to be ‘suspended’ to allow other messages to be processed prior to sending a response message.

How it works: -30!(::) indicates the response for the currently-executing sync message will be sent explicitly later via -30!(handle;isError;msg).

You can use -30!(::) at any place in the execution path of .z.pg, start up some work, allow .z.pg to complete without sending a response, and then when the workers complete the task, send the response explicitly.

Kdb+ tracks which handles are expecting a response. If you try to send a response to a handle that is not expecting one, you’ll see

q).z.W / list of socket handles being monitored by kdb+ main thread
8i
q)-30!(8i;0b;helloworld) / try to send a response of (0b;helloworld)
'Handle 8 was not expecting a response msg
[0]  -30!(8i;0b;helloworld)
^

and if the handle is not a member of .z.W, you’ll observe a 'domain error.

Below is a simple script to demonstrate the mechanics of -30!x in a gateway. Further error checking, .z.pc, timeouts, sequence numbers, load-balancing, etc., are left as an exercise for the reader.

workerHandles:hopen each 6000 6001 / open handles to worker processes

pending:()!() / keep track of received results for each clientHandle

/ this example fn joins the results when all are received from the workers
reduceFunction:raze

/ each worker calls this with (0b;result) or (1b;errorString)
callback:{[clientHandle;result]
pending[clientHandle],:enlist result; / store the received result
/ check whether we have all expected results for this client
if[count[workerHandles]=count pending clientHandle;
/ test whether any response (0|1b;...) included an error
isError:0<sum pending[clientHandle][;0];
result:pending[clientHandle][;1]; / grab the error strings or results
/ send the first error or the reduced result
r:\$[isError;{first x where 10h=type each x};reduceFunction]result;
-30!(clientHandle;isError;r);
pending[clientHandle]:(); / clear the temp results
]
}

.z.pg:{[query]
remoteFunction:{[clntHandle;query]
neg[.z.w](callback;clntHandle;@[(0b;)value@;query;{[errorString](1b;errorString)}])
};
neg[workerHandles]@\:(remoteFunction;.z.w;query); / send the query to each worker
-30!(::); / defer sending a response message i.e. return value of .z.pg is ignored
}`
Back to top