— Artikel — № 011

011 —Magento

Magento 1 in 2026: live bewerken zonder cache te slopen

Een Nederlands bureau, een vastgelopen Magento 1-shop, vrijdag 23:41, en de catalog_product_flat-tabellen die bijna de storefront sloopten.

Houten letterkast open op donkere eiken werkbank, koperen handvat, loden letters in vakjes, rood verzegeld kaartje.
Hero · gestileerd stilleven№ 011

Het was 23:41 op een vrijdag toen de Loom in onze inbox belandde. Een Nederlands bureau waar we mee werken, vier developers, doet onderhoud op een Magento 1.9.4.5-catalogus voor een B2B-reseller die ongeveer €11k per dag aan orders draait. De lead had zichzelf opgenomen terwijl hij naar een terminal staarde: php -f shell/indexer.php reindexall draaide al negentien minuten, de storefront serveerde verouderde prijzen op ongeveer één op de zes productpagina's, en de inkoper van de klant in Hamburg had net een bestelling van 240 stuks tegen de oude prijs geplaatst.

Magento 1 bereikte end-of-life in juni 2020. We zitten inmiddels in mei 2026. Een verrassend aantal shops draait er nog op, meestal omdat de offerte voor een replatform binnenkwam op €60k en de eigenaar de rekensom maakte tegenover nog twee jaar patchen. Als je er een onderhoudt, ken je de regels al: je raakt de catalogus niet aan tijdens kantooruren, je vertrouwt de cache flush-knop in de admin niet, en je past nooit, maar dan ook nooit, een product attribute set aan op een live shop zonder plan.

Dit artikel is dat plan. Het is de volgorde die we die avond met het bureau hebben uitgewerkt, verfijnd over de zes weken erna met vergelijkbare incidenten, en die we inmiddels op de automatische piloot draaien als we de catalogus van een legacy site moeten bewerken zonder de storefront plat te leggen.

Wat er daadwerkelijk stuk gaat

Mensen praten over 'de cache' alsof Magento 1 er één heeft. Dat is niet zo. Een productie-installatie met de flat catalog aan heeft op elk moment minstens zes caches in de lucht, en ze worden op verschillende signalen geïnvalideerd.

  • var/cache/, de file backend, tenzij je die naar Redis of APC hebt verhuisd.
  • var/full_page_cache/, Enterprise FPC, of de Lesti_Fpc-fork als je op Community draait.
  • catalog_product_flat_1, catalog_product_flat_2, enzovoort: één tabel per store view, opnieuw opgebouwd door de flat catalog-indexer.
  • catalog_category_flat_store_1, idem voor categorieën.
  • De block HTML-cache, die gerenderde productlist-fragmenten per customer group bevat.
  • Varnish, als je het geluk hebt dat je het draait; de Turpentine VCL als je de pech hebt om Turpentine te gebruiken.

De failure mode die de Hamburg-bestelling onderuit haalde, was de derde. Het bureau had een rij met tier-prices direct in catalog_product_entity_tier_price aangepast via phpMyAdmin om een typo te fixen. Dat ging om de event observers van Magento heen, waardoor de flat tables nooit werden gemarkeerd voor reindex, waardoor de storefront nog drieënveertig minuten lang de oude tier-prijs bleef serveren tot de volgende geplande cron.sh-run een ongerelateerde wijziging oppikte en een volledige reindex triggerde.

Directe SQL op een Magento 1-catalogus-tabel is veruit de meest gangbare manier om een live shop kapot te maken. Het schema is genormaliseerd over EAV, de flat tables zijn gedenormaliseerde kopieën, en de indexer is het enige onderdeel dat weet hoe het beide met elkaar moet rijmen.

De pre-flight checklist

Voor elke catalogus-wijziging op een live shop draaien we nu vijf checks. Ze kosten ongeveer negentig seconden en hebben ons ongeveer één keer per maand voor een incident behoed.

Eén: controleer welke indexers op manual mode staan en welke scheduled zijn. Op een gezonde installatie horen de meeste op 'Update on Save' te staan, behalve de twee flat indexers, die op 'Manual' horen als je catalogus groter is dan zo'n 8.000 SKU's.

php -f shell/indexer.php info
php -f shell/indexer.php mode

Twee: kijk naar de grootte van de reindex-queue. Heeft index_process al een rij met status='working', loop dan weg en kom over tien minuten terug. Twee reindexers tegelijk laten draaien is precies hoe je eindigt met een half gevulde catalog_product_flat_1.

SELECT process_id, indexer_code, status, started_at
FROM index_process
WHERE status IN ('working','require_reindex')
ORDER BY started_at DESC;

Drie: zoek in de core_cache-tabel naar tags met CATALOG_PRODUCT of CATALOG_CATEGORY die ouder zijn dan de laatste deploy. Verouderde entries hier zijn de reden dat de 'Flush Cache Storage'-knop in de admin vaak niets nuttigs doet.

Vier: bevestig dat cron echt draait. Op zo'n derde van de legacy Magento 1-shops die we auditen, ligt cron.sh al maanden stilletjes dood omdat iemand de document root heeft verplaatst en het crontab-pad nooit is bijgewerkt. tail -n 50 var/log/cron.log en bekijk de timestamps.

Vijf: maak een snapshot. Geen volledige database dump (die duren twintig minuten op een echte catalogus), alleen de twee of drie tabellen die je gaat aanraken. mysqldump --single-transaction met de tabellijst, gzipped, naast de deploy geparkeerd.

