Week 6

De helft van deze week betekende de helft van mijn stage, misschien geen goede overeenkomst met mijn vorderingen aangezien woendag een initiële versie van het prototype werd afgewerkt, het voorlopig laatste deel van mijn opdracht Take-A-Trip. Het woord “voorlopig” is hierbij noodzakelijk aangezien het zou kunnen (dit is nog niet zeker) dat ik het design ook nog zal implementeren. Hoewel het design het project een afgewerkter gevoel geeft, is dit misschien ook een beetje spijtig; bij een design veranderen er steeds dingen aan het prototype, datgene waar ik net zo veel tijd aan besteedde om goed te krijgen (en ik ben daarbij geen held in JavaScript of front-end development over het algemeen ;)).

Performantie

Performantie is een woord dat al enkele keren terugkwam, wat niet ongewoon is aangezien peformantie zeer belangrijk is voor Take-A-Trip. Ook deze week stonden polishing, bugfixes en performantie opnieuw in de kijker. Op dit moment groepeert de front-end reizen op basis van titel (de naam van het hotel of de reis). Hiervoor worden eerst alle reizen die voldoen aan de criteria opgehaald en worden ze nadien (in PHP) gegroepeerd per titel. Dit is jammer genoeg de enigste oplossing aangezien je wel een GROUP BY title kan doen, maar dan verlies je in de teruggegeven resultset van PHP de reizen onder die titel (bij het doorgeven naar PHP worden geen geneste arrays gemaakt, je krijgt dus één van de reizen die toevallig net die titel had en de andere reizen vallen uit de boot). Om dit proces te versnellen is de relatie niet meer “Feeds hebben producten en producten hebben properties” maar wel “Feeds hebben productaanbiedingen en productaanbiedingen hebben propertiesproductaanbiedingen hebben geen titel maar een foreign key naar product die gemeenschappelijke informatie groepeert”. Dit om bij het groeperen op titel, iets wat bij elke zoekopdracht gebeurt,  geen zoekopdracht op tekst uit te voeren maar wel op getallen.

Zoals de lezer al kan vermoeden is het groeperen in PHP bij veel resultaten traag, wat net de reden is dat er slechts een tiental reizen tegelijk worden opgehaald. Dit helpt al een stukje, maar om het nog vervelender te maken kwam hierbij een limitatie bij het gebruik van LIMIT (pun not intended) in SQL aan het licht: stel ik wil tien reizen ophalen (de titels en universele eigenschappen waaronder de aanbiedingen met dezelfde titel terechtkomen) en tonen aan de front-end, dan zou je iets als volgt kunnen krijgen als query:

SELECT * FROM productoffers po INNER JOIN products p WHERE meets_criterium_1 AND meets_criterium_2 AND … ORDER BY column LIMIT 10

Ik krijg nu een lijst van de tien reisaanbiedingen die voldoen aan de criteria, maar ik heb niet gegroepeerd op titel (m.a.w. sommige van deze reisaanbiedingen kunnen dezelfde “hoofdreis” of titel hebben). Geen probleem, dan groepeer je ze toch even in PHP? Het zijn toch maar tien resultaten. Bij deze redenering treedt een probleem op: als er bv. twee reisaanbiedingen onder dezelfde titel komen, heb je nog maar negen titels, wat duidelijk zichtbaar is aan de front-end. Bij het laden van meer aanbiedingen komen er weer tien titels bij… of misschien negen… of misschien zelfs maar één als de resultaten toevallig allemaal dezelfde titel hadden. Kan het Misschien op deze manier?

SELECT * FROM products p WHERE meets_criterium_1 AND meets_criterium_2 AND EXISTS (SELECT 1 FROM productoffers po WHERE po.product_id = p.id AND meets_criterium_3 AND meets_criterium_4 AND …) ORDER BY column LIMIT 10

Deze oplossing werkt, m.a.w. het aantal resultaten zal in dit geval steeds consistent tien zijn (tenzij er natuurlijk minder als tien resultaten in totaal zijn, maar dat is geen probleem). Hierbij zijn er echter twee problemen:

  1. Er is een subquery aanwezig die afhankelijk is van de hoofdquery en daarbij ook nog complex is, waardoor de SQL-server moeilijker aan caching kan doen. Dit is niet zo erg voor kleine tabellen, maar wel voor grote tabellen met tienduizenden records.
  2. We hebben nu alle hoofdreizen en titels maar we hebben de productaanbiedingen die erbij horen nog niet, wat eigenlijk gewoonweg stom is, want we de subquery heeft net al naar de juiste aanbiedingen gezocht. Na het teruggeven van deze records moeten we nog eens itereren om de bijhorende aanbiedingen op te halen met dezelfde filters.

Er is nog een derde oplossing:

SELECT * FROM productoffers po INNER JOIN products p WHERE meets_criterium_1 AND meets_criterium_2 AND … GROUP BY po.product_id ORDER BY column LIMIT 10

Deze versie heeft opnieuw het probleem dat we nog eens moeten itereren over de resultaten om de bijhorende aanbiedingen op te halen (we hebben ten slotte enkel de hoofdreizen en één aanbieding opgehaald), maar heeft geen last van een dure subquery. Het is nog steeds geen mooie oplossing, maar de performantie is vrij goed. Als deze query klaar is, itereren we over de tien teruggegeven resultaten en moeten we opnieuw filteren om de bijhorende aanbiedingen op te halen. Bij deze laatste query kunnen we echter nog de beperking opleggen dat enkel de product_id van de producten waarover we itereren gebruikt mag worden. Samen met een index op de foreign key maakt dit het opnieuw doorzoeken van de aanbiedingen vrij performant en volstaat de performantie (tot nu toe :)).

Als slotwoord bij dit onderdeel moet ik wel vermelden dat ik zelf geen SQL-expert ben. Als de lezer een veel betere of performantere manier ziet om dit probleem op te lossen, aarzel niet om een bericht achter te laten!

 Testfase

Na het werken aan de performantie werd Take-A-Trip voor de tweede keer door anderen getest (de eerste keer was zeer vroeg tijdens mijn stage en daardoor ook vrij kort). Tijdens deze testfase wordt het geheel (de beheermodule, de API en het prototype) uitgebreid getest. Er zijn al enkele aanpassingen nodig en kleine bugs tevoorschijn gekomen. Naast de testfase volgt er ook een code review, waarbij het technische aspect van de code geëvalueerd wordt. Volgende week staat dus volledig in teken van bugfixes, polishing en refactoring.

2 reacties

  • Wim Hendrikx

    Een heel uitgebreid verslag!

    In verband met je queries, probeer enkel de kolommen op te halen die je nodig hebt en werk zeker niet met * (maar dit is misschien enkel zo in je verslag om de query niet te lang te maken). Speel zeker ook nog wat met indexen op kolomgroepen en zet eens EXPLAIN of voor nog meer info EXPLAIN EXTENDED voor je query om meer interne info te krijgen zodat je de query mss nog wat verder kan tweaken. Soms kan het ook gebeuren dat niet de juiste index wordt gebruikt en dan kan je dit forceren door de juiste index in je query mee te geven.

Reactie plaatsen

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *

*