Canvas-Page-Region-Versions-Drift-Heilung
drush cr auslöst.
Kontext
Drupal-Canvas (drupal/canvas) speichert pro Component-Instanz im component_tree-Feld einer Page (oder page_region-Config) den Hash der Component-Schema-Version zum Zeitpunkt des Speicherns. Beim Render-Versuch validiert Drupal\canvas\Entity\VersionedConfigEntityBase::assertVersionExists() diesen Hash gegen die heute im Component-Storage verfügbaren Versionen. Driftet der Hash (z. B. weil eine Drupal-Core-Aktualisierung den system_branding_block strukturell verändert und seine alte Version aus dem Storage entfernt), wirft Canvas eine OutOfRangeException und die gesamte Page rendert nicht mehr — auch nicht die unbeteiligten Header-/Footer-Bereiche, weil sie als page_region-Configs auf jeder Page eingebunden werden.
Im SOLCOM-Setup waren betroffen: 5 Hashes auf 2 page_region-Configs (byte_theme.header, byte_theme.footer) für die Components block.system_branding_block, block.system_menu_block.main, block.system_menu_block.footer und zwei SDC-Komponenten.
Entscheidung
Drift wird durch ein idempotentes Drush-PHP-Eval-Script geheilt, das alle page_region-Entities lädt, ihren component_tree durchläuft, jede component_version gegen die getActiveVersion() der zugehörigen Component-Entity vergleicht und bei Abweichung den aktiven Hash einsetzt. Die Page-Region wird neu gespeichert — Canvas validiert die neue Version und rendert wieder.
$cstor = \Drupal::entityTypeManager()->getStorage('component'); $active = []; foreach ($cstor->loadMultiple() as $id => $ent) { $active[$id] = $ent->getActiveVersion(); } $rstor = \Drupal::entityTypeManager()->getStorage('page_region'); foreach ($rstor->loadMultiple() as $rid => $region) { $tree = $region->get('component_tree'); $dirty = FALSE; $newTree = []; foreach ($tree as $key => $item) { if (isset($active[$item['component_id']]) && $active[$item['component_id']] !== $item['component_version']) { $item['component_version'] = $active[$item['component_id']]; $dirty = TRUE; } $newTree[$key] = $item; } if ($dirty) { $region->set('component_tree', $newTree); $region->save(); } }
drush cr ausführen. Das Script ist idempotent und kann bei erneutem Drift unverändert wieder laufen.Rejected alternatives
Manuelles Editieren der Configs in config/sync + config:import rejected
Scheitert daran, dass die ungültigen Hashes auch im exportierten YAML stünden — der Reimport würde sie unverändert wieder einspielen.
Direkter UPDATE config SET data = REPLACE(...)-SQL rejected
Würde funktionieren, ist aber spröde gegen Sub-Component-Trees in Slots, mehrfach vorkommende Hashes, und hinkt nicht auf Folge-Drifts.
Page-Regions im Canvas-Editor neu speichern (UI-Pfad) rejected
Setzt voraus, dass die Site überhaupt rendert — was hier durch den Drift unmöglich war (Henne-Ei-Problem).
Consequences
- Das Script ist idempotent. Sobald Canvas oder Drupal-Core einen weiteren Component-Version-Drift verursacht, kann es unverändert erneut laufen. Sinnvoller Kandidat für ein Repo-internes Maintenance-Script (z. B.
scripts/repair-canvas-page-regions.php). - Wenn die alten Component-Versionen Schema-inkompatibel zu den neuen sind (z. B. Pflicht-Slot hinzugekommen), erzeugt der Hash-Update ein syntaktisch korrektes, semantisch aber leeres Slot-Item. Hier nicht passiert, aber bei zukünftigen Drifts visuell prüfen.
- Auch
canvas_page__componentsundnode__field_component_treekönnen theoretisch driften (gleicher Mechanismus). Bei diesem Vorfall waren sie sauber. Falls künftig betroffen: Storage-ID im Script austauschen und erneut laufen. - Drupal-Config-Export (
drush config:export) sollte nach der Reparatur erfolgen, sonst überschreibt der nächsteconfig:importdie gefixten Page-Regions wieder mit den alten Hashes aus dem Sync-Verzeichnis.