Het edit-window

Zodra de checklist schoon is, volgt de bewerking zelf een vaste volgorde. Die volgorde maakt uit, want het event-systeem is fragiel en elke afwijking laat doorgaans een van de caches achter in een staat die noch geldig noch expliciet geïnvalideerd is.

  1. Zet de twee flat indexers op 'Manual' als dat nog niet zo is.
  2. Voer de wijziging door via de admin-UI, niet via SQL. Kan de admin-UI de wijziging niet aan (bijvoorbeeld een bulk-attribuutwijziging over 4.000 SKU's), schrijf dan een eenmalig PHP-script dat Mage bootstrapt en Mage::getModel('catalog/product')->load($id)->setData(...)->save() gebruikt. Het save-event triggert de observers; ruwe SQL niet.
  3. Reindex alleen de betreffende indexers, één voor één, in dependency-volgorde: catalog_product_attribute, dan catalog_product_price, dan catalog_product_flat, dan catalog_category_flat, dan catalog_category_product.
  4. Flush de block HTML-cache en daarna de full page cache, in die volgorde. Niet andersom.
  5. Doe drie representatieve product-URL's met curl -I en controleer dat de X-Magento-Cache-Debug-header MISS retourneert bij de eerste hit en HIT bij de tweede.

Die laatste stap is de stap die de meeste engineers overslaan en juist de stap die de stille faalmomenten oppikt. Geeft een productpagina nog steeds HIT op het eerste request na een flush, dan zit er ergens boven Magento (Varnish, CloudFront, een CDN edge) een verouderde kopie te serveren en lijkt het alsof je wijziging niets gedaan heeft.

Het PHP 8.2-probleem dat boven dit alles hangt

Magento 1 werd nooit officieel ondersteund op PHP 7.2, laat staan iets nieuwers. De community-patches van OpenMage LTS houden het draaiende op PHP 7.4 en 8.0, en de laatste OpenMage 20.x-branch claimt PHP 8.2-compatibiliteit. In de praktijk komen we drie categorieën breakage tegen bij de 8.2-upgrade.

Dynamische property creation geeft nu een deprecation warning, en de Varien_Object-basisklasse gebruikt overal dynamic properties. De OpenMage-patches voegen #[\AllowDynamicProperties] toe waar het nodig is, maar third-party modules uit 2014 doen dat niet, en de warnings overspoelen var/log/system.log tot de schijf vol staat.

each() is weg. create_function() is weg. mb_strrpos() met negatieve offsets gedraagt zich anders. Heb je een module die sinds 2018 niet is aangeraakt, grep er dan op vóór je PHP upgradet, niet erna.

Wat we daadwerkelijk bij de klant afleveren

De oplevering aan het einde van een incident als die vrijdagavond is nooit 'we hebben het opgelost'. Het is een runbook van één pagina dat de developers van het bureau om 23:41 op de volgende vrijdag kunnen volgen, als de senior engineer in een andere tijdzone ligt te slapen. Het runbook bevat de vijf pre-flight checks, het edit-window in vijf stappen, en een lijst met de specifieke cache-tags en indexer codes die voor de catalogus van die klant relevant zijn.

We laten ook een versie-historie achter van elk bestand en elke databaserij die we hebben aangeraakt. Op een Magento 1-shop, waar één foute attribuut-wijziging een flat table kan corrumperen die er dertig minuten over doet om opnieuw op te bouwen, is het verschil tussen een rollback van tien minuten en een incident van vier uur of je kunt aanwijzen welke wijziging het probleem veroorzaakt heeft.

Toen we Pier bouwden, liepen we met dit bureau precies hiertegenaan: elke catalogus-wijziging moest auditeerbaar zijn, terug te draaien, en gekoppeld aan de developer die hem doorvoerde, zonder een deployment-pipeline op een codebase uit 2014 te schroeven. Hoe we het uiteindelijk hebben opgelost is een version history per bestand aan de FTP-kant en snapshot-diffs in de MySQL editor, zodat de rollback één klik is, of de wijziging nu in app/code/local/ zat of in core_config_data.

Onderhoud je een Magento 1-shop, dan is het kleinste nuttige dat je vandaag kunt doen php -f shell/indexer.php info erop draaien en noteren welke indexers in manual mode staan. Plak het op de monitor. De volgende keer dat iemand de catalogus bewerkt, is dat briefje wat voorkomt dat de cache de storefront opvreet.

— Vragen —

Is Magento 1 veilig om in 2026 te draaien?

Officieel niet. OpenMage LTS houdt het gepatched en het PHP 8.2-pad werkt, maar je draait niet-ondersteunde software en je PCI-scope groeit elk kwartaal. Behandel het als brug, niet als bestemming.

Mag ik de Magento 1-database direct met SQL bewerken?

Alleen op tabellen die Magento niet indexeert. Aan catalog_product_entity, tier-prices of stock items komen via ruwe SQL omzeilt de observers en laat de flat tables uit sync tot de volgende volledige reindex.

Waarom werkt de Flush Cache-knop in de admin vaak niet?

Hij wist getagde entries in core_cache, maar raakt de flat catalog-tabellen, de block HTML-cache of bovenliggende Varnish/CDN-lagen niet aan. Je moet elke laag expliciet flushen, in volgorde.