From 51b57b4d2fbb1797f1d447b0cb47d59a41541eec Mon Sep 17 00:00:00 2001 From: Andreas Braun Date: Wed, 2 Oct 2019 10:49:58 +0200 Subject: [PATCH] PHPC-1274: Add function to reset manager instance --- config.m4 | 1 + config.w32 | 2 +- php_phongo.c | 8 ++- src/MongoDB/functions.c | 50 +++++++++++++++++ src/MongoDB/functions.h | 33 +++++++++++ tests/manager/manager-reset-001.phpt | 78 ++++++++++++++++++++++++++ tests/manager/manager-reset-002.phpt | 83 ++++++++++++++++++++++++++++ tests/manager/manager-reset-003.phpt | 82 +++++++++++++++++++++++++++ tests/session/bug1274-001.phpt | 70 +++++++++++++++++++++++ 9 files changed, 405 insertions(+), 2 deletions(-) create mode 100644 src/MongoDB/functions.c create mode 100644 src/MongoDB/functions.h create mode 100644 tests/manager/manager-reset-001.phpt create mode 100644 tests/manager/manager-reset-002.phpt create mode 100644 tests/manager/manager-reset-003.phpt create mode 100644 tests/session/bug1274-001.phpt diff --git a/config.m4 b/config.m4 index ec61c740b..bfaf8a7e5 100644 --- a/config.m4 +++ b/config.m4 @@ -167,6 +167,7 @@ if test "$PHP_MONGODB" != "no"; then src/MongoDB/Monitoring/CommandSucceededEvent.c \ src/MongoDB/Monitoring/Subscriber.c \ src/MongoDB/Monitoring/functions.c \ + src/MongoDB/functions.c \ " PHP_ARG_WITH([libbson], diff --git a/config.w32 b/config.w32 index b453ed7a1..845ad19bb 100644 --- a/config.w32 +++ b/config.w32 @@ -88,7 +88,7 @@ if (PHP_MONGODB != "no") { EXTENSION("mongodb", "php_phongo.c phongo_compat.c", null, PHP_MONGODB_CFLAGS); ADD_SOURCES(configure_module_dirname + "/src", "bson.c bson-encode.c", "mongodb"); ADD_SOURCES(configure_module_dirname + "/src/BSON", "Binary.c BinaryInterface.c DBPointer.c Decimal128.c Decimal128Interface.c Int64.c Javascript.c JavascriptInterface.c MaxKey.c MaxKeyInterface.c MinKey.c MinKeyInterface.c ObjectId.c ObjectIdInterface.c Persistable.c Regex.c RegexInterface.c Serializable.c Symbol.c Timestamp.c TimestampInterface.c Type.c Undefined.c Unserializable.c UTCDateTime.c UTCDateTimeInterface.c functions.c", "mongodb"); - ADD_SOURCES(configure_module_dirname + "/src/MongoDB", "BulkWrite.c Command.c Cursor.c CursorId.c CursorInterface.c Manager.c Query.c ReadConcern.c ReadPreference.c Server.c Session.c WriteConcern.c WriteConcernError.c WriteError.c WriteResult.c", "mongodb"); + ADD_SOURCES(configure_module_dirname + "/src/MongoDB", "BulkWrite.c Command.c Cursor.c CursorId.c CursorInterface.c Manager.c Query.c ReadConcern.c ReadPreference.c Server.c Session.c WriteConcern.c WriteConcernError.c WriteError.c WriteResult.c functions.c", "mongodb"); ADD_SOURCES(configure_module_dirname + "/src/MongoDB/Exception", "AuthenticationException.c BulkWriteException.c CommandException.c ConnectionException.c ConnectionTimeoutException.c Exception.c ExecutionTimeoutException.c InvalidArgumentException.c LogicException.c RuntimeException.c ServerException.c SSLConnectionException.c UnexpectedValueException.c WriteException.c", "mongodb"); ADD_SOURCES(configure_module_dirname + "/src/MongoDB/Monitoring", "CommandFailedEvent.c CommandStartedEvent.c CommandSubscriber.c CommandSucceededEvent.c Subscriber.c functions.c", "mongodb"); ADD_SOURCES(configure_module_dirname + "/src/libmongoc/src/common", PHP_MONGODB_COMMON_SOURCES, "mongodb"); diff --git a/php_phongo.c b/php_phongo.c index 2344052d2..d1d5eeb99 100644 --- a/php_phongo.c +++ b/php_phongo.c @@ -67,6 +67,7 @@ #include "php_phongo.h" #include "php_bson.h" #include "src/BSON/functions.h" +#include "src/MongoDB/functions.h" #include "src/MongoDB/Monitoring/functions.h" #undef MONGOC_LOG_DOMAIN @@ -3344,6 +3345,10 @@ ZEND_BEGIN_ARG_INFO_EX(ai_mongodb_driver_monitoring_subscriber, 0, 0, 1) ZEND_ARG_OBJ_INFO(0, subscriber, MongoDB\\Driver\\Monitoring\\Subscriber, 0) ZEND_END_ARG_INFO(); +ZEND_BEGIN_ARG_INFO_EX(ai_mongodb_driver_manager, 0, 0, 1) + ZEND_ARG_OBJ_INFO(0, manager, MongoDB\\Driver\\Manager, 0) +ZEND_END_ARG_INFO(); + static const zend_function_entry mongodb_functions[] = { ZEND_NS_NAMED_FE("MongoDB\\BSON", fromPHP, PHP_FN(MongoDB_BSON_fromPHP), ai_bson_fromPHP) ZEND_NS_NAMED_FE("MongoDB\\BSON", toPHP, PHP_FN(MongoDB_BSON_toPHP), ai_bson_toPHP) @@ -3353,7 +3358,8 @@ static const zend_function_entry mongodb_functions[] = { ZEND_NS_NAMED_FE("MongoDB\\BSON", fromJSON, PHP_FN(MongoDB_BSON_fromJSON), ai_bson_fromJSON) ZEND_NS_NAMED_FE("MongoDB\\Driver\\Monitoring", addSubscriber, PHP_FN(MongoDB_Driver_Monitoring_addSubscriber), ai_mongodb_driver_monitoring_subscriber) ZEND_NS_NAMED_FE("MongoDB\\Driver\\Monitoring", removeSubscriber, PHP_FN(MongoDB_Driver_Monitoring_removeSubscriber), ai_mongodb_driver_monitoring_subscriber) - PHP_FE_END + ZEND_NS_NAMED_FE("MongoDB\\Driver", reset, PHP_FN(MongoDB_Driver_reset), ai_mongodb_driver_manager) + PHP_FE_END }; /* }}} */ diff --git a/src/MongoDB/functions.c b/src/MongoDB/functions.c new file mode 100644 index 000000000..9022e91a3 --- /dev/null +++ b/src/MongoDB/functions.c @@ -0,0 +1,50 @@ +/* + * Copyright 2019 MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "phongo_compat.h" +#include "php_phongo.h" + +ZEND_EXTERN_MODULE_GLOBALS(mongodb) + +/* {{{ proto void MongoDB\Driver\reset(MongoDB\Driver\Manager $manager) + Resets a manager after forking */ +PHP_FUNCTION(MongoDB_Driver_reset) +{ + zval* zManager = NULL; + php_phongo_manager_t* intern; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &zManager, php_phongo_manager_ce) == FAILURE) { + return; + } + + intern = Z_MANAGER_OBJ_P(zManager); + mongoc_client_reset(intern->client); +} /* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/src/MongoDB/functions.h b/src/MongoDB/functions.h new file mode 100644 index 000000000..01d1a8a5e --- /dev/null +++ b/src/MongoDB/functions.h @@ -0,0 +1,33 @@ +/* + * Copyright 2019 MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PHONGO_FUNCTIONS_H +#define PHONGO_FUNCTIONS_H + +#include + +PHP_FUNCTION(MongoDB_Driver_reset); + +#endif /* PHONGO_FUNCTIONS_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/tests/manager/manager-reset-001.phpt b/tests/manager/manager-reset-001.phpt new file mode 100644 index 000000000..b8e198d32 --- /dev/null +++ b/tests/manager/manager-reset-001.phpt @@ -0,0 +1,78 @@ +--TEST-- +MongoDB\Driver\reset: Cursor destruct should not kill cursor from parent process +--SKIPIF-- + + + + + +--FILE-- +getCommand(); + + if ($event->getCommandName() === 'find') { + printf("find command specifies batchSize: %d\n", $command->batchSize); + } + + if ($event->getCommandName() === 'getMore') { + printf("getMore command specifies batchSize: %d\n", $command->batchSize); + } + } + + public function commandSucceeded(MongoDB\Driver\Monitoring\CommandSucceededEvent $event) + { + } + + public function commandFailed(MongoDB\Driver\Monitoring\CommandFailedEvent $event) + { + } +} + +MongoDB\Driver\Monitoring\addSubscriber(new CommandLogger); + +$manager = new MongoDB\Driver\Manager(URI); + +$bulk = new MongoDB\Driver\BulkWrite(); +$bulk->insert(['x' => 1]); +$bulk->insert(['x' => 2]); +$bulk->insert(['x' => 3]); +$manager->executeBulkWrite(NS, $bulk); + +$query = new MongoDB\Driver\Query([], ['batchSize' => 2]); +$cursor = $manager->executeQuery(NS, $query); + +$parentPid = getmypid(); +$childPid = pcntl_fork(); + +if ($childPid === 0) { + MongoDB\Driver\reset($manager); + echo "Child exits\n"; + exit; +} + +if ($childPid > 0) { + $waitPid = pcntl_waitpid($childPid, $status); + + if ($waitPid === $childPid) { + echo "Parent waited for child to exit\n"; + } + + printf("Parent fully iterated cursor for %d documents\n", iterator_count($cursor)); +} + +?> +===DONE=== + +--EXPECT-- +find command specifies batchSize: 2 +Child exits +Parent waited for child to exit +getMore command specifies batchSize: 2 +Parent fully iterated cursor for 3 documents +===DONE=== diff --git a/tests/manager/manager-reset-002.phpt b/tests/manager/manager-reset-002.phpt new file mode 100644 index 000000000..73a00e384 --- /dev/null +++ b/tests/manager/manager-reset-002.phpt @@ -0,0 +1,83 @@ +--TEST-- +MongoDB\Driver\reset: Child process cannot iterate cursor from parent process +--SKIPIF-- + + + + + +--FILE-- +getCommand(); + + if ($event->getCommandName() === 'find') { + printf("find command specifies batchSize: %d\n", $command->batchSize); + } + + if ($event->getCommandName() === 'getMore') { + printf("getMore command specifies batchSize: %d\n", $command->batchSize); + } + } + + public function commandSucceeded(MongoDB\Driver\Monitoring\CommandSucceededEvent $event) + { + } + + public function commandFailed(MongoDB\Driver\Monitoring\CommandFailedEvent $event) + { + } +} + +MongoDB\Driver\Monitoring\addSubscriber(new CommandLogger); + +$manager = new MongoDB\Driver\Manager(URI); + +$bulk = new MongoDB\Driver\BulkWrite(); +$bulk->insert(['x' => 1]); +$bulk->insert(['x' => 2]); +$bulk->insert(['x' => 3]); +$manager->executeBulkWrite(NS, $bulk); + +$query = new MongoDB\Driver\Query([], ['batchSize' => 2]); +$cursor = $manager->executeQuery(NS, $query); + +$parentPid = getmypid(); +$childPid = pcntl_fork(); + +if ($childPid === 0) { + MongoDB\Driver\reset($manager); + echo throws(function() use ($cursor) { + printf("Child fully iterated cursor for %d documents\n", iterator_count($cursor)); + }, 'MongoDB\Driver\Exception\RuntimeException'), "\n"; + echo "Child exits\n"; + exit; +} + +if ($childPid > 0) { + $waitPid = pcntl_waitpid($childPid, $status); + + if ($waitPid === $childPid) { + echo "Parent waited for child to exit\n"; + } + + printf("Parent fully iterated cursor for %d documents\n", iterator_count($cursor)); +} + +?> +===DONE=== + +--EXPECT-- +find command specifies batchSize: 2 +OK: Got MongoDB\Driver\Exception\RuntimeException +Cannot advance cursor after client reset +Child exits +Parent waited for child to exit +getMore command specifies batchSize: 2 +Parent fully iterated cursor for 3 documents +===DONE=== diff --git a/tests/manager/manager-reset-003.phpt b/tests/manager/manager-reset-003.phpt new file mode 100644 index 000000000..ce8da3ebd --- /dev/null +++ b/tests/manager/manager-reset-003.phpt @@ -0,0 +1,82 @@ +--TEST-- +MongoDB\Driver\reset: Resetting a manager causes socket errors on SSL connections +--SKIPIF-- + + + + + +--FILE-- +getCommand(); + + if ($event->getCommandName() === 'find') { + printf("find command specifies batchSize: %d\n", $command->batchSize); + } + + if ($event->getCommandName() === 'getMore') { + printf("getMore command specifies batchSize: %d\n", $command->batchSize); + } + } + + public function commandSucceeded(MongoDB\Driver\Monitoring\CommandSucceededEvent $event) + { + } + + public function commandFailed(MongoDB\Driver\Monitoring\CommandFailedEvent $event) + { + } +} + +MongoDB\Driver\Monitoring\addSubscriber(new CommandLogger); + +$manager = new MongoDB\Driver\Manager(URI); + +$bulk = new MongoDB\Driver\BulkWrite(); +$bulk->insert(['x' => 1]); +$bulk->insert(['x' => 2]); +$bulk->insert(['x' => 3]); +$manager->executeBulkWrite(NS, $bulk); + +$query = new MongoDB\Driver\Query([], ['batchSize' => 2]); +$cursor = $manager->executeQuery(NS, $query); + +$parentPid = getmypid(); +$childPid = pcntl_fork(); + +if ($childPid === 0) { + MongoDB\Driver\reset($manager); + + echo "Child exits\n"; + exit; +} + +if ($childPid > 0) { + $waitPid = pcntl_waitpid($childPid, $status); + + if ($waitPid === $childPid) { + echo "Parent waited for child to exit\n"; + } + + echo throws(function () use ($cursor) { + iterator_count($cursor); + }, MongoDB\Driver\Exception\ConnectionTimeoutException::class), "\n"; +} + +?> +===DONE=== + +--EXPECTF-- +find command specifies batchSize: 2 +Child exits +Parent waited for child to exit +getMore command specifies batchSize: 2 +OK: Got MongoDB\Driver\Exception\ConnectionTimeoutException +Failed to send "getMore" command with database "%s": Failed to read 4 bytes: socket error or timeout +===DONE=== diff --git a/tests/session/bug1274-001.phpt b/tests/session/bug1274-001.phpt new file mode 100644 index 000000000..54ffbd6e1 --- /dev/null +++ b/tests/session/bug1274-001.phpt @@ -0,0 +1,70 @@ +--TEST-- +PHPC-1274: Session destruct should not kill session from parent process +--SKIPIF-- + + + + + + + +--FILE-- +executeCommand( + DATABASE_NAME, + new MongoDB\Driver\Command(['create' => COLLECTION_NAME]), + ['writeConcern' => new MongoDB\Driver\WriteConcern('majority')] +); + +$session = $manager->startSession(); +$session->startTransaction(['writeConcern' => new MongoDB\Driver\WriteConcern('majority')]); + +$bulk = new MongoDB\Driver\BulkWrite(); +$bulk->insert(['x' => 1]); +$bulk->insert(['x' => 2]); +$result = $manager->executeBulkWrite(NS, $bulk, ['session' => $session]); +printf("Parent inserted %d documents\n", $result->getInsertedCount()); + +$parentPid = getmypid(); +$childPid = pcntl_fork(); + +if ($childPid === 0) { + MongoDB\Driver\reset($manager); + echo "Child exits\n"; + exit; +} + +if ($childPid > 0) { + $waitPid = pcntl_waitpid($childPid, $status); + + if ($waitPid === $childPid) { + echo "Parent waited for child to exit\n"; + } + + $bulk = new MongoDB\Driver\BulkWrite(); + $bulk->insert(['x' => 3]); + $bulk->insert(['x' => 4]); + $result = $manager->executeBulkWrite(NS, $bulk, ['session' => $session]); + printf("Parent inserted %d documents\n", $result->getInsertedCount()); + + $session->commitTransaction(); + + $cursor = $manager->executeQuery(NS, new MongoDB\Driver\Query([])); + printf("Parent fully iterated cursor for %d documents\n", iterator_count($cursor)); +} + +?> +===DONE=== + +--EXPECT-- +Parent inserted 2 documents +Child exits +Parent waited for child to exit +Parent inserted 2 documents +Parent fully iterated cursor for 4 documents +===DONE===