Issues

ZF-7520: fsocken does not implement a timeout in Zend_Queue_Stomp_Client_Connection

Description

The Fsockopen function does not currently implement a timeout which means that if the broker is down then queue->send results in php timeout. I have gone through the code and it does'nt look like there is any other config parameter that can take care of this. I had to manually add a timeout parameter to the fsockopen call to make queue->send work (throw an exception) in case the ActiveMq broker was not running

Comments

Setting component to Zend_Queue and assigning to Justin.

Hi,

Thank you for reporting this bug. I am not clear on what you are reporting as being broken. The fsockopen function will use the default "default_socket_timeout" function.

I will check into the send function (php:fwrite()) needing a timeout...

-daniel

I manually tested this.

If the Activemq adapter is not up, then I get a connection refused error:

Zend_Queue_Exception: Unable to connect to tcp://127.0.0.1; error = Connection refused ( errno = 111 ) in [filtered]/trunk/library/Zend/Queue/Stomp/Client/Connection.php on line 76

I will next test for what happens when I terminate the ActiveMQ while recieving messages.

I have manually tested terminating the ActiveMQ broker while it was running, while sending "Hello world" messages to it.

I got the following error:

php test.php Sending Hello world 1 Sending Hello world 2 Sending Hello world 3 Sending Hello world 4 Sending Hello world 5 Sending Hello world 6 Sending Hello world 7 Sending Hello world 8 Sending Hello world 9 Sending Hello world 10

Zend_Queue_Exception: No bytes written in [filtered]/ZF/trunk/library/Zend/Queue/Stomp/Client/Connection.php on line 161

Call Stack: 0.0003 100136 1. {main}() [filtered]/ZF/trunk/test.php:0 9.0591 1008432 2. Zend_Queue->send() [filtered]/ZF/trunk/test.php:21 9.0591 1008432 3. Zend_Queue_Adapter_Activemq->send() [filtered]/ZF/trunk/library/Zend/Queue.php:416 9.0593 1008432 4. Zend_Queue_Stomp_Client->send() [filtered]/ZF/trunk/library/Zend/Queue/Adapter/Activemq.php:263 9.0593 1008432 5. Zend_Queue_Stomp_Client_Connection->write() [filtered]/ZF/trunk/library/Zend/Queue/Stomp/Client.php:138

Fatal error: Exception thrown without a stack frame in Unknown on line 0

Could you provide more information about the bug you have received? And if possible, please provide the get_ini('default_socket_timeout') setting.

-daniel

Thats strange,

The code I am seeing in 1.9 RC1 distro is


....
$this->_socket = fsockopen($str, $port, $errno, $errstr,10);
        
        // the code below is all the same
         if ($this->_socket === false) {
            // aparently there is some reason that fsockopen will return false
            // but it normally throws an error.
            require_once 'Zend/Queue/Exception.php';
            throw new Zend_Queue_Exception("Unable to connect to $str; error = $errstr ( errno = $errno )");
        }
 

the fsockopen call is not inside a try/catch so I am not sure how the exception is being thrown? The code does not get to the point where it can check if the socket is open and is a valid resource.

Anyways, default_socket_timeout is set to 60 secs in my php.ini and the page making the request to my broker times out after that. All fine and dandy there.

The problem is that a) while 60 secs timeout maybe valid for a webpage it is IMHO on the higher side for talking to a broker whose only job is to accect a message and put it on a queue. if it is down it is down and i am not aware of any application that can bring figure out that a broker is down and then bring it back up cleanly within 60 secs. b) I could make a manual call to set smaller timeout through default_socket_connection but the way I have coded my logic - if the broker is down then i dehydrate my data onto the filesystem and db and that leg of the transaction has its own latency. Of course, once the broker times out I could set another timeout before commencing the dehydration process but all that is too much hackery i think.

I have reported this as a minor issue as it is not the end of the world if fsockopen can not be invoked using a custom timeout but if it can be then it would save a lot of working around.

In regards to the distro here is the file from 1.9.0rc1

http://framework.zend.com/svn/framework/…

I suspect you may have modified this file on your system.

--

if fsockopen cannot open the file it returns a false value

http://us3.php.net/fsockopen

then following line which checks if ( $this->socket === false ) will then throw the exception.

--

In regards to the request for a "driverOptions" open_timeout I don't see any problem about this. I will check with Justin first and then code it if he is happy about the idea.

This turned out to be much more of a bigger change than I thought.

I sent an email to zf-contributors requesting help as solving this problem would involve changing the API for the "best" solution and the least best solution "a patch" would involve making a static class variable in Zend_Queue_Stomp_Client_Connection.

Below is the email I sent to zf-contributors

Hello,

Someone recently brought to my attention that fsockopen's dependency on get_ini('default_socket_timeout') was really inconvenient in a web application because it is 60 seconds by default. ( http://www.php.net/manual/en/ini.list.php )

I thought about it, and I agreed that this was probably unreasonable and should allowed to be set in some manner via a configuration setting.

However, implementing this change has become fairly difficult because of the "depth" of the final class that needs to be modified.

Zend_Queue is a class that returns a Zend_Queue + an adapter attached on. The adapter in this question is Activemq. However, Activemq uses Stomp (a simple protocol for messaging), but the Stomp uses another class for actually doing the connection IO.

So in affect, a configuration change passed to

Zend_Queue($options['timeout']) -> Activemq -> Stomp wrapper -> Stomp IO.

In eagle hindsight, I should have made the "connection" options an associative array for several of the constructor calls instead of using open($scheme, $host, $port...). Does anyone have any suggestions for fixing this that don't involve an API change, which I understand is verbotten between minor revisions.

The best that comes to my mind is to use a class variable

class Zend_Queue_Stomp_Client_Connection { static protected $timeout = 10; static public function getTimeout() { return $this->timeout; }

static public function setTimeout() { ... }

}

This saves me from disrupting a large section of code to implement a minor "useful" change, but I don't like this idea of setting a "singleton" -in effect, global configuration variable. Then in Zend Frameworks 2.0 - fix this flaw, which should allow API changes.

Does anyone know a better solution?

-daniel

From the response I got back, I think implementing static functions or updating the manual (Zend_Queue) so that the ini_setting is set properly is the best method.

Bulk change of all issues last updated before 1st January 2010 as "Won't Fix".

Feel free to re-open and provide a patch if you want to fix this issue.