After speaking with Ben Gray and the discussion on the mailing list, this is an attempt to create a sockopt to allow connecting pipes to not immediately be available for traffic. The problem is in a PUSH to many PULL situation, where there is a connect to a PULL which is not there. This connect will immediately create a pipe (unlike bind), and traffic will be load balanced to that pipe. This means if there is a persistently unavailable end point then the traffic will queue until HWM is hit, and older messages will be lost.

This patch adds a sockopt ZMQ_DELAY_ATTACH_ON_CONNECT, which if set to 1 will attempt to preempt this behavior. It does this by extending the use of the session_base to include in the outbound as well as the inbound pipe, and only associates the pipe with the socket once it receives the connected callback via a process_attach message. This works, and a test has been added to show so, but may introduce unexpected complications. The shutdown logic in this class has become marginally more awkward because of this, requiring the session to serve as the sink for both pipes if shutdown occurs with a still-connecting pipe in place. It is also possible there could be issues around flushing the messages, but as I could not directly think how to create such an issue I have not written any code with regards to that.

The documentation has been updated to reflect the change, but please do check over the code and test and review.
This commit is contained in:
Ian Barber
2012-06-01 17:58:19 +01:00
parent c28af41ca4
commit fe3fb419fe
12 changed files with 232 additions and 3 deletions

View File

@@ -111,6 +111,7 @@ zmq::session_base_t::session_base_t (class io_thread_t *io_thread_,
io_object_t (io_thread_),
connect (connect_),
pipe (NULL),
outpipe (NULL),
incomplete_in (false),
pending (false),
engine (NULL),
@@ -150,6 +151,13 @@ void zmq::session_base_t::attach_pipe (pipe_t *pipe_)
pipe->set_event_sink (this);
}
void zmq::session_base_t::onconnect_attach_pipe (pipe_t *pipe_)
{
zmq_assert (!is_terminating ());
zmq_assert (pipe_);
outpipe = pipe_;
}
int zmq::session_base_t::read (msg_t *msg_)
{
// First message to send is identity (if required).
@@ -229,6 +237,12 @@ void zmq::session_base_t::clean_pipes ()
void zmq::session_base_t::terminated (pipe_t *pipe_)
{
// If we get a term signal from our held outpipe
// we can safely ignore it.
if (pipe_ == outpipe) {
return;
}
// Drop the reference to the deallocated pipe.
zmq_assert (pipe == pipe_);
pipe = NULL;
@@ -310,6 +324,12 @@ void zmq::session_base_t::process_attach (i_engine *engine_)
send_bind (socket, pipes [1]);
}
if (outpipe && options.delay_attach_on_connect) {
send_bind (socket, outpipe);
// Forget the outpipe
outpipe = NULL;
}
// Plug in the engine.
zmq_assert (!engine);
engine = engine_;
@@ -358,6 +378,12 @@ void zmq::session_base_t::process_term (int linger_)
// Start pipe termination process. Delay the termination till all messages
// are processed in case the linger time is non-zero.
pipe->terminate (linger_ != 0);
// If we're storing to a to be connected, we can clear that as well
if (outpipe) {
outpipe->set_event_sink (this);
outpipe->terminate (linger_ != 0);
}
// TODO: Should this go into pipe_t::terminate ?
// In case there's no engine and there's only delimiter in the
@@ -385,6 +411,9 @@ void zmq::session_base_t::timer_event (int id_)
// Ask pipe to terminate even though there may be pending messages in it.
zmq_assert (pipe);
pipe->terminate (false);
if (outpipe)
outpipe->terminate (false);
}
void zmq::session_base_t::detached ()