Paginated Pool fails to send the pages correctly
Using Pool with PagePaginated requests will send the same page for all concurrent requests, since $request is shallow cloned and $query is an ArrayStore object, it will be the same $query object for all concurrent requests if it was initialized before calling ->paginate()
this causes all requests sent together to have the same $query as well as other object properties
$connector = Connector::make();
$request = Request::make();
$request->query()->add('key', 'value'); // this will initialize the $query ArrayStore
$paginator = $connector->paginate($request); // this will clone $request internally
$actualPages = [];
$pool = $paginator
->setMaxPages(10)
->pool(
concurrency: 5,
responseHandler: function (Response $response) use (&$actualPages) {
$actualPages[] = $response->json('meta.current_page');
});
$promise = $pool->send();
$promise->wait();
$actualPages;
// expected: [1, 2, 3, 5, 6, 4, 7, 8, 9, 10];
// actual: [1, 6, 6, 6, 6, 6, 8, 7, 10, 10];
to avoid that we overrode the function applyPagination and changed it to:
protected function applyPagination(Request $request): Request
{
if ($request->query()->get('page') === null) {
(function () {
$currentQuery = $this->query()->all();
unset($this->query);
$this->query()->merge($currentQuery);
})->call($request);
}
return parent::applyPagination($request);
}
$query, $headers, $config, and $delay are all objects, if any of them is initialized before cloning they will be shared between all requests, but i think only pagination needs the deep clone for query
another solution is to add this to the Request class
public function __clone(): void
{
if (isset($this->query)) {
$this->query = clone $this->query;
}
if (isset($this->headers)) {
$this->headers = clone $this->headers;
}
if (isset($this->config)) {
$this->config = clone $this->config;
}
if (isset($this->middlewarePipeline)) {
$this->middlewarePipeline = clone $this->middlewarePipeline;
}
if (isset($this->delay)) {
$this->delay = clone $this->delay;
}
}