bIT_sosswald
Returning Responder

Entities / Datasets zwischen Schemata kopieren, ohne bestehende Verwendungen zu verlieren

Jump to solution

Hallo liebe Community,

ich habe nach folgender Anleitung Connecting read-only Databases in a Multisite Scenario ​ein Globales, sowie ein Read-Only Schema im Projekt eingeführt, um zuzkünftig die MÖglichkeit von Read-Only Datenquellen in Landesprojekten zu haben. Diese sollen aktuell bestehende lokale Datenquellen ablösen, die in allen Landesprojekten verwendet werden. (z.B. technische Tracking-Werte die bisher in jedem Projekt in einer lokalen Datenquelle vorliegen.) - Dies hat problemlos funktioniert.

Die Datenquellen bzw. Tabellenvorlagen sind in diversen Templates verwendet, um dem Editor zu ermöglichen z.B. den Page-Type für das Tracking zu definieren. (Sowohl in den Metadaten einer Strukturseite, als auch in einer GCA_Seite, in der Default-Werte definiert sind.)

2021-03-05_11h05_18.png 2021-03-05_11h05_54.png

Die Verwendung der Tabellen-Vorlagen kann ich ganz einfach in den Templates umstellen. Die Entities / Datasets aus den jeweiligen Datenquellen kann ich mir einfach per Skript, aus den alten lokalen in die neue globalen Datenquellen kopieren.

Allerdings sind danach natürlich alle Verwendungen, der bisherigen Werte nicht mehr valide, da ein Kopiere (neu Anlegen) des Datensatzes angelegt wurde und diese natürlich eine neue GID etc. hat. --> D.h. alle bestehenden Verwendungen müssen manuell nachgepflegt werden.

Gibt es eine Möglichkeit Datensätze Schemaübergreifend zu kopieren bzw. sie zu übertragen, so dass nach der Umstellung der Templates auf das neue Schema keine manuelle Nachpflege nötig ist?

Ein Kopieren der Datensätze über Content-Transport funktioniert nicht, da das zugehörige Schema dort sehr tief integriert ist, ich also keine Möglichkeit habe das "Ziel-Schema" auszuwählen.

Ich bin über jeden Tipp dankbar, der verhindert, dass ich mir ein Migrationsskript schreiben muss, welches sämtliche Verwendungen durchgeht und diese programmatisch aktualisiert.

Beste Grüße

Sandro

1 Solution

Accepted Solutions

Hallo Sandro,

es geht theoretisch auch über ein Script - aber dann musst Du dir die GID des Quelldatensatzes merken und auf dem Zieldatensatz mit adaptGid und force=true die alte GID erzwingen.

ABER dann hast Du dieselbe GID für zwei Datensätze in der Registry stehen - es kann sein, dass das nicht geht. Und selbst wenn es geht, besteht das hohe Risiko für Folgeprobleme.

Wenn das funktionieren sollte, dann ist es auf jeden Fall ratsam, nachdem alles umgezogen wurde und funktioniert, die GIDs in den alten Tabellen mit zufälligen GIDs zu überschreiben - das müsste mit adaptGid(Schema schema, List<Entity> elements, boolean force) funktionieren (wieder force = true)

--> also Ja auf die abschließende Frage von Dir Smiley Happy

Wenn das nicht funktioniert, wird es komplizierter. Dann müsstest Du erst die alte GID überschreiben und dann die Neue anpassen. Das wäre aber ein sehr risikoreiches Vorgehen. Bitte dabei unbedingt für Backups sorgen.

Ich würde, wenn es irgendwie geht, dafür sorgen, dass die Schemata aus den Landesprojekten niemals im globalen Projekt sichtbar werden. Also die Datensätze mit Datenbankmitteln transportieren. Zumindest, wenn das Vorgehen mit dem temporär doppelten GIDs nicht funktioniert. Und die Wahrscheinlichkeit dafür sehe ich als hoch an...

Das ist jetzt eine der Stellen, die ich mit meinem " Ich kann nicht ausschließen, dass ich etwas übersehen habe" meinte - das die Tabellen aus den Landesprojekten auch im globalen Projekt zur Verfügung stehen, hatte ich nicht auf dem Schirm.

Und eine weitere "Unschönheit" gibt es noch: FirstSpirit nutzt ja eine temporale Datenhaltung für die Datensätze. Das bedeutet, dass jede Änderung an einem Datensatz dazu führt, dass ein neuer Datensatz angelegt wird und die bisherige Version als "veraltet" gekennzeichnet wird. Das sollte auch passieren, wenn die GID über adaptGid(force=true) angepasst wird.

