From f3c9345b3464ced4c286d33dc2ae1bad43b4456a Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 12 Oct 2026 16:57:11 +1300 Subject: [PATCH] fix: time out stalled source and destination requests 2.0.9 (#232) stopped migrations hanging forever on a remote that accepts the connection and never answers, but it was released from the 2.x line after 3.0.0 was cut, so 3.x still lacks it. Consumers moving from ^2.0.9 to ^3 (appwrite) would silently regress to workers stuck on the job with the migration left in processing. Port it unchanged: connections give up after 10s, Target::call abandons a response that sends nothing for 30s, and the Appwrite SDK clients cap each request at 30s, so the stall surfaces as a failed migration. (cherry picked from commit 6900ead4da28d393b3bd88e67516b12e1a15342d) (cherry picked from commit db89ecee9f265c6181dd1d2746482c37ab4d4248) Co-Authored-By: Claude Opus 5.5 --- src/Migration/Destinations/Appwrite.php | 4 +++- src/Migration/Sources/Appwrite.php | 4 +++- src/Migration/Target.php | 20 ++++++++++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/Migration/Destinations/Appwrite.php b/src/Migration/Destinations/Appwrite.php index 5af540ff..01035649 100644 --- a/src/Migration/Destinations/Appwrite.php +++ b/src/Migration/Destinations/Appwrite.php @@ -283,7 +283,9 @@ public function __construct( $this->client = (new Client()) ->setEndpoint($endpoint) ->setProject($project) - ->setKey($key); + ->setKey($key) + ->setConnectTimeout(static::CONNECT_TIMEOUT) + ->setTimeout(static::REQUEST_TIMEOUT); $this->functions = new Functions($this->client); $this->messaging = new Messaging($this->client); diff --git a/src/Migration/Sources/Appwrite.php b/src/Migration/Sources/Appwrite.php index d64d39d4..4b10c1ba 100644 --- a/src/Migration/Sources/Appwrite.php +++ b/src/Migration/Sources/Appwrite.php @@ -143,7 +143,9 @@ public function __construct( $this->client = (new Client()) ->setEndpoint($endpoint) ->setProject($projectId) - ->setKey($key); + ->setKey($key) + ->setConnectTimeout(static::CONNECT_TIMEOUT) + ->setTimeout(static::REQUEST_TIMEOUT); $this->users = new Users($this->client); $this->teams = new Teams($this->client); diff --git a/src/Migration/Target.php b/src/Migration/Target.php index b855d3ce..52d89181 100644 --- a/src/Migration/Target.php +++ b/src/Migration/Target.php @@ -4,6 +4,23 @@ abstract class Target { + /** + * Seconds to wait for a remote to accept the connection. + */ + public const int CONNECT_TIMEOUT = 10; + + /** + * Seconds a request may go without receiving a byte before it is abandoned. + * Transfers of any length stay alive as long as data keeps arriving. + */ + public const int STALL_TIMEOUT = 30; + + /** + * Seconds an Appwrite SDK request may take in total. The SDK transport only + * takes a total timeout; its requests are single API pages and file chunks. + */ + public const int REQUEST_TIMEOUT = 30; + /** * Global Headers * @@ -118,6 +135,9 @@ protected function call( \curl_setopt($ch, CURLOPT_USERAGENT, php_uname('s').'-'.php_uname('r').':php-'.phpversion()); \curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); \curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + \curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, static::CONNECT_TIMEOUT); + \curl_setopt($ch, CURLOPT_LOW_SPEED_LIMIT, 1); + \curl_setopt($ch, CURLOPT_LOW_SPEED_TIME, static::STALL_TIMEOUT); \curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($curl, $header) use (&$responseHeaders) { $len = strlen($header); $header = explode(':', strtolower($header), 2);