Beste DEVvers,
TL;DR: Is er een manier / library / composer-package om "eigen" klassen te kunnen gebruiken binnen closures in threads binnen PHP?
Ik heb een applicatie ontwikkeld in PHP 7.3 op basis van Symfony 4.4.
Deze applicatie kan gezien worden als een webbased Product Information Manager en bevat momenteel ruim 20 miljoen unieke producten. Deze producten hebben we opgeslagen in NoSQL (MongoDB) database.
Ieder product bevat, per leverancier, een collectie van attributen welke het product omschrijven. Denk hierbij aan gewicht, prijs, lengte, breedte, leverbaarheidsstatus etc..
Nu is de klantwens gekomen om statistieken over deze producten te draaien. Een van de wensen is het aantal leverbaarheidsstatussen per leverancier.
Om dit vraagstuk te beantwoorden, zijn we allereerst begonnen met het schrijven van een query in MongoDB. Dit bleek echter al snel, vanwege de enorme hoeveelheid data, onmogelijk te realiseren. We liepen tegen timeouts en/of out-of-memory exceptions aan.
Derhalve zijn we gedwongen om over de collectie van producten te intereren en van ieder BSONDocument weer een intern model te bouwen en daaruit de benodigde data te peuteren.
Deze data slaan we op in een tijdelijk array en schrijven we, nadat we alle producten hebben behandeld, weg in de statistieken database.
Dit proces werkt prima, maar heeft een heel lange doorlooptijd. We hebben getest met een subset van de data en als we dat getal extrapoleren komen we uit op ruim 9 uur. Gedurende deze tijd zit er 1 core continue op 100% te draaien.
Om een inzicht te krijgen waar de meeste tijd zit, heb ik de code door de profiler gehaald. Dit bleek, zoals verwacht, te zitten in het converteren van het BSONDocument naar het interne model. Dit betreft ruim 90% van de doorlooptijd.
Om het genereren van de statistieken (theoretisch) te versnellen, ben ik een poging gestart om dit proces parallel te gaan draaien. Om middels een pool van workers/threads meerdere cores aan het werk te zetten en de data aan het einde te combineren.
Om dit te bereiken heb ik de originele code welke het BSONDocument naar het interne model omzet in een Closure gegoten en deze Closure wordt middels een Future/Promise afgetrapt. Daarna wordt gecontroleerd of een taak is afgerond alvorens een nieuwe toe te voegen aan de threadpool.
In de vereenvoudigde test welke ik draaide, werkte dit allemaal vlekkeloos. Echter op het moment dat ik "eigen" types en/of klassen wil gebruiken binnen de Closure klapt de code er uit met een volgende Exception:
De closure ziet er zo uit:
En in de klasse welke de threadpool regelt, roep ik de closure zo aan
Runtime en Future komen uit PHP's parallel library.
Is er een manier / library / composer-package om toch "eigen" klassen te kunnen gebruiken binnen closures in threads binnen PHP?
Alvast bedankt
Matis
TL;DR: Is er een manier / library / composer-package om "eigen" klassen te kunnen gebruiken binnen closures in threads binnen PHP?
Ik heb een applicatie ontwikkeld in PHP 7.3 op basis van Symfony 4.4.
Deze applicatie kan gezien worden als een webbased Product Information Manager en bevat momenteel ruim 20 miljoen unieke producten. Deze producten hebben we opgeslagen in NoSQL (MongoDB) database.
Ieder product bevat, per leverancier, een collectie van attributen welke het product omschrijven. Denk hierbij aan gewicht, prijs, lengte, breedte, leverbaarheidsstatus etc..
Nu is de klantwens gekomen om statistieken over deze producten te draaien. Een van de wensen is het aantal leverbaarheidsstatussen per leverancier.
Om dit vraagstuk te beantwoorden, zijn we allereerst begonnen met het schrijven van een query in MongoDB. Dit bleek echter al snel, vanwege de enorme hoeveelheid data, onmogelijk te realiseren. We liepen tegen timeouts en/of out-of-memory exceptions aan.
Derhalve zijn we gedwongen om over de collectie van producten te intereren en van ieder BSONDocument weer een intern model te bouwen en daaruit de benodigde data te peuteren.
Deze data slaan we op in een tijdelijk array en schrijven we, nadat we alle producten hebben behandeld, weg in de statistieken database.
Dit proces werkt prima, maar heeft een heel lange doorlooptijd. We hebben getest met een subset van de data en als we dat getal extrapoleren komen we uit op ruim 9 uur. Gedurende deze tijd zit er 1 core continue op 100% te draaien.
Om een inzicht te krijgen waar de meeste tijd zit, heb ik de code door de profiler gehaald. Dit bleek, zoals verwacht, te zitten in het converteren van het BSONDocument naar het interne model. Dit betreft ruim 90% van de doorlooptijd.
Om het genereren van de statistieken (theoretisch) te versnellen, ben ik een poging gestart om dit proces parallel te gaan draaien. Om middels een pool van workers/threads meerdere cores aan het werk te zetten en de data aan het einde te combineren.
Om dit te bereiken heb ik de originele code welke het BSONDocument naar het interne model omzet in een Closure gegoten en deze Closure wordt middels een Future/Promise afgetrapt. Daarna wordt gecontroleerd of een taak is afgerond alvorens een nieuwe toe te voegen aan de threadpool.
In de vereenvoudigde test welke ik draaide, werkte dit allemaal vlekkeloos. Echter op het moment dat ik "eigen" types en/of klassen wil gebruiken binnen de Closure klapt de code er uit met een volgende Exception:
10:52:42 CRITICAL [commandLogger] illegal parameter (MongoDB\Model\BSONDocument) passed to task at argument 2
De closure ziet er zo uit:
PHP:
1
2
3
4
5
6
7
8
9
| /** * @param int $key * @param BSONDocument[] $documents */ $closure = function (int $key, array $documents) { $this->logger->debug(sprintf('Starting %d', $key)); /* ... */ $this->logger->debug(sprintf('Stopped %d', $key)); }; |
En in de klasse welke de threadpool regelt, roep ik de closure zo aan
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| /** * @param BSONDocument[] $documents * @return bool */ private function scheduleWork(array $documents): bool { foreach ($this->futures as $key => $future) { if ($future instanceof Future === true) { /* Already a taken slot, so no new work can be scheduled */ continue; } $runtime = new Runtime(); $future = $runtime->run($this->closure, [$key, $documents]); $this->futures[$key] = $future; return true; } return false; } |
Runtime en Future komen uit PHP's parallel library.
Is er een manier / library / composer-package om toch "eigen" klassen te kunnen gebruiken binnen closures in threads binnen PHP?
Alvast bedankt
Matis
If money talks then I'm a mime
If time is money then I'm out of time