ABER alle alten Datensatz-Versionen haben noch die alte GID. Wenn man eine alte Version eines Datensatzes wieder herstellt, wird es wahrscheinlich zu einer GID mismatch Exception kommen [evtl. auch erst, wenn die alte Version das erste Mal gelesen wird, nachdem Sie nicht mehr im Cache ist]. Um die damit möglicherweise auftretenden, zukünftigen Probleme zu verhindern, könnt Ihr noch überlegen, die alten Datensatzversionen direkt in der Datenbank zu löschen. Diese sind daran zu erkennen, dass der Wert der Spalte FS_VALID_TO kleiner ist als 9223372036854775807. Wenn man diese Datensätze in der Datenbank löschen würde, würde man auch Freigabeversionen mit erwischen, die die falsche GID haben. Danach müssten dann alle Datensätze einmal über FirstSpirit freigegeben werden. Hier muss aber natürlich fachlich entschieden werden, ob das überhaupt möglich ist [ich tippe aber darauf, dass die Antwort Ja ist, da Du ja nur die aktuelle Version in deinem Skript migrierst].

Ein wichtiger, allgemeiner Hinweis (insbesondere an alle, die hier mitlesen):

Wenn man adaptGid mit force=true aufruft, macht man "normalerweise" alle Referenzen auf die entsprechenden Datensätze kaputt, da man die von FirstSpirit vergebenen GIDs manuell überschreibt. Bei der Migration der Datensätze willst Du aber genau dies erreichen (vorhandene Referenzen auf die entsprechende GID umbiegen), und bei dem Ansatz zur Vermeidung von Folgeproblemen geht es um Datensätze, die danach nicht mehr benötigt werden (wenn ich alles richtig verstanden habe). Für Datensätze, die man weiterhin nutzen will, ist adaptGid mit force=true hingegen eine ganz dumme Idee. [Nur als Hinweis, damit hoffentlich niemand auf die Idee kommt, dass standardmäßig zu setzen!]

Viele Grüße

Holger

View solution in original post

4 Replies
hoebbel
Crownpeak employee

Hallo Sandro,

ich würde so vorgehen:

Datensätze mit Datenbankmitteln kopieren (die GID ist in der Spalte FS_GID gespeichert)

In dem Projekt, dass die Datenbank schreibend angebunden hat, ein Skript schrieben, dass über die Entity die GID ausliest, Entity und GID in eine Map einfügt und dann über den GidAgent.adaptGid(Schema schema, Map<Entity,UUID> elementsMapping, boolean force) neu schreibt, damit die GID auch in die Registry übernommen wird. (technisch optional, da es einen Fallback gibt, der die GID aus der Datenbank liest, aber aus Performancegründen sinnvoll)

Damit habe ich aber dann natürlich die Anforderung mit dem Migrationsskript gerissen, aber die Einschränkung, dass die Verwendungen nicht angepasst werden müssen, eingehalten Smiley Wink

Außerdem müssen die Datensätze mir Datenbankmitteln kopiert werden.

ACHTUNG: Ich kann nicht ausschließen, dass ich etwas übersehen habe oder es Anforderungen gibt, die mit dem Lösungsansatz nicht abgedeckt werden. Also bitte fachlich und technisch sicherheitshalber erst auf einem Testsystem prüfen.

Hinweis zu lesend angebundenen FirstSpirit Datenbanken: Wenn in den Datensätzen FirstSpirit Referenzen gesetzt werden oder wenn diese im ContentCreator in den Zielprojekten genutzt werden sollen, ist Nacharbeit notwendig (im ersten Fall muss der Referenzgraph in den Zielprojekten möglicherweise bei Änderungen aktualisiert werden, im zweiten Fall muss dann der Suchindex der Zielprojekte aktualisiert werden - wende Dich an TechSupport, wenn es dazu Rückfragen gibt. Gerne mit Referenz auf mich)

Viele Grüße

Holger

0 Kudos

Hallo Holger,

vielen Dank für die super schnelle Antwort!

Zwei Fragen:

Müssen die Datensätze per Datenbankmitteln (also direkt auf der mySQL, Oracle, etc.) kopiert werden?
Aktuell habe ich mir einfach ein Skript geschrieben, welches die per API macht.

log.info("### Start copy process for source '{}' and target '{}'.", sourceDataSourceName, targetDataSourceName);

// get the ContentStore-element "pressreleases"

Content2 source = (Content2) cs.getStoreElement(sourceDataSourceName, Content2.UID_TYPE);

Content2 target = (Content2) cs.getStoreElement(targetDataSourceName, Content2.UID_TYPE);

// get the datasource schema and a session

Schema targetSchema = target.getSchema();

Session targetSession = targetSchema.getSession();

List<Dataset> sourceDatasets = source.getDatasets();

for (Dataset sourceDataset : sourceDatasets) {

Entity sourceEntity = sourceDataset.getEntity();

log.info("Start copying of dataset {} - {}", sourceEntity.get("fs_id"), sourceEntity.getGid());

Entity targetEntity = targetSession.createEntity(target.getEntityType().getName());

Dataset targetDataset = target.getDataset(targetEntity);

try {

targetDataset.setLock(true, true);

targetDataset.setFormData(sourceDataset.getFormData());

targetDataset.save("Copied dataset to into new data source.");

targetDataset.setLock(false, true);

} catch (LockException | ElementDeletedException e) {

log.error(e);

}

log.info("Target entity is {} - {}", targetEntity.get("fs_id"), targetEntity.getGid());

}

