Skip to main content

You want a timeout (but which one?)

· 7 min read

/by Barzilai Spinak a.k.a. @barspi a.k.a. The Facilitator/

A long time ago, our Benevolent Dictator for Life published a blog post titled You want a timeout, which I suggest reading before this one. That post is still totally valid, but when time comes to actually configure those timeouts, the available options can get a little confusing.

In the jPOS world, several configuration options use terms like "timeout" or "alive" in their name, but each serves a different purpose. Due to the organic growth of jPOS throughout the years, configuration option names where often chosen because they made sense within the specific context or use case. They have a distinct meaning within a particular jPOS component, and apply only at a certain point during the message flow or protocol stage. However, when viewed in the broader picture, some of those names can feel repetitive or vague against the full array of available options.

Making things even more confusing, the configuration options follow different design trends, or just use what was historically available in jPOS at the time of implementation. Thus, we have some that are XML attributes, others may be a configuration <property ...> (of the kind you get through the Configurable interface), and yet others are their own full blown XML element.

It's often unclear which option applies to which object, or what are all the available ones. Many are documented, and for others we may need to refer to the source code to understand the full semantics and when they apply

A couple of years ago I compiled, mostly for myself, a file in the style of a ChannelAdaptor deploy file, with comments about all the relevant configurations I found. It took me a good part of an afternoon, following code paths and grepping the jPOS code base.

So here it is, sanitized and commented, for your own benefit. I hope it will clarify this topic and help you build more robust systems

<channel-adaptor name='my-channel'  class="org.jpos.q2.iso.ChannelAdaptor">

<!-- ================================
This is the ISOChannel being wrapped by the ChannelAdaptor.
The following explanations apply if the channel implementation extends BaseChannel.
================================= -->
<channel class="org.jpos.iso.channel.SomeChannel"
packager="org.jpos.iso.packager.GenericPackager"
timeout="DOES NOT EXIST!!!">
^^^^^^^^^^^^^^^^^^^^^^^^^^
I have seen multiple cases of the timeout XML attribute for <channel>
It may be a leftover from an ancient jPOS codebase that does not exist anymore,
or a totally hallucinated property. But it has been copied in many projects in a
sort of "cargo cult programming" fashion.
The channel element has NO KNOWLEDGE of a timeout attribute.
It doesn't hurt that it's there, but serves no purpose .


[... some extra channel config ...]

<!-- ==================================================
The following configuration **properties** are at the CHANNEL LEVEL,
thefore, inside the channel XML element.
====================================================== -->

<!-- boolean: default false
Enable the **low-level** `SO_KEEPALIVE` feature for the socket.
This feature is handled by the operating system's network stack, and neither
jPOS, nor we, can do anything else other than enabling it.

This property has **absolutely nothing** to do with the <keep-alive> XML
element defined for the ChannelAdatpr and explained below in this file.
-->
<property name="keep-alive" value="true" />


<!-- boolean: default false
When enabled, if a zero-length ISOMsg is received from the remote endpoint,
the BaseChannel receive() method will consume it and keep reading from the socket.

Some implementations of BaseChannel will handle this in the overridden getMessageLength()
and reply with a send a zero-length message response back, and continue
reading from the socket. In this case, the zero-length message does not propagate up to
BaseChannel.

This config is the counterpart to a remote sending zero-length messages.
Normally used in a QServer's channel, because in jPOS at least, the remote
client ChannelAdaptor is the one that sends the zero-length messages.

So this config is more or less related to the <keep-alive> element described below
(which is NOT the keep-alive property described above!).
-->
<property name="expect-keep-alive" value="true" />


<!-- pretty obvious: how long to wait when attempting connection.
default: whatever the timeout property (see below) has been set to (or its default)
This is a long value, in milliseconds, and it's by default initialized to the
same value as `timeout` (see below), but a shorter value may be advisable.
-->
<property name="connect-timeout" value="60000" />


<!--
default: 300000 (5 min)

This is the **socket read** timeout, handled at the O.S. socket level.

It is always enabled at **some value** (unless explicitly set to zero, which
effectively disables it, and strongly discouraged!).

If the "reader thread" of the channel does not read even a single byte for that
amount of time, an exception will be thrown, the local socket will be forcefully closed,
and the <reconnect-delay> will kick in, and a new connection will be attempted.

NOTE: Since this timeout only applies when nothing has been received from the socket, the
regular exchange of ISO-8583 echo tests (either receiving a request or a response) will
make keep the channel "happy" and this timeout will not happen.
-->
<property name="timeout" value="300000" />

</channel>

<!-- ==================================================
The following config options are OUTSIDE the channel element.
They are children elements of the ChannelAdaptor (or QServer) wrapper.
They are NOT config properties, but have their own XML tags instead.
====================================================== -->

<!--
WARNING: it's not a true/false but yes/no (anything different from "yes" means "no")
Default: "no"

If enabled, and the ChannelAdaptor hasn't **sent** an ISOMsg out for
"reconnect-delay" milliseconds, then invoke the underlying channel's sendKeepAlive(),
which by defaults sends an ISOMsg of length zero.

It's worth noticing that:
1) The underlying channel's sendKeepAlive() is the one sending the packet (usually a zero-length
message,cwith just the header).
2) The ChannelAdaptor (not the channel) is the one who triggers this behavior.
3) There's no equivalent for a channel wrapped in a QServer.
4) The timeout for the ChannelAdaptor to trigger this behavior is the same as the reconnect-delay
value. It repurposes that config value for this extra behavior.
5) The regular exchange of ISO-8583 echo tests (or any other message) will prevent this from
happening.
-->
<keep-alive>yes/no</keep-alive>


<!--
default: 0, meaning forever

When we receive an ISOMsg through the channel, it will be sent to the <out> space queue.
The ISOMsg has an **expiration** timeout (`sp.out(msg, timeout)`) in the queue taken from this value.
It will live in the queue for this amount of time, until consumed or expired.

Normally there will be a MUX on the other side of the <out> queue who will consume the ISOMsg
quickly (and maybe send it to a TransactionManager queue, with a different timeout, so we don't
typically have to worry about this parameter.
-->
<timeout>15000</timeout>


<!--
Another hallucinated configuration that I've occassionally seen in the wild.
The ready configuration is something belonging to QMUX.
A ChannelAdaptor will maintain an entry in the space called "my-name.ready", using the
ChannelAdaptor's name (from the QBean `name` attribute). But this configuration shown
below does not exist and serves no purpose.
-->
<ready>DOES-NOT-EXIST.ready</ready>


<!-- For a ChannelAdaptor, how long to wait after a disconnection is detected (or forced)
before we attempt a new connection.

It makes no sense in a QServer: If the socket timeout occurrs in a QServer, the channel
will be closed and it's the client's responsibility to reconnect.
-->
<reconnect-delay>10000</reconnect-delay>

<!-- Queues for communicating with the rest of the system.
They are usually associated to the same queues (in reverse) configured for a MUX.
-->
<in>my-channel-send</in>
<out>my-channel-receive</out>

</channel-adaptor>