diff --git a/app/config/errors.php b/app/config/errors.php index 9e742d3296..cb3ffb53a7 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -519,6 +519,11 @@ return [ 'description' => 'Entrypoint for your Appwrite Function is missing. Please specify it when making deployment or update the entrypoint under your function\'s "Settings" > "Configuration" > "Entrypoint".', 'code' => 404, ], + Exception::FUNCTION_SYNCHRONOUS_TIMEOUT => [ + 'name' => Exception::FUNCTION_SYNCHRONOUS_TIMEOUT, + 'description' => 'Synchronous function execution timed out. Use asynchronous execution instead, or ensure the execution duration doesn\'t exceed 30 seconds.', + 'code' => 408, + ], /** Builds */ Exception::BUILD_NOT_FOUND => [ diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index ed5af1d38a..a88d3815e9 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -9,6 +9,7 @@ use Appwrite\Event\Func; use Appwrite\Event\Usage; use Appwrite\Event\Validator\FunctionEvent; use Appwrite\Extend\Exception; +use Appwrite\Extend\Exception as AppwriteException; use Appwrite\Messaging\Adapter\Realtime; use Appwrite\Task\Validator\Cron; use Appwrite\Utopia\Database\Validator\CustomId; @@ -1750,6 +1751,10 @@ App::post('/v1/functions/:functionId/executions') ->setAttribute('responseStatusCode', 500) ->setAttribute('errors', $th->getMessage() . '\nError Code: ' . $th->getCode()); Console::error($th->getMessage()); + + if ($th instanceof AppwriteException) { + throw $th; + } } finally { $queueForUsage ->addMetric(METRIC_EXECUTIONS, 1) @@ -1757,11 +1762,11 @@ App::post('/v1/functions/:functionId/executions') ->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000)) // per project ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000)) // per function ; - } - if ($function->getAttribute('logging')) { - /** @var Document $execution */ - $execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution)); + if ($function->getAttribute('logging')) { + /** @var Document $execution */ + $execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution)); + } } $roles = Authorization::getRoles(); diff --git a/app/controllers/general.php b/app/controllers/general.php index 1fa70ed703..2c8a4f68aa 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -289,6 +289,7 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo $execution->setAttribute('logs', $executionResponse['logs']); $execution->setAttribute('errors', $executionResponse['errors']); $execution->setAttribute('duration', $executionResponse['duration']); + } catch (\Throwable $th) { $durationEnd = \microtime(true); @@ -298,6 +299,10 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo ->setAttribute('responseStatusCode', 500) ->setAttribute('errors', $th->getMessage() . '\nError Code: ' . $th->getCode()); Console::error($th->getMessage()); + + if ($th instanceof AppwriteException) { + throw $th; + } } finally { $queueForUsage ->addMetric(METRIC_EXECUTIONS, 1) @@ -305,11 +310,11 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo ->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000)) // per project ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000)) // per function ; - } - if ($function->getAttribute('logging')) { - /** @var Document $execution */ - $execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution)); + if ($function->getAttribute('logging')) { + /** @var Document $execution */ + $execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution)); + } } $execution->setAttribute('logs', ''); diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index 711b46733f..9c0cc31bbf 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -731,7 +731,7 @@ services: <<: *x-logging restart: unless-stopped stop_signal: SIGINT - image: openruntimes/executor:0.4.12 + image: openruntimes/executor:0.5.5 networks: - appwrite - runtimes diff --git a/docker-compose.yml b/docker-compose.yml index b3b197381b..1a38893f3a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -794,7 +794,7 @@ services: hostname: exc1 <<: *x-logging stop_signal: SIGINT - image: openruntimes/executor:0.5.1 + image: openruntimes/executor:0.5.5 restart: unless-stopped networks: - appwrite diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 48409f06b1..d48787bb98 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -153,6 +153,7 @@ class Exception extends \Exception public const FUNCTION_NOT_FOUND = 'function_not_found'; public const FUNCTION_RUNTIME_UNSUPPORTED = 'function_runtime_unsupported'; public const FUNCTION_ENTRYPOINT_MISSING = 'function_entrypoint_missing'; + public const FUNCTION_SYNCHRONOUS_TIMEOUT = 'function_synchronous_timeout'; /** Deployments */ public const DEPLOYMENT_NOT_FOUND = 'deployment_not_found'; diff --git a/src/Executor/Executor.php b/src/Executor/Executor.php index 76c66de231..e9b0ae016e 100644 --- a/src/Executor/Executor.php +++ b/src/Executor/Executor.php @@ -2,6 +2,7 @@ namespace Executor; +use Appwrite\Extend\Exception as AppwriteException; use Exception; use Utopia\System\System; @@ -193,7 +194,6 @@ class Executor 'path' => $path, 'method' => $method, 'headers' => $headers, - 'image' => $image, 'source' => $source, 'entrypoint' => $entrypoint, @@ -311,6 +311,8 @@ class Executor $responseType = $responseHeaders['content-type'] ?? ''; $responseStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $curlError = curl_errno($ch); + $curlErrorMessage = curl_error($ch); if ($decode) { switch (substr($responseType, 0, strpos($responseType, ';'))) { @@ -327,8 +329,11 @@ class Executor } } - if ((curl_errno($ch)/* || 200 != $responseStatus*/)) { - throw new Exception(curl_error($ch) . ' with status code ' . $responseStatus, $responseStatus); + if ($curlError) { + if ($curlError == CURLE_OPERATION_TIMEDOUT) { + throw new AppwriteException(AppwriteException::FUNCTION_SYNCHRONOUS_TIMEOUT); + } + throw new Exception($curlErrorMessage . ' with status code ' . $responseStatus, $responseStatus); } curl_close($ch);