From b6c62fafa0c8c4a129ad3f8e42b8b4d98761537d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=BDborn=C3=BD=20Adam?= Date: Fri, 20 Jan 2023 15:38:57 +0100 Subject: [PATCH 1/4] Make QueryExceededMemoryLimitNoDiskUseAllowed as user exception --- src/Export.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Export.php b/src/Export.php index 9e8c976..60e2898 100644 --- a/src/Export.php +++ b/src/Export.php @@ -107,6 +107,12 @@ protected function handleMongoExportFails(Throwable $e): void )); } + if (str_contains($e->getMessage(), 'QueryExceededMemoryLimitNoDiskUseAllowed')) { + throw new UserException('Sort exceeded memory limit, but did not opt in to ' . + 'external sorting. The field should be set as an index, so there will be no sorting in the ' . + 'incremental fetching query, because the index will be used'); + } + if (preg_match('/query \'\\[[^\\]]*\\]\' is not valid JSON/i', $e->getMessage())) { throw new UserException(sprintf( 'Export "%s" failed. Query "' . $this->exportOptions->getQuery() . '" is not valid JSON', From ee4e54f1f70d6917c33513890cbb9e4f7060b0ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=BDborn=C3=BD=20Adam?= Date: Fri, 20 Jan 2023 15:54:06 +0100 Subject: [PATCH 2/4] Fix PHP version in Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index fedd391..54f7533 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM php:8-cli +FROM php:8.1-cli ARG COMPOSER_FLAGS="--prefer-dist --no-interaction" ARG DEBIAN_FRONTEND=noninteractive From 0d3ff103233d2938437df742e25158da81936054 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=BDborn=C3=BD=20Adam?= Date: Mon, 23 Jan 2023 21:41:31 +0100 Subject: [PATCH 3/4] Handle also "dial tcp: i/o timeout" as UserException --- src/Export.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Export.php b/src/Export.php index 60e2898..089c5c9 100644 --- a/src/Export.php +++ b/src/Export.php @@ -113,6 +113,11 @@ protected function handleMongoExportFails(Throwable $e): void 'incremental fetching query, because the index will be used'); } + if (str_contains($e->getMessage(), 'dial tcp: i/o timeout')) { + throw new UserException('Could not connect to server: connection() error occurred during ' . + 'connection handshake: dial tcp: i/o timeout'); + } + if (preg_match('/query \'\\[[^\\]]*\\]\' is not valid JSON/i', $e->getMessage())) { throw new UserException(sprintf( 'Export "%s" failed. Query "' . $this->exportOptions->getQuery() . '" is not valid JSON', From 0678d4ef851d683b4e0b89add719a7478eb7a916 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=BDborn=C3=BD=20Adam?= Date: Mon, 23 Jan 2023 21:41:53 +0100 Subject: [PATCH 4/4] Test for handling MongoExport exceptions --- tests/phpunit/HandleMongoExportFailsTest.php | 75 ++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 tests/phpunit/HandleMongoExportFailsTest.php diff --git a/tests/phpunit/HandleMongoExportFailsTest.php b/tests/phpunit/HandleMongoExportFailsTest.php new file mode 100644 index 0000000..4ea4e30 --- /dev/null +++ b/tests/phpunit/HandleMongoExportFailsTest.php @@ -0,0 +1,75 @@ +expectException(get_class($expectedException)); + $this->expectExceptionMessage($expectedException->getMessage()); + + $class = new ReflectionClass(Export::class); + $method = $class->getMethod('handleMongoExportFails'); + $exportOptions = new ExportOptions(['name' => '', 'mode' => '']); + $exportClass = new Export(new ExportCommandFactory(new UriFactory(), false), [], $exportOptions); + $method->invoke($exportClass, $mongoException); + } + + public function exceptionsProvider(): Generator + { + yield 'dial tcp: i/o timeout' => [ + new ProcessFailedException($this->createMockInstanceOfProcess('2023-01-23T17:02:32.685+0000\t' . + 'could not connect to server: connection() error occured during connection handshake: dial tcp: i/o ' . + 'timeout')), + new UserException('Could not connect to server: connection() error occurred during ' . + 'connection handshake: dial tcp: i/o timeout'), + ]; + + yield 'QueryExceededMemoryLimitNoDiskUseAllowed' => [ + new ProcessFailedException($this->createMockInstanceOfProcess('2023-01-23T17:02:32.685+0000\t' . + '(QueryExceededMemoryLimitNoDiskUseAllowed) Sort exceeded memory limit of 104857600 bytes, but did ' . + 'not opt in to external sorting.')), + new UserException('Sort exceeded memory limit, but did not opt in to ' . + 'external sorting. The field should be set as an index, so there will be no sorting in the ' . + 'incremental fetching query, because the index will be used'), + ]; + } + + private function createMockInstanceOfProcess(string $errorOutput): Process + { + $mockProcess = $this->createMock(Process::class); + $mockProcess->method('isSuccessful')->willReturn(false); + $mockProcess->method('getCommandLine')->willReturn('mongoexport --uri ' . + '\'mongodb://user:pass@mongo/mongodb\' --collection \'transactions\' ' . + '--query \'{\"_id\":{\"$gte\":{\"$oid\": \"63ceb66a967f8f0017ceed64\"}}}\' --sort \'{\"_id\":1}\' ' . + '--type \'json\''); + $mockProcess->method('getExitCode')->willReturn(1); + $mockProcess->method('getExitCodeText')->willReturn('General error'); + $mockProcess->method('getWorkingDirectory')->willReturn('/code'); + $mockProcess->method('getOutput')->willReturn(''); + $mockProcess->method('getErrorOutput')->willReturn($errorOutput); + + return $mockProcess; + } +}