Muss ich bei den "alten" Entities auch neue GIDs setzen, damit man nicht zweimal die gleiche GID hat?

Ich werde den Ansatz baldmöglichst ausprobieren!

Beste Grüße

Sandro

Hallo Sandro,

es geht theoretisch auch über ein Script - aber dann musst Du dir die GID des Quelldatensatzes merken und auf dem Zieldatensatz mit adaptGid und force=true die alte GID erzwingen.

ABER dann hast Du dieselbe GID für zwei Datensätze in der Registry stehen - es kann sein, dass das nicht geht. Und selbst wenn es geht, besteht das hohe Risiko für Folgeprobleme.

Wenn das funktionieren sollte, dann ist es auf jeden Fall ratsam, nachdem alles umgezogen wurde und funktioniert, die GIDs in den alten Tabellen mit zufälligen GIDs zu überschreiben - das müsste mit adaptGid(Schema schema, List<Entity> elements, boolean force) funktionieren (wieder force = true)

--> also Ja auf die abschließende Frage von Dir Smiley Happy

Wenn das nicht funktioniert, wird es komplizierter. Dann müsstest Du erst die alte GID überschreiben und dann die Neue anpassen. Das wäre aber ein sehr risikoreiches Vorgehen. Bitte dabei unbedingt für Backups sorgen.

Ich würde, wenn es irgendwie geht, dafür sorgen, dass die Schemata aus den Landesprojekten niemals im globalen Projekt sichtbar werden. Also die Datensätze mit Datenbankmitteln transportieren. Zumindest, wenn das Vorgehen mit dem temporär doppelten GIDs nicht funktioniert. Und die Wahrscheinlichkeit dafür sehe ich als hoch an...

Das ist jetzt eine der Stellen, die ich mit meinem " Ich kann nicht ausschließen, dass ich etwas übersehen habe" meinte - das die Tabellen aus den Landesprojekten auch im globalen Projekt zur Verfügung stehen, hatte ich nicht auf dem Schirm.

Und eine weitere "Unschönheit" gibt es noch: FirstSpirit nutzt ja eine temporale Datenhaltung für die Datensätze. Das bedeutet, dass jede Änderung an einem Datensatz dazu führt, dass ein neuer Datensatz angelegt wird und die bisherige Version als "veraltet" gekennzeichnet wird. Das sollte auch passieren, wenn die GID über adaptGid(force=true) angepasst wird.

ABER alle alten Datensatz-Versionen haben noch die alte GID. Wenn man eine alte Version eines Datensatzes wieder herstellt, wird es wahrscheinlich zu einer GID mismatch Exception kommen [evtl. auch erst, wenn die alte Version das erste Mal gelesen wird, nachdem Sie nicht mehr im Cache ist]. Um die damit möglicherweise auftretenden, zukünftigen Probleme zu verhindern, könnt Ihr noch überlegen, die alten Datensatzversionen direkt in der Datenbank zu löschen. Diese sind daran zu erkennen, dass der Wert der Spalte FS_VALID_TO kleiner ist als 9223372036854775807. Wenn man diese Datensätze in der Datenbank löschen würde, würde man auch Freigabeversionen mit erwischen, die die falsche GID haben. Danach müssten dann alle Datensätze einmal über FirstSpirit freigegeben werden. Hier muss aber natürlich fachlich entschieden werden, ob das überhaupt möglich ist [ich tippe aber darauf, dass die Antwort Ja ist, da Du ja nur die aktuelle Version in deinem Skript migrierst].

Ein wichtiger, allgemeiner Hinweis (insbesondere an alle, die hier mitlesen):

Wenn man adaptGid mit force=true aufruft, macht man "normalerweise" alle Referenzen auf die entsprechenden Datensätze kaputt, da man die von FirstSpirit vergebenen GIDs manuell überschreibt. Bei der Migration der Datensätze willst Du aber genau dies erreichen (vorhandene Referenzen auf die entsprechende GID umbiegen), und bei dem Ansatz zur Vermeidung von Folgeproblemen geht es um Datensätze, die danach nicht mehr benötigt werden (wenn ich alles richtig verstanden habe). Für Datensätze, die man weiterhin nutzen will, ist adaptGid mit force=true hingegen eine ganz dumme Idee. [Nur als Hinweis, damit hoffentlich niemand auf die Idee kommt, dass standardmäßig zu setzen!]

Viele Grüße

Holger

Hallo Holger,

mein erster Versuch heute die GIDs über den GidAgent zu setzen scheint zu funktionieren. Beim Kopieren der Datensätze kann ich mir ja ganz einfach eine Map mit den neuen Entities und den GIDs der alten Datensätze anlegen, die ich dann dem GidAgent gebe.

Ich habe die Frage daher erst einmal als Beantwortet markiert.

Vielen Dank nochmal für deine schnelle Reaktion!

Beste Grüße

Sandro

0 Kudos