Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fix "BC Break"; Throw MongoCursorTimeoutException during failover

  • Loading branch information...
commit 0660f5311f574e118e92f2a57093f25d4f199ed9 1 parent fb4ce62
Hannes Magnusson bjori authored derickr committed
13 cursor.c
@@ -104,9 +104,9 @@ static signed int get_cursor_header(mongo_connection *con, mongo_cursor *cursor,
104 104
105 105 client = (mongoclient*)zend_object_store_get_object(cursor->resource TSRMLS_CC);
106 106 status = client->manager->recv_header(con, &client->servers->options, cursor->timeout, buf, REPLY_HEADER_LEN, error_message);
107   - /* Read failed, error message populated by recv_header */
108   - if (status == -1) {
109   - return -1;
  107 + if (status < 0) {
  108 + /* Read failed, error message populated by recv_header */
  109 + return abs(status);
110 110 } else if (status < INT_32*4) {
111 111 *error_message = malloc(256);
112 112 snprintf(*error_message, 256, "couldn't get full response header, got %d bytes but expected atleast %d", status, INT_32*4);
@@ -155,7 +155,7 @@ static signed int get_cursor_header(mongo_connection *con, mongo_cursor *cursor,
155 155 }
156 156
157 157 /* Reads a cursors body
158   - * Returns -1 on failure or an int indicating the number of bytes read */
  158 + * Returns -1 on failure, -2 on timeout, -3 on EOF, or an int indicating the number of bytes read */
159 159 static int get_cursor_body(mongo_connection *con, mongo_cursor *cursor, char **error_message TSRMLS_DC)
160 160 {
161 161 mongoclient *client = (mongoclient*)zend_object_store_get_object(cursor->resource TSRMLS_CC);
@@ -196,7 +196,7 @@ int php_mongo_get_reply(mongo_cursor *cursor, zval *errmsg TSRMLS_DC)
196 196 return FAILURE;
197 197 }
198 198
199   - if (get_cursor_body(cursor->connection, cursor, (char **) &error_message TSRMLS_CC) == FAILURE) {
  199 + if (get_cursor_body(cursor->connection, cursor, (char **) &error_message TSRMLS_CC) < 0) {
200 200 #ifdef WIN32
201 201 mongo_cursor_throw(cursor->connection, 12 TSRMLS_CC, "WSA error getting database response %s (%d)", error_message, WSAGetLastError());
202 202 #else
@@ -1527,6 +1527,9 @@ zval* mongo_cursor_throw(mongo_connection *connection, int code TSRMLS_DC, char
1527 1527 * cursor timeout instead. */
1528 1528 if (code == 80) {
1529 1529 exception_ce = mongo_ce_CursorTimeoutException;
  1530 + } else if(code == 2) {
  1531 + /* code=2 comes from recv_header() (abs()) recv_data() stream handlers */
  1532 + exception_ce = mongo_ce_CursorTimeoutException;
1530 1533 } else {
1531 1534 exception_ce = mongo_ce_CursorException;
1532 1535 }
13 io_stream.c
@@ -99,6 +99,11 @@ void* php_mongo_io_stream_connect(mongo_con_manager *manager, mongo_server_def *
99 99
100 100 }
101 101
  102 +/* Returns the bytes read on success
  103 + * Returns -1 on unknown failure
  104 + * Returns -2 on timeout
  105 + * Returns -3 when remote server closes the connection
  106 + */
102 107 int php_mongo_io_stream_read(mongo_connection *con, mongo_server_options *options, int timeout, void *data, int size, char **error_message)
103 108 {
104 109 int num = 1, received = 0;
@@ -119,8 +124,12 @@ int php_mongo_io_stream_read(mongo_connection *con, mongo_server_options *option
119 124 num = php_stream_read(con->socket, (char *) data, len);
120 125
121 126 if (num < 0) {
  127 + /* Doesn't look like this can happen, php_sockop_read overwrites the failure from recv() to return 0 */
  128 + *error_message = strdup("Read from socket failed");
122 129 return -1;
123 130 }
  131 +
  132 + /* It *may* have failed. It also may simply have no data */
124 133 if (num == 0) {
125 134 zval *metadata;
126 135
@@ -144,7 +153,7 @@ int php_mongo_io_stream_read(mongo_connection *con, mongo_server_options *option
144 153 *error_message = malloc(256);
145 154 snprintf(*error_message, 256, "Read timed out after reading %d bytes, waited for %d.%06d seconds", num, rtimeout.tv_sec, rtimeout.tv_usec);
146 155 zval_ptr_dtor(&metadata);
147   - return -1;
  156 + return -2;
148 157 }
149 158 }
150 159 if (zend_hash_find(Z_ARRVAL_P(metadata), "eof", sizeof("eof"), (void**)&tmp) == SUCCESS) {
@@ -152,7 +161,7 @@ int php_mongo_io_stream_read(mongo_connection *con, mongo_server_options *option
152 161 if (Z_BVAL_PP(tmp)) {
153 162 *error_message = strdup("Remote server has closed the connection");
154 163 zval_ptr_dtor(&metadata);
155   - return -1;
  164 + return -3;
156 165 }
157 166 }
158 167 }
9 mcon/connections.c
@@ -363,7 +363,6 @@ static int mongo_connect_send_packet(mongo_con_manager *manager, mongo_connectio
363 363 uint32_t data_size;
364 364 char reply_buffer[MONGO_REPLY_HEADER_SIZE];
365 365 uint32_t flags; /* To check for query reply status */
366   - char *recv_error_message;
367 366
368 367 /* Send and wait for reply */
369 368 if (manager->send(con, options, packet->d, packet->l, error_message) == -1) {
@@ -371,11 +370,9 @@ static int mongo_connect_send_packet(mongo_con_manager *manager, mongo_connectio
371 370 return 0;
372 371 }
373 372 mcon_str_ptr_dtor(packet);
374   - read = manager->recv_header(con, options, options->socketTimeoutMS, reply_buffer, MONGO_REPLY_HEADER_SIZE, &recv_error_message);
375   - if (read == -1) {
376   - *error_message = malloc(256);
377   - snprintf(*error_message, 256, "send_package: error reading from socket: %s", recv_error_message);
378   - free(recv_error_message);
  373 + read = manager->recv_header(con, options, options->socketTimeoutMS, reply_buffer, MONGO_REPLY_HEADER_SIZE, error_message);
  374 + if (read < 0) {
  375 + /* Error already populated */
379 376 return 0;
380 377 }
381 378
2  mcon/io.c
@@ -145,7 +145,7 @@ int mongo_io_recv_header(mongo_connection *con, mongo_server_options *options, i
145 145 *error_message = strdup(strerror(errno));
146 146 return -1;
147 147 } else if (status == 0) {
148   - *error_message = strdup("The socket is closed");
  148 + *error_message = strdup("The socket was closed by remote host");
149 149 return -1;
150 150 }
151 151 return status;
2  tests/replicaset-failover/mongoclient-construct-001.phpt
@@ -59,5 +59,5 @@ string(23) "The world is not enough"
59 59 string(23) "The world is not enough"
60 60 string(20) "MongoCursorException"
61 61 string(%d) "%s:%d: Remote server has closed the connection?"
62   -int(-1)
  62 +int(3)
63 63
2  tests/replicaset-failover/mongocollection-insert-001.phpt
@@ -53,7 +53,7 @@ for($i=0;$i<60; $i++) {
53 53 Killing master
54 54 Master killed
55 55 string(%d) "%s:%d: Remote server has closed the connection?"
56   -int(-1)
  56 +int(3)
57 57 string(51) "Couldn't get connection: No candidate servers found"
58 58 int(16)
59 59 string(51) "Couldn't get connection: No candidate servers found"

0 comments on commit 0660f53

Jeremy Mikola
Collaborator

Doesn't this conflict with the following from MongoCursor::hasNext()?

if (cursor->flag & 1) {
    mongo_cursor_throw(cursor->connection, 2 TSRMLS_CC, "cursor not found");
    return;
}
Jeremy Mikola
Collaborator

Ah, just saw the docs on http://php.net/manual/en/class.mongocursorexception.php. "This usually means that the cursor timed out on the server side".

Please sign in to comment.
Something went wrong with that request. Please try again.