Datenimport mit fabasoft Components/COLD Hannes Mahringer ISBN: 978-3-902495-24-2 Alle Rechte der Verbreitung, auch durch fotomechanische Wiedergabe, Tonträger jeder Art, auszugsweisen Nachdruck oder Einspeicherung und Rückgewinnung in Datenverarbeitungsanlagen aller Art, sind vorbehalten. Es wird darauf verwiesen, dass alle Angaben in diesem Fachbuch trotz sorgfältiger Bearbeitung ohne Gewähr erfolgen und eine Haftung der Autoren oder des Verlages ausgeschlossen ist. Aus Gründen der einfacheren Lesbarkeit wird auf die geschlechtsspezifische Differenzierung, z.B. Benutzer/-innen, verzichtet. Entsprechende Begriffe gelten im Sinne der Gleichbehandlung grundsätzlich für beide Geschlechter. Fabasoft und das Fabasoft Logo sind registrierte Warenzeichen der Fabasoft AG. Microsoft, MS-DOS, Windows, das Windows-Logo, Windows 95, Windows 98, Windows Me, Windows XP, Windows NT, Windows 2000, Windows Server, Active Directory, Outlook, Excel, Word, PowerPoint, Visual Studio, Visual Basic, Visual C++ sind entweder registrierte Warenzeichen oder Warenzeichen der Microsoft Corporation. Alle anderen verwendeten Hard- und Softwarenamen sind Handelsnamen und/oder Marken der jeweiligen Hersteller. © Fabasoft International Services GmbH, Linz 2007 Honauerstraße 4, 4020 Linz Tel.: +43 (732) 606162 http://www.fabasoft.at Datenimport mit Fabasoft Components/COLD Hannes Mahringer Inhaltsverzeichnis 2007 Datenimport mit Fabasoft Components/COLD 1 2 Einleitung ______________________________________________________ ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ Datenimportobjekte ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 1 11 13 2.1 Ablauf eines Imports ____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 13 2.2 Zuordnung der Spalten zu Eigenschaften ____________________________ 13 2.3 Tracing in Fabasoft Components Expressions ______________________________________________________________________________________________________________________________________________________________________________________________________________ 21 Datenquelle ____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 21 Zuordnungen von Eigenschaften ________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 23 Suchen und Erzeugen von Objekten ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 24 Importieren der Daten __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 24 3 Ablauf eines Datenimports 4 Datenimportobjekt __________________________________________________________________________________________________________________________________________________________________________________________________________________________ __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 27 31 4.1 Datenquellen ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 39 Auswahl des Datenbereichs __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 39 4.2 Objektklassen ________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 40 5 4.3 Setzen und Ändern von Eigenschaften ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ Eigenschaften ____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ Objektbeziehungen ________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ Schlüsseleigenschaften und Änderung von Werten ______________________________________________________________________________________________________________________________________________________________________________________________________ Setzen von Inhaltseigenschaften ________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ Zeichenkettenlisten ____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ Zusammengesetzte Eigenschaften ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ Verarbeitung von Listen ____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 40 4.4 Identifikation und Duplikatsprüfung ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ Objektsuche ________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ Suchbereiche ________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ Objektsperren ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 55 40 42 42 46 48 49 51 55 67 68 4.5 Parameter der Verarbeitung ________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 69 Transaktionen und Threads __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 69 Übergehen von Methoden ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 72 4.6 Importoptionen und Protokoll ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ Protokoll: Modus, Objekt, max. Einträge ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ Änderungen sofort anzeigen __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ Objekte nicht aktualisieren __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 74 4.7 Skripts ________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ Filterskript für Rohdaten __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ Filter für Objekte ____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ Filter für Objekte nach dem Commit ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 75 74 74 75 76 77 79 5 Was man unbedingt beachten sollte … ______________________________________________________________________________ 81 5.1 Nummeratoren ____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 81 Nummerator-Eigenschaften ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 81 Schlüssel-Nummerator ________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 83 5.2 Protokoll __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 5.3 Roll-Forward ________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 84 85 5.4 Starten des Datenimports ________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 88 Ferngesteuertes Importieren __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 88 6 Optimierung ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 93 6.1 Struktur des Datenimports __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ Abhängigkeiten ____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ Migrationszwischenformat __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ Zusammenfassen von Importen ________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ Aufteilung von Importen __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 93 93 96 97 97 6.2 Optimierungsoptionen des Imports ____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 97 Algorithmen zur Vermeidung doppelter Objekte ____________________________________________________________________________________________________________________________________________________________________________________________________________________________ 99 Sortierung __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 99 Parallelisierung ____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 101 6.3 Client-Optimierung __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 102 Client-Cache ____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 102 Netzwerkanbindung __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 103 Client-CPUs __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 104 Festplatte ____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 104 Anzahl der Clients ____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 105 7 6.4 Fabasoft Components COO-Service-Optimierung ____________________________________________________________________________________________________________________________________________________________________________________ 105 Anzahl der Worker Threads ________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 105 Fabasoft Components COO-Service-Cache __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 107 Objektsperren ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 107 6.5 Optimierungen am Datenbanksystem ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 107 Backup, Restore und Recovery ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 108 Indizes und Statistiken __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 108 Tabellendefinitionen ________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 110 Auto-Grow ________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 112 Device-Placement ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 112 6.6 Fabasoft Components MMC-Service-Optimierung ____________________________________________________________________________________________________________________________________________________________________________ 113 6.7 Von Objekten und Töpfen __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 113 Statische Problemanalyse ________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 114 Dynamische Problemanalyse ________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 115 Performance Monitor/SNMP ________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 117 Counter des Fabasoft Components Kernels ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 121 Counter der Fabasoft Components Services __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 124 6.8 Der Kernel-Trace ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 125 Erstellen der Traces ____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 125 Interpretation des Traces ____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 126 7 Konfiguration der Datenquellen ________________________________________________________________________________________________________________________________________________________________________ 131 7.1 ODBC ________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 135 Microsoft Excel ____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 135 CSV und Tab-Separated Files ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 136 Oracle unter Linux ____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 139 7.2 OLE DB ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 139 Definition der OLE DB-Datenquelle ____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 140 Microsoft SQL Server __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 142 Microsoft Access ________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 142 File Provider ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 143 7.3 Report-Umsetzer __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 147 7.4 Skript-Datenquelle __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 149 7.5 LDAP-Datenquelle ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 155 7.6 ADE DB ____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 156 7.7 CSV ____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 157 7.8 Roll-Forward Log-Reader ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 157 8 Zusätzliche Beispiele ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 159 8.1 Datenquellenskript __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 159 XML-Datenquelle ________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 159 8.2 Filterskript für Rohdaten ________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 160 Berechnete Spalten ____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 160 Berechung der Werte aus der LDAP-Datenquelle ____________________________________________________________________________________________________________________________________________________________________________________________________________________ 164 8.3 Filterskript für Objekte __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 166 Versionierung ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 166 8.4 Filterskript für Objekte nach dem Commit ________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 168 Archivierung ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 168 9 Glossar 10 Abbildungsverzeichnis 11 __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ Literaturverzeichnis 171 ______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 173 ____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ 175 9 1 11 1 Einleitung Dieses Buch ist für all jene geschrieben, die Daten in Fabasoft Components Domänen importieren wollen. Die Softwarekomponente Fabasoft Components/COLD ([email protected]) bietet ein mächtiges und – bei richtiger Konfiguration – sehr effizientes Werkzeug um Daten aus verschiedensten Datenquellen zu lesen und auf das Objektmodell der konkreten Installation abzubilden. Das Buch besteht aus mehreren Teilen: ° In Kapitel 2 werden anhand eines anschaulichen Beispieles die Grundfunktionen des Datenimports mit Fabasoft Components/COLD erklärt. ° ° ° ° ° ° Kapitel 3 widmet sich dem Ablauf eines Datenimports. Kapitel 4 schafft einen Einblick in die Details der Konfiguration von Datenimportobjekten. In Kapitel 5 gibt es Wissenswertes rund um den Datenimport zu lesen. Kapitel 6 behandelt die Optimierung der Imports auf allen Ebenen. Eine Anleitung zur Konfiguration der verschiedenen Datenquellen ist in Kapitel 7 zu finden. Zuletzt werden in Kapitel 8 noch einige Konfigurationsbeispiele und Skripts erklärt. 2 13 2 Ein Beispiel für den Anfang Die Zielsetzung von Fabasoft Components/COLD ist es, Daten einfach konfigurierbar und effizient in eine Fabasoft Components Domäne importieren zu können. Dieses Kapitel ist eine Einführung in die Verwendung von Fabasoft Components/COLD und ist für jene Anwender gedacht, die noch keine Erfahrungen damit haben. Dabei werden die Grundlagen anhand eines einfachen Beispiels erklärt. 2.1 Ablauf eines Imports Bei einem Datenimport werden Daten aus einer Datenquelle gelesen und in der Fabasoft Components Domäne auf bestehende oder neue Objekte übertragen. Wie diese Übertragung stattfindet, genauer gesagt welche Werte der Quelldaten in welche Eigenschaften welcher Zielobjekte geschrieben werden, wird in Datenimportobjekten definiert. Der Import wird dann durch den Aufruf der Import-Methode dieses Datenimportobjekts gestartet. Das kann entweder über das Kontextmenü oder durch den direkten Aufruf der Importmethode, zum Beispiel von einem Skript aus, erfolgen. 2.2 Zuordnung der Spalten zu Eigenschaften Bevor wir uns auf die Konfiguration der Datenimportobjekte stürzen, sehen wir uns die Zuordnung der Daten zwischen Datenquelle und Zielobjekten an. Die Daten der Datenquelle bestehen im Allgemeinen aus einer oder mehreren Zeilen (Records) die alle die gleichen Spalten (Columns) enthalten, auch wenn nicht immer jede dieser Spalten in jeder Zeile einen Wert enthalten muss. Beispiel: „Customers“ aus der „Northwind“-Datenbank Microsoft hat bis zum Microsoft SQL Server 2000 eine Demo-Datenbank mit dem Namen Northwind mit dem Produkt mitgeliefert. Diese ist bis heute auch als Download frei verfügbar und kann auch am Microsoft SQL Server 2005 verwendet werden. In dieser Datenbank gibt es eine Tabelle Customers, die 91 Datensätze mit Kundendaten enthält. Diese eignen sich dazu, in eine Fabasoft Components Domäne geladen zu werden. Aus diesen Daten sollen Personenobjekte und Organisationen erzeugt werden. Tabelle 1 zeigt das Format der Quelldaten, so wie sie in der Datenbank zur Verfügung stehen. COLUMN NAME TYPE LENGTH NULLABLE CustomerID nchar 5 No CompanyName nvarchar 40 No ContactName nvarchar 30 Yes ContactTitle nvarchar 30 Yes Address nvarchar 60 Yes City nvarchar 15 Yes Region nvarchar 15 Yes PostalCode nvarchar 10 Yes Country nvarchar 15 Yes Phone nvarchar 24 Fax nvarchar 24 Yes Yes Tabelle 1: Format der Quelldaten Man sieht, dass alle Spalten als Textspalten deklariert sind. Die CustomerID eignet sich hervorragend als Schlüsseleigenschaft für die Person, der CompanyName ist als einzige Eigenschaft der Organisation gleichzeitig die Schlüsseleigenschaft der Organisation. Tabelle 2 und Tabelle 3 enthalten die zu verwendenden Eigenschaften der Zielobjektklassen: 2. Ein Beispiel für den Anfang 2.2 Zuordnung der Spalten zu Eigenschaften Person NAME REFERENZ TYPE LÄNGE Externer Schlüssel [email protected]:objexternalkey STRING 254 Vorname [email protected]:userfirstname STRING 64 Nachname [email protected]:usersurname STRING 64 Organisation. Orgnaisation [email protected]:persjobs. [email protected]:joborganization STRING 80 Organisation. Position/Tätigkeit [email protected]:persjobs. [email protected]:jobfunction STRING 80 Adresse.Adresstyp [email protected]:address. [email protected]:addrtype ENUM Adresse.Straße [email protected]:address. [email protected]:addrstreet STRING 80 Adresse.Ort [email protected]:address. [email protected]:addrcity STRING 60 Adresse.Bundesland [email protected]:address. [email protected]:addrstate STRING 60 Adresse. Postleitzahl [email protected]:address. [email protected]:addrzipcode STRING 20 15 NAME REFERENZ TYPE LÄNGE Adresse.Land [email protected]:address. [email protected]:addrcountry STRING 60 Telefonnummern.Typ [email protected]:telephones. [email protected]:teltype ENUM Telefonnummern. Telefonnummer [email protected]:telephones. [email protected]:telnumber STRING 32 Tabelle 2: Eigenschaften der Objektklasse Person Organisation NAME REFERENZ TYPE LÄNGE Kurzname [email protected]:orgshortname STRING 50 Tabelle 3: Eigenschaft der Objektklasse Organisation Wenn man die Quelldaten mit den Zieldaten vergleicht, so stößt man auf folgendes Problem: Die Spalte ContactName beinhaltet sowohl den Vor- als auch den Nachnamen der Person. Dieser Wert muss also zuerst in zwei Teile „zerlegt“ werden, bevor er den beiden Eigenschaften Vorname und Nachname zugeordnet werden kann. Dafür werden in weiterer Folge zwei Lösungen gezeigt. 1. Am Microsoft SQL Server kann eine View definiert werden, in der aus dem ContactName zwei Spalten, ContactFirstname und ContactLastname, berechnet werden. 2. Ein Beispiel für den Anfang 2.2 Zuordnung der Spalten zu Eigenschaften 2. Im Filterskript für Rohdaten des Datenimportobjekts kann der ContactName in zwei berechnete Spalten aufgeteilt werden. Dieses Skript finden Sie als Beispiel auf Seite 161. Wenden wir uns also der ersten Lösungsmöglichkeit zu: Eine SQL-View definiert eine Sicht auf die bestehenden Daten, in der neben den Originaldaten auch berechnete Spalten enthalten sein können. In unserem Fall sollen zwei zusätzliche Spalten definiert werden. Für die Berechnung wird der erste Teil des Namens bis zum ersten Leerzeichen als Vorname in der Spalte ContactFirstname, und der Rest nach dem Leerzeichen als Nachname in der Spalte ContactLastname interpretiert. Die View, wie sie in Quelltext 1 dargestellt ist, funktioniert zwar nicht bei allen Einträgen, dies ist aber für dieses Beispiel unerheblich („Miguel Angel Paolino“ und „José Pedro Freyre“ mögen mir verzeihen). CREATE VIEW CustomersNameView AS SELECT SUBSTRING(ContactName, 1, CHARINDEX(' ', ContactName)) AS ContactFirstname, SUBSTRING(ContactName, CHARINDEX(' ', ContactName) + 1, 200) AS ContactLastname, * FROM Customers Quelltext 1: SQL-View CustomersNameView Aus den vorangegangenen Tabellen ergibt sich bei Verwendung der CustomersNameView die Zuordnung der Spalten zu den einzelnen Eigenschaften der Objekte: 17 SPALTE/WERT OBJEKTKLASSE EIGENSCHAFT REFERENZ DER EIGENSCHAFT CustomerID Person Externer Schlüssel [email protected]: objexternalkey CompanyName Organisation Kurzname [email protected]: orgshortname ContactFirstname Person Vorname [email protected]: userfirstname ContactLastname Person Nachname [email protected]: usersurname <Organisation> Person Organisation. Orgnaisation [email protected]: persjobs. [email protected]: joborganization ContactTitle Person Organisation. Position/ Tätigkeit [email protected]: persjobs. [email protected]: jobfunction "1" Person Adresse. Adresstyp [email protected]: address. [email protected]: addrtype 2. Ein Beispiel für den Anfang 2.2 Zuordnung der Spalten zu Eigenschaften SPALTE/WERT OBJEKTKLASSE EIGENSCHAFT REFERENZ DER EIGENSCHAFT Address Person Adresse.Straße [email protected]: address. [email protected]: addrstreet City Person Adresse.Ort [email protected]: address. [email protected]: addrcity Region Person Adresse. Bundesland [email protected]: address. [email protected]: addrstate PostalCode Person Adresse. Postleitzahl [email protected]: address. [email protected]: addrzipcode Country Person Adresse.Land [email protected]: address. [email protected]: addrcountry 19 SPALTE/WERT OBJEKTKLASSE EIGENSCHAFT REFERENZ DER EIGENSCHAFT "400" Person Telefonnummern. Typ [email protected]: telephones. [email protected]: teltype Phone Person Telefonnummern. Telefonnummer [email protected]: telephones. [email protected]: telnumber "405" Person Telefonnummern. Typ [email protected]: telephones. [email protected]: teltype Fax Person Telefonnummern. Telefonnummer [email protected]: telephones. [email protected]: telnumber Tabelle 4: Zuordnung der Datenbankspalten zu Eigenschaften von Objekten Zusätzlich zu den Werten der Datenbanken werden hier für die Typ-Eigenschaften der Adresse und der Telefonnummern Konstanten verwendet, die sich aufgrund der Bedeutung der Werte ergeben. So steht zum Beispiel die Zahl „405“ in der vorletzten Zeile der Tabelle für den Aufzählungswert „Fax“ des Typs der Telefonnummer. 2. Ein Beispiel für den Anfang 2.2 Zuordnung der Spalten zu Eigenschaften 2.3 Datenimportobjekte 2.3 Datenimportobjekte Wie alles in Fabasoft Components werden auch Datenimporte in Form von Objekten definiert – den Datenimportobjekten. Es gibt zwei Objektklassen dafür, die von der Funktionalität her äquivalent sind und sich nur dadurch unterscheiden, dass die eine von der Objektklasse Konfigurationsobjekt abgeleitet ist und die andere von Basisobjekt. Komponentenobjekte haben den Vorteil, im Rahmen von Softwarekomponenten extrahiert und in anderen Fabasoft Components Domänen wieder geladen werden zu können, während die„normalen“ Datenimportobjekte ohne Softwarekomponente lokal in einer Fabasoft Components Domäne angelegt werden können, damit aber weniger leicht in andere Installationen transportiert werden können. Für die Definition und die Durchführung der Datenimporte ist dieser Unterschied jedoch unerheblich und so wird in weiterer Folge nur von Datenimportobjekten gesprochen und es sind damit, sofern nicht explizit darauf eingegangen wird, beide Ausprägungen dieser Objekte gemeint. Wird nun ein Datenimportobjekt angelegt und über den Menübefehl Eigenschaften bearbeiten aus dem Kontextmenü geöffnet, besteht die erste Aufgabe darin, die Verbindung zur Datenquelle herzustellen. Datenquelle In unserem Beispiel gehen wir davon aus, dass die Quelldaten in einer Microsoft SQL Server-Datenbank am lokalen Rechner in einer Datenbank mit dem Namen Northwind in der Tabelle CustomersNameView stehen. Andere Datenquellen sind ähnlich zu verwenden und es wird im Kapitel „Konfiguration der Datenquellen“ auf Seite 131 im Detail auf die Konfiguration und Verwendung der verschiedenen Datenquellen eingegangen. Die beste Möglichkeit auf eine Microsoft SQL Server-Datenbank unter Microsoft Windows zuzugreifen ist die Verwendung des OLE DB-Providers für den SQL Server. 21 Dazu wird, wie in Abbildung 1 ersichtlich, als Datenquelle „OLE DB“ ausgewählt und als Verbindungsparameter „Provider=SQLOLEDB;Datasource=localhost;Catalog=Northwind“ angegeben. Damit wird definiert, dass die Verbindung über den OLE DB-Provider „SQLOLEDB“ zu Datenbank Northwind auf dem lokalen Rechner „localhost“ hergestellt wird. In der Eigenschaft Tabelle wird der Name der Tabelle oder View, also in unserem Fall CustomersNameView eingegeben und damit ist die Datenquelle fertig definiert. Abbildung 1 Customers: Datenquelle und Zuordnungen 2. Ein Beispiel für den Anfang 2.3 Datenimportobjekte Zuordnungen von Eigenschaften Nun folgt der Schritt, der in Tabelle 4 bereits vorbereitet wurde: Die Abbildung der Spalten der Quelldatenbank auf Eigenschaften von Objekten. Dazu wird für jede der Zeilen in der Tabelle eine Zeile in der zusammengesetzten Eigenschaft Zuordnung definiert (siehe Abbildung 1). Um in den Zuordnungen zwischen Telefon- und Faxnummer zu unterscheiden, wird bei einer Faxnummer die Objekt-ID „1“ gesetzt. Die Objektbeziehung zwischen Organisation und Person wird nun, wie in Abbildung 2 ersichtlich, in der Tabelle Objektbeziehungen definiert. Abbildung 2 Customers: Objektbeziehungen und Klasseneigenschaften 23 Suchen und Erzeugen von Objekten Die Klasseneigenschaften legen vor allem fest, wie die Objekte gesucht und ob sie erzeugt werden sollen. In unserem Beispiel werden die in Abbildung 2 angeführten Einstellungen verwendet. In diesem Fall wird in der Fabasoft Components Domäne nach bestehenden Objekten mit den definierten Schlüsseleigenschaften gesucht. Wird ein Objekt nicht gefunden, so wird es neu erzeugt. Bei der Person mit der ObjektID „1“, die für den zweiten Eintrag in die Liste der Telefonnummern benötigt wurde, wird hier definiert, dass für die Person mit ID „1“ dasselbe Objekt, wie bei der Person mit der niedrigeren ID – also „0“ – verwendet wird. Importieren der Daten Der Import kann nun, wie in Abbildung 3 dargestellt, aus dem Kontextmenü des Datenimportobjekts mit dem Menübefehl Daten importieren gestartet werden. Alternativ dazu kann ein Datenimport auch durch Skripts, Fabasoft Components Expressions oder durch einen Fabasoft Components AT-Job gestartet werden. 2. Ein Beispiel für den Anfang 2.3 Datenimportobjekte Abbildung 3 Customers: Daten importieren 25 3 27 3 Ablauf eines Datenimports In diesem Kapitel werden anhand der Datenflüsse die am Import beteiligten Komponenten erklärt. Dieses Verständnis ist die Voraussetzung dafür, dass die Auswirkungen der Konfigurationsmaßnahmen richtig eingeschätzt werden können und danach die richtigen Optimierungsmaßnahmen ergriffen werden. Abbildung 4 Datenfluss bei der Verarbeitung eines Datenimports Betrachtet man die Endpunkte eines Datenimports in Abbildung 4, so erkennt man, dass ein Datenimport eine Transformation von Daten, die in einer Datenbank oder einer Dateistruktur vorliegen, in eine andere Form auf einer anderen Datenbank und/oder Dateistruktur darstellt. Dazwischen liegen die Services der Fabasoft Components Domäne. Auf dem Fabasoft Components Kernel wird der eigentliche Importvorgang ausgeführt, und die Fabasoft Components Backendservices übernehmen die Umsetzung der Objektstruktur des Fabasoft Components Kernels in das Format zur Speicherung in der Datenbank bzw. im Dateisystem. Betrachtet man den Datenimport am Fabasoft Components Kernel näher, so erkennt man, dass auch hier unterschiedliche Prozesse (Threads) an der Verarbeitung beteiligt sind. Neben dem Import Thread, der die Steuerung der Abläufe übernimmt, ist das der Reader Thread, der die Daten der Datenquelle liest und in Paketen zur Weiterverarbeitung an einen oder mehrere Worker Threads übergibt. Diese Worker Threads übernehmen die Umsetzung der Werte aus dem Quellformat in die Objektwelt von Fabasoft Components. Diese Worker Threads sind es auch, die mit den Fabasoft Components Backendservices kommunizieren, sei es bei der Objektsuche oder beim Commit der Transaktionen, also beim Schreiben der Änderungen auf die Fabasoft Components COO- und MMC-Services. Auch auf den Fabasoft Components COO- und MMC-Services ist ein Pool von Worker Threads für die Bearbeitung der Anforderungen von den einzelnen Fabasoft Components Kernels verfügbar. Hier wird eine Maximalzahl der Worker Threads konfiguriert und damit die maximale Parallelität des jeweiligen Services definiert. Werden gleichzeitig mehr Anfragen an eines der Fabasoft Components Backendservices gestellt als bei diesem Worker Threads definiert sind, so müssen die überzähligen Anfragen auf das Freiwerden eines Threads warten. Im Bereich der Datenspeicherung in der Datenbank und dem Dateisystem werden die Änderungen in Form von Datensätze und Dateien persistiert. 3. Ablauf eines Datenimports 29 4 31 4 Datenimportobjekt Wenden wir uns nun der Konfiguration des Datenimports zu. Wie in Fabasoft Components üblich, erfolgt diese Konfiguration in Form von Objekten – den Datenimportobjekten – in der Fabasoft Components Domäne. Die Datenimportobjekte gibt es in zwei Ausprägungen: Als normales Datenimportobjekt ([email protected]:DataImport) abgeleitet von Basisobjekt ([email protected]: BasicObject) und als Datenimport-Komponentenobjekt ([email protected]: DataImportComponentObject) abgeleitet von Konfigurationsobjekt ([email protected]: ConfigurationObject), das sich von dessen Basisklasse Komponentenobjekt ([email protected]:ComponentObject) durch die häufigere Aktualisierung unterscheidet. Die beiden Datenimportobjektklassen haben dieselben Eigenschaften zugeordnet, nur dass das Komponentenobjekt zusätzlich die drei Eigenschaften Softwarekomponente ([email protected]:component), Referenz ([email protected]:reference) und Gelöscht ([email protected]:deleted) erbt und daher im Rahmen einer Softwarekomponente transportiert werden kann. Im sonstigen Verhalten unterscheiden sich diese beiden Objektklassen nicht und daher wird im Weiteren nicht zwischen diesen beiden Ausprägungen unterschieden und allgemein von Datenimportobjekten gesprochen, womit jedoch beide Arten gemeint sind. Die Eigenschaften des Datenimportobjekts werden in mehrere Gruppen eingeteilt, auf die im späteren Verlauf dieses Kapitels weiter eingegangen wird: Definition der Datenquelle Ein Beispiel für diese Eigenschaften finden Sie in Abbildung 1. ° Datenquelle ([email protected]:datimpdatasourcetype und [email protected]: datimpdatasource): Gemeinsam definieren diese beiden Eigenschaften die Verbindung zur Datenquelle. ° Datenquellenobjekt ([email protected]:datimpobjdatasource): Objekte dieser Eigenschaft werden bei dateibasierten Quelldatenformaten als Datenquelle verwendet. Unterstützt werden dabei Dokumente, deren Hauptinhalt die Endung „xls“ oder „csv“ hat. Diese werden mit den passenden ODBCTreibern geöffnet, was jedoch nur auf der Microsoft Windows-Plattform funktioniert. ° Tabelle ([email protected]:datimpsourcetable): Hier wird der Name der Tabelle angegeben. Zuordnung der Werte und Referenzen zu den Eigenschaften ° Zuordnung ([email protected]:datimpmapping): In dieser Liste werden die einzelnen Zuordnungen von Datenbankspalten bzw. Festwerten zu den jeweiligen Eigenschaften vorgenommen (siehe Abbildung 5). Abbildung 5 Zuordnungen mit Spaltenansicht ° Objektbeziehungen ([email protected]:datimpobjectreferences): In dieser Liste werden die Referenzen zwischen den am Import beteiligten Objekten definiert (siehe Abbildung 6). 4. Datenimportobjekt 33 Abbildung 6 Objektbeziehungen mit Spaltenansicht Suchen und Erzeugen von Objekten ° Klasseneigenschaften ([email protected]:datimpclassproperties): Hier wird für jede Objektklasse definiert, welche Methode zum Suchen von bereits existierenden Objekten verwendet wird und ob neue Objekte erzeugt werden sollen (siehe Abbildung 7). Abbildung 7 Kasseneigenschaften mit Spaltenansicht Einstellungen zur Verarbeitung Die Einstellungen zur Verarbeitung umfassen die Auswahl eines Bereichs der Datensätze sowie die Anzahl der parallelen Transaktionen und deren Größe, wie in Abbildung 8 dargestellt. 4. Datenimportobjekt 35 Abbildung 8 Einstellungen und Optionen des Datenimports ° Anzahl der Datensätze für Commit ([email protected]:datimpbulksize) und Mindestens ([email protected]:datimpminbulksize): Mit diesen beiden Eigenschaften wird festgelegt, wie viele Datensätze in einem Block verarbeitet und damit in einer Transaktion auf die Datenbank geschrieben werden. ° Anzahl von Threads ([email protected]:datimpthreads): Diese Eigenschaft definiert die Anzahl der in einem Datenimport verwendeten parallelen Worker Threads. ° Anfangsdatensatz ([email protected]:datimpstartrecord) und Anzahl zu lesender Datensätze ([email protected]:datimprecords): Mit diesen beiden Eigenschaften wird der Bereich der zu verarbeiteten Datensätze angegeben. Protokollierungsoptionen Die Protokollierungsoptionen sind in Abbildung 9 dargestellt. Abbildung 9 Protokollobjekt ° Protokollmodus ([email protected]:datimprecordmode): Diese Eigenschaft bestimmt den Detaillierungsgrad des Protokolls. ° Protokoll-Objekt ([email protected]:datimplogobj): In dieser Eigenschaft wird jenes Objekt abgelegt, in dem die Protokolle der Datenimporte gespeichert werden. Im Protokollobjekt wird pro Ladevorgang ein Eintrag erstellt, in dem neben Start- und Endzeit in einer Datei je nach Protokollmodus eine mehr oder weniger ausführliche XML-Datei abgelegt wird. Ergänzt wird der Protokolleintrag durch die wichtigsten statistischen Daten wie Anzahl der Datensätze, Anzahl der erzeugten bzw. aktualisierten Objekte, Anzahl der aufgetretenen Fehler und Anzahl der verwendeten Worker Threads. 4. Datenimportobjekt 37 ° Maximale Anzahl der Einträge im Protokoll ([email protected]:datimpmaxhistory): Wird dieser Wert gesetzt, wird im Protokollobjekt maximal die gesetzte Anzahl von Protokolleinträgen aufbewahrt, um den Aufwand für die Protokollierung in Grenzen zu halten. Performanceoptionen Zuletzt sind in Abbildung 8 noch vier vor allem für die Performance relevante Eigenschaften des Datenimportobjekts zu sehen. ° Änderungen sofort anzeigen ([email protected]:datimpuiupdate): Wird diese Eigenschaft auf den Wert „Ja“ gesetzt, so werden im Fabasoft Win32-Client der Version 6 die Änderungen an den Objekten im Rahmen des Commits der Transaktion an die Benutzerschnittstelle weitergegeben. Damit werden die Objekte sofort aktualisiert. ° Methoden ohne Prüfung übergehen? ([email protected]:datimpfastwrapper): Wird konfiguriert, dass Methoden übergangen werden sollen und ist diese Eigenschaft auf „Ja“ gesetzt, wird beim Übergehen nicht geprüft, ob die Methode gerade auf die konfigurierte Objektklasse bzw. Eigenschaft angewendet wird. ° Datenimport bei Fehler sofort abbrechen ([email protected]:datimtermonerror): Wird diese Eigenschaft auf „Ja“ gesetzt, wird der Datenimport beim Auftreten des ersten Fehlers abgebrochen. ° Objekte nicht aktualisieren ([email protected]:datimpnoobjectrefresh): Bei bereits existierenden Objekten wird vor jeder Verwendung geprüft, ob dieses Objekt zwischenzeitlich von einem anderen Fabasoft Components Kernel verändert wurde. Diese Prüfung entfällt, wenn diese Eigenschaft auf „Ja“ gesetzt wird. Skripts Es gibt drei Skripts, die in verschiedenen Phasen der Verarbeitung jedes Datensatzes aufgerufen werden und somit unterschiedliche Möglichkeiten bieten, in die Verarbeitung einzugreifen. Jedes dieser Skripts wird mit einer eigenen booleschen Eigenschaft aktiviert bzw. deaktiviert (siehe Abbildung 10). Abbildung 10 Skripts des Datenimports ° Filter-Skript für Rohdaten ([email protected]:datimpfilterdata): In diesem Skript können die Daten, die von der Datenquelle geliefert werden, vor der weiteren Verarbeitung noch gefiltert und modifiziert werden. Auch selbst definierte Spalten, die nicht von der Datenquelle stammen, können hier berechnet werden. ° Filter-Skript für Objekte ([email protected]:datimpfilterobjects): Dieses Skript wird nach dem Setzen der Eigenschaften und vor dem Aufruf der Commit-Methode aufgerufen. 4. Datenimportobjekt 4.1 Datenquellen ° 4.1 Filter-Skript für Objekte nach dem Commit ([email protected]:datimpfiltercommit): Nach dem erfolgreichen Commit wird dieses Skript für jeden Datensatz aufgerufen. Datenquellen Die Quelldaten des Imports stammen meistens aus Datenbanken oder strukturierten Dateien (z.B. CSV-Dateien), aber es können auch andere Datenquellen angebunden werden. Eine genaue Beschreibung der Datenquellen und deren Konfiguration finden Sie im Kapitel „Konfiguration der Datenquellen“ auf Seite 131. Auswahl des Datenbereichs Von den Datensätzen, die von der Datenquelle geliefert werden, müssen nicht alle verarbeitet werden. Einerseits können im Skript für Rohdaten gezielt Daten herausgefiltert werden, andererseits besteht die Möglichkeit nur einen bestimmten Bereich zu laden. Dazu gibt man die Nummer des ersten Datensatzes und die Anzahl der zu lesenden Datensätze an. Damit kann man zum Beispiel zum Testen erst einmal wenige Datensätze importieren und danach die restlichen Datensätze. Oder man verwendet diese Funktion zur Aufteilung der Daten in verschiedene Datenblöcke, die dann mit mehreren Clients parallel importiert werden können. Allerdings sollte man beachten, dass nicht immer gewährleistet ist, dass die Datensätze immer in derselben Reihenfolge geliefert werden, auch wenn das im Normalfall so ist. Gerade bei Datenbanken ist das nicht garantiert, wenn auch die Reihenfolge meistens von der physikalischen Speicherung der Datensätze auf der jeweiligen Datenbank abhängt. So werden Tabellen vom Microsoft SQL Server generell in der Reihenfolge gelesen, die dem Clustered Index entspricht, sofern einer definiert ist. Gewissheit schafft in diesem Fall nur ein ORDER BY in der SQL-Abfrage. Bei anderen Datenquellen (z.B. CSV-Dateien) ist die Reihenfolge durch die Reihenfolge der Datensätze am Medium gegeben und somit können gezielt Datenbereiche ausgewählt werden. 39 4.2 objektklassen In den folgenden Kapiteln werden Objektklassen zur Identifikation von Objektmengen verwendet. So werden Zuordnungen von Werten zu Eigenschaften von Objekten durch die Angabe der Objektklasse und der Eigenschaft definiert. Die Objektklasse ist aber in einigen Fällen nicht ausreichend, um die Objektmenge eindeutig zu identifizieren, da Objektklassen in einem Datenimport auch in unterschiedlichen Funktionen verwendet werden können. Zum Beispiel werden bei der Definition von Hierarchien eine übergeordnete Instanz und eine untergeordnete Instanz derselben Objektklasse verwendet. Um diese Instanzen unterscheiden zu können, wird überall neben der Objektlasse auch noch eine numerische Objekt-ID angegeben. Die Kombination von Objektklasse und ID identifiziert dann das Objekt in seiner Funktion. Die ID kann pro Objektklasse unabhängig vergeben werden. Ist keine ID angegeben, so wird sie implizit als „0“ interpretiert. Für jede Objektklasse kann für jede ID explizit angegeben werden, ob neue Objekte angelegt werden sollen und ob die Objekte gesperrt werden sollen. Die Suchmethoden, Suchbereiche und Optionen wirken sich auf alle Instanzen der Objektklasse aus, egal welche ID verwendet wird. Auch die Definition der Schlüsseleigenschaften muss pro Objektklasse einheitlich erfolgen. 4.3 Setzen und Ändern von Eigenschaften Eigenschaften In diesem Bereich des Datenimportobjekts wird spezifiziert, welche Spalten der Datenquelle in der Fabasoft Components Domäne Eigenschaften von Objekten zugeordnet werden. 4. Datenimportobjekt 4.2 Objektklassen 4.3 Setzen und Ändern von Eigenschaften Beispiel: Zuordnung der Schlüsseleigenschaft einer Person Es sollen die Werte der Spalte CustomerID der Eigenschaft Externer Schlüssel ([email protected]:objexternalkey) der Objektklasse Person ([email protected]: CISPerson) zugeordnet werden. Diese Zuordnung ist in Abbildung 5 in der Spaltenansicht dargestellt. Eine Spalte der Quelldaten kann auch mehreren Eigenschaften – auch verschiedener Objektklassen – zugeordnet werden. Dazu verwendet man einfach den Namen der Spalte in mehreren Zeilen der Zuordnungstabelle. Soll eine Eigenschaft auf einen Festwert gesetzt werden, so kann an Stelle des Spaltennamens in der Eigenschaft Spalte in der Eigenschaft Festwert jener Wert eingetragen werden, der der Eigenschaft zugeordnet werden soll. Werte können auch aus mehreren Spalten und Festwerten zusammengesetzt werden. Dazu werden einfach mehrere Zuordnungen zur selben Objektklasse und Eigenschaft definiert und alle dadurch zusammengesetzten Werte werden als Zeichenkette aneinandergehängt. So kann beispielsweise der Objektname der Person aus dem Nachnamen, einem Beistrich und dem Vornamen zusammengesetzt werden, indem zuerst für die Spalte Nachname, dann für den Festwert „, “ und schließlich für die Spalte Vorname eine Zuordnung zur selben Eigenschaft des Objektes erstellt wird. Eine Zeile, in der weder eine Spalte noch ein Festwert enthalten sind, wird als Festwert mit einem Leerzeichen interpretiert. Da die Werte intern immer als Zeichenketten vorliegen, werden sie auch immer als Zeichenketten zusammengefügt, ganz egal ob sie einer Zeichenketteneigenschaft oder zum Beispiel einer Zahleneigenschaft zugeordnet werden. Für komplexere Formatierungen muss auf SQL- bzw. Rohdatenskripts zurückgegriffen werden. Dafür steht auch die Möglichkeit zur Verfügung, berechnete Spalten zu verwenden. Diese werden bei der Zuordnung wie Spalten der Datenquelle behandelt, erhalten aber ihren Wert vom Rohdatenskript, das später beschrieben wird. Zur Unterscheidung von Datenbankspalten beginnen die Namen dieser Spalten mit einem „@“-Zeichen. 41 Objektbeziehungen In einem Datensatz werden neben einzelnen Objekten (z.B. Personen) auch Beziehungen zwischen Objekten definiert. So sind bei Personen auch oft Zuordnungen zu Organisationen oder Firmen hinterlegt, die in eigenen Objekten erfasst sind. Um diese Verbindung herzustellen, werden bei den Eigenschaften Zuordnungen zu mehreren Objektklassen definiert. Um diese miteinander zu verknüpfen, definiert man bei den Objektbeziehungen, welches Quellobjekt in die angegebene Objektzeiger-Eigenschaft des Zielobjektes übernommen werden soll. Beispiel: Objektbeziehung zwischen Person und Organisation In Abbildung 6 wird die Objektbeziehung zwischen Organisation und Person definiert, indem die Objektklasse in die Eigenschaft Organisation ([email protected]) der zusammengesetzten Eigenschaft Organisation ([email protected]:persjobs) eingetragen wird. Durch die Rückwärtsverkettung wird auch die Person in der Eigenschaft Personen ([email protected]: orgemployeelist) der Organisation eingetragen. Diese Zuordnung kann optional auch direkt im Datenimportobjekt durch eine weitere Objektbeziehung abgebildet werden, was aber praktisch keinen Vorteil hat. Auch hier können Objekte gleichzeitig in mehrere Eigenschaften derselben oder anderer Objektklassen eingetragen werden, man muss nur das Quellobjekt in mehreren Zeilen als Quellobjekt eintragen. Auch ist es möglich, gleichzeitig mehrere Objekte in eine Objektliste einzufügen, indem man die Zielobjektklasse und Eigenschaft in mehreren Zeilen verwendet. Auch können Objekte in einem Ladevorgang als Quell- und Zielobjekt in einer oder mehreren Zeilen verwendet werden. Schlüsseleigenschaften und Änderung von Werten Die Art und Weise der Änderung kann bei Zuordnung von Werten zu Eigenschaften mithilfe der Eigenschaft Schlüssel- und Änderungsmodus konfiguriert werden, bei den Objektbeziehungen mithilfe der Eigenschaft Änderungsmodus. Die unterschiedliche Bezeichnung ergibt sich nur aus der Tatsache, dass Objektzeigereigen- 4. Datenimportobjekt 4.3 Setzen und Ändern von Eigenschaften schaften keine Schlüsseleigenschaften sein können. Abbildung 11 zeigt die möglichen Werte der Eigenschaft im Rahmen der Zuordnungen. Abbildung 11 Schlüssel und Änderungsmodus Schlüssel Schlüsseleigenschaften identifizieren ein Objekt eindeutig, wobei ein Objekt durch eine oder mehrere Eigenschaften identifiziert werden kann. Mindestens eine dieser Eigenschaften muss einen Wert beinhalten, um einen gültigen Schlüssel zu ergeben. Objekte, die keinen gültigen Schlüssel haben, bei denen also keine der definierten Schlüsseleigenschaften einen Wert zugeordnet hat, der nicht leer ist, werden weder gesucht noch erzeugt. Bei der Suche nach bestehenden Objektinstanzen werden die Schlüsseleigenschaften als Suchkriterien verwendet. Wird das Objekt nicht gefunden, so kann eine neue Instanz angelegt werden und dieser werden die Schlüs- 43 seleigenschaften zugewiesen, sodass sie in weiterer Folge unter diesem Schlüssel wiedergefunden werden kann. Daher können diese Eigenschaften auch nicht mehr verändert werden. Es gibt Einschränkungen, welche Eigenschaften als Schlüsseleigenschaften verwendet werden können. Darauf wird später noch eingegangen. Objektbeziehungen gehören nicht dazu und daher ist dieser Änderungsmodus bei Objektbeziehungen auch nicht auswählbar. Setzen beim Erzeugen Wie bei Schlüsseleigenschaften wird die Eigenschaft nur dann gesetzt, wenn das Objekt während der Verarbeitung dieses Datensatzes erzeugt wurde. Werte bestehender Objekte werden nicht mehr verändert. Ist für diese Eigenschaft ein Defaultwert definiert oder wird diese durch einen Konstruktor gesetzt, bleibt dieser Wert erhalten, sofern der Wert der zugeordneten Spalte leer ist. Das ergibt sich daher, dass Leerwerte bei diesem Modus nicht gesetzt werden, um unnötige Aktualisierungen zu vermeiden. Ändern Die Eigenschaft wird geändert, wenn die zugeordnete Spalte einen Wert beinhaltet. Bestehende Werte werden also durch Leerwerte nicht überschrieben. Bei Listeneigenschaften werden die Werte an die Liste angefügt, ausgenommen es ist eine Liste mit eindeutigen Einträgen und der Wert ist bereits in der Liste enthalten. Das wird im Typ der Eigenschaft durch die Eigenschaft Eindeutige Einträge in Liste bestimmt. Dazu wird der Wert der Eigenschaft ausgelesen. Nur wenn dieser ungleich dem neuen Wert ist bzw. bei eindeutigen Listen der neue Wert in der Liste noch nicht enthalten ist, wird der Wert geändert. Beim nochmaligen Laden der Daten werden dadurch nur die notwendigen Änderungen der Eigenschaften durchgeführt, aber keine Duplikate eingefügt. Ein Problem ergibt sich jedoch bei Eigenschaften, deren Werte durch Get- oder Set-Aktionen geändert (z.B. formatiert) werden. Diese werden als ungleich erkannt und jedes Mal aufs Neue geändert. 4. Datenimportobjekt 4.3 Setzen und Ändern von Eigenschaften Überschreiben Die Eigenschaft wird geändert, auch wenn der Wert der zugeordneten Spalte leer ist. In diesem Fall wird die Eigenschaft also gelöscht. Auch hier wird der Wert nur gesetzt, wenn er sich gegenüber dem bestehenden Wert geändert hat. Bei Listeneigenschaften wird der gesamte Inhalt der Liste durch den vorliegenden Wert ersetzt bzw. gelöscht, wenn der Eigenschaft kein Wert zugewiesen wird. Ist dafür keine Änderung notwendig, wird die Eigenschaft nicht neu gesetzt. Löschen und neu anlegen Dieser Änderungsmodus dient zum Abgleich bestehender Listen durch eine Datenquelle (z.B. dem Active Directory). Es werden dabei alle Werte der Eigenschaft gelöscht und durch die Werte aus dem Datenimport neu befüllt. Beispiel: Abgleich der Gruppen eines Benutzers über LDAP Wenn man die Benutzerdaten mit einer LDAP-Datenquelle abgleicht, so werden den Benutzern in der Regel Gruppen zugeordnet. Problematisch ist dabei das Löschen von Gruppenzuordnungen, da für diese Löschung von der Datenquelle kein Datensatz geliefert wird, sondern nur jene Datensätze für die aktiven Zuordnungen in den Quelldaten enthalten sind. Daher muss die Zuordnungstabelle bei jedem Ladevorgang komplett neu aufgebaut werden. Dabei muss jedoch sichergestellt werden, dass der komplette Neuaufbau der Liste innerhalb der Verarbeitung eines einzigen Datenblocks durchgeführt wird, da sonst bei der Verarbeitung des nächsten Datenblocks die gerade gesetzten Werte gelöscht würden. Das kann durch Sortierung der Daten im Zusammenhang mit der Verwendung der Option „Gruppenwechsel“ gewährleistet werden. Diese Option ist nur bei Listeneigenschaften auf Objektebene verwendbar. Listeneigenschaften innerhalb zusammengesetzter Eigenschaften können mit dieser Methode nicht abgeglichen werden. 45 Ignorieren Diese Option dient dazu, Zuordnungen zu deaktivieren. Allerdings wird dadurch nur die Zuordnung des Wertes bzw. des Objektzeigers zur Eigenschaft deaktiviert und daher werden keine Änderungen mehr durch diese Zeilen verursacht. Nicht deaktiviert werden einige später noch beschriebene Optionen. Sie bleiben trotz „Ignorieren“ wirksam. Auf diese Spezialfälle wird dann im Rahmen der Behandlung zusammengesetzter Eigenschaften und dem Übergehen von Methoden näher eingegangen. Setzen von Inhaltseigenschaften Das Setzen von Inhaltseigenschaften erfolgt, wie bei anderen Eigenschaften auch, durch Zuordnung eines Werts zu der Inhaltseigenschaft. Damit ist nicht etwa die zusammengesetzte Eigenschaft Hauptinhalt ([email protected]:content) gemeint, sondern Eigenschaften mit dem Typ Inhalt ([email protected]: CONTENT) oder Liste von Inhalten ([email protected]:CONTENTLIST). Es gibt dabei zwei mögliche Interpretationen des Werts, einerseits als Dateiname und andererseits als Wert. Wenn nicht anders angegeben, wird zuerst versucht den zugeordneten Wert als Dateinamen zu interpretieren. Wenn es unter dem angegebenen Pfad eine Datei gibt, wird diese mit SetFile der Inhaltseigenschaft zugeordnet. Gibt es an der angegebenen Stelle keine Datei, so wird der Wert in eine temporäre Datei geschrieben (im ANSI-Zeichensatz) und der Inhaltseigenschaft zugewiesen. Diese temporäre Datei wird dabei vom Datenimport automatisch wieder gelöscht. Werden Dateien von der Datenquelle in eine temporäre Datei geschrieben, werden diese Dateien ebenfalls nach der Verarbeitung wieder gelöscht. Dieses Verhalten kann man durch Optionen, die man bei der Zuordnung angibt, modifizieren. Gibt man die Option „Dateiname“ an, so wird der Wert immer als Dateiname interpretiert. Ist an der angegebenen Stelle keine Datei zu finden, dann wird die Eigenschaft nicht gesetzt. Gibt man jedoch die Option „Inhalt als Wert“ an, so wird nicht versucht eine Datei zu finden, sondern der Wert wird über den Umweg einer temporären Datei der Inhaltseigenschaft zugewiesen. Diese Optionen haben auch Auswirkungen auf die Art, wie die Daten von der Datenquelle geholt werden. Sowohl bei OLE DB- als auch bei ODBC-Datenquellen werden Werte, deren Größe 4.000 Zeichen überschreitet, 4. Datenimportobjekt 4.3 Setzen und Ändern von Eigenschaften temporär als Datei abgelegt, da diese meistens Inhaltseigenschaften zugeordnet werden. Diese Dateien können beliebige Länge haben und sie werden binär von der Datenquelle gelesen und in die Datei geschrieben. Verwendet man bei OLE DB-Datenquellen die Option „Inhalt als Wert“, so wird der Wert nicht in einer Datei abgelegt und der Wert – maximal jedoch 500 Kilobyte – als Wert übergeben. Damit können vor allem auch längere Stringlisten auf einmal mit Werten befüllt werden. Aus Gründen der Performance werden Inhaltseigenschaften nicht mit bestehenden Inhalten verglichen, um sie nur bei Änderungen zu setzen, sondern sie werden immer neu gesetzt, je nachdem welcher Änderungsmodus eingestellt ist. Um unnötige Änderungen auf den MMC-Bereichen zu vermeiden empfiehlt es sich daher, die Inhalte nur dann zu setzten, wenn wirklich neue Inhalte darin enthalten sind. Gerade bei MMC-Bereichen mit Logging würde sonst unnötig viel Speicherplatz belegt, der erst bei einem Aufräumen wieder freigegeben würde. In Migrationen kann man durch den Änderungsmodus „Setzen beim Erzeugen“ oft den gewünschten Effekt erzielen, sodass zum Beispiel bei einem nochmaligen Start des Imports die Inhalte bereits bestehender Inhaltsobjekte nicht nochmals gesetzt werden. Inhalte im Archiv Eine sehr effiziente Art Inhalte zu „importieren“ bietet sich im Zusammenhang mit Archivsystemen. Anstatt die Inhalte vom Client auf die Fabasoft Components MMC-Services zu übertragen und dort im Dateisystem abzulegen, werden jene Daten importiert, die die Verbindung von einem Objekt zu einem bereits existierenden Inhalt auf einem Archivsystem darstellen, also quasi nur ein Link auf die eigentlichen Daten. Dadurch müssen die Inhalte nicht mehr während der Migration bewegt werden, sondern können im Vorhinein im Archiv erfasst werden. Die Daten werden dann zur Migration nur mehr über Referenzen eingebunden. Jede Referenz auf einen Inhalt des Archivsystems besteht aus einer Dokumenten-ID und einer Inhalts-ID. Beim Setzen des Inhalts müssen diese beiden IDs gleichzeitig an den Fabasoft Components Kernel übergeben werden, der diese im Objekt hinterlegt und damit die Referenz auf das Archivsystem aufbaut. Wird später auf den Inhalt zugegriffen, erkennt der Fabasoft Components Kernel aufgrund dieser Informationen, dass der Inhalt im Archiv liegt und findet ihn unter der angegeben Dokumenten- und Inhalts-ID. Um die Referenz im Datenimport zu konfigurieren, steht die Eigenschaft Archivposition zur Verfügung. Drei Werte spielen hierbei eine Rolle: 47 Das Archivstore-Objekt, das das Archiv definiert, in dem der Inhalt archiviert wurde, wird direkt in die Eigenschaft Archivstore in der Archivposition eingetragen. Die Dokumenten-ID wird durch die Zuordnung auf die Inhaltseigenschaft definiert. Für die ID des Inhaltes wird ein zweiter variabler Wert benötigt, der aus jener Spalte genommen wird, deren Name in der Archivposition unter Spalte mit ID des Inhalts hinterlegt ist. Mit dieser Methode kann ein Import von Inhaltsobjekten erfolgen, ohne einen einzigen Inhalt zum Zeitpunkt der Migration transferieren zu müssen. Trotz dieses Vorteils habe ich seit 1999 keine Migration betreut, in der diese Vorgehensweise gewählt wurde. Bei einigen Migrationen wurden aber Teile der Datenbestände bereits im Rahmen der Migration oder in einer anschließenden Phase auf Archive ausgelagert. Wie das geht, wird bei den Anwendungsfällen der Skripts näher erläutert. Zeichenkettenlisten Zeichenkettenlisten sind einerseits ganz normale Listen von einzelnen Zeichenketten, wie sie zum Beispiel in der Eigenschaft Log-in-Name ([email protected]:userlogname) der Objektklasse Benutzer verwendet werden. Dort werden die Log-in-Namen des Benutzers – jeder Log-in in einer eigenen Zeile – abgelegt. Andererseits können die Zeichenkettenlisten auch wie eine Inhaltseigenschaft interpretiert und als Text bearbeitet werden. Dafür steht auch im Datenbankschema der Fabasoft Components COO-Services die Möglichkeit zur Verfügung, diese Zeichenkettenlisten in einer Textspalte mit 4.000 Zeichen abzulegen. Verwendet man diese datenbankseitige Abbildung nicht, so können Zeichenkettenlisten theoretisch beliebig groß werden. Weist man in einem Datenimport einer Zeichenkettenliste Werte zu, so erhält man im Grunde dasselbe Verhalten, wie bei anderen Listeneigenschaften. Man kann also den Wert auch zeilenweise zur Zeichenkettenliste hinzufügen. Beinhaltet der Wert jedoch mehrere Zeilen, werden alle diese Zeilen der Eigenschaft zugewiesen. Damit kann man mit einem Datensatz ganze Texte in Zeichenkettenlisten setzten. Hier kommt jedoch das Limit der ODBC- und OLE DB-Datenquellen wieder zum Tragen, die bei Spalten, die länger als 4.000 Zeichen sind, alle Inhalte (egal wie groß die enthaltenen Daten sind) in Dateien ablegen. Überschreitet eine Spalte der Datenquelle dieses Limit, so wird man an Stelle des Wertes einen Dateipfad auf eine 4. Datenimportobjekt 4.3 Setzen und Ändern von Eigenschaften temporäre Datei in der Eigenschaft vorfinden, die im Rahmen des Datenimports wieder gelöscht wurde. Das kann man im Fall von OLE DB- und seit Neuem auch bei ADE DB-Datenquellen verhindern, indem man die Option „Inhalt als Wert“ in der Zeile, in der diese Datenbankspalte steht, angibt. Damit können Werte bis maximal 500 Kilobyte als Text in der jeweilige Spalte verarbeitet und so in eine Zeichenkettenliste geladen werden. Daten, die über die 500-Kilobyte-Grenze hinausgehen, werden dabei jedoch ohne Fehlermeldung abgeschnitten. Bei allen anderen Datenquellen ändert die Option nichts am Verhalten der Datenquelle. Bis Fabasoft Components Version 5.0.2 mussten Zeichenkettenlisten in Listen von Zeichenketten zerlegt werden. Dazu musste die Option „Zeichenkettenliste“ angegeben werden. Es wurde dabei erstens bei Zeilenumbrüchen (CR, LF oder CRLF), zweitens beim letzten Leerzeichen vor der in der Eigenschaft definierten maximalen Länge der Zeichenkette und drittens, falls kein Leerzeichen bis zu dieser Marke enthalten war, genau bei dieser Länge umgebrochen. Ab Version 5.0.3 muss der Wert der Zeichenkettenliste nicht mehr zerlegt werden, da der Fabasoft Components Kernel das selbständig erledigt und auch Zeilen mit einer Länge unterstützt, die größer ist als die maximal eingestellte Zeichenlänge. Zusammengesetzte Eigenschaften Wie bereits erwähnt werden Eigenschaften in zusammengesetzten Eigenschaften (in den Referenzen und in manchen Bezeichnungen findet sich dafür auch noch die frühere Bezeichnung Aggregat) durch den Eigenschaftspfad definiert, also durch die vollständige Liste aller Eigenschaften, die vom Objekt bis zum konkreten Wert führen. 49 Beispiel: Eigenschaftspfad bei Telefonnummerneigenschaft Die Eigenschaft Telefonnummern ([email protected]:telephones) ist eine zusammengesetzte Eigenschaft, die bis zu vier Werte beinhalten kann: ° ° ° ° Typ ([email protected]:teltype) Telefonnummer ([email protected]:telnumber) Beschreibung ([email protected]:teldesc) Telefonnummer (Rohformat) ([email protected]:rawnumber) Um eine Telefonnummer in die Telefonnummern-Eigenschaft zu setzen, muss also als Eigenschaftspfad Telefonnummern.Telefonnummer ([email protected]:[email protected]: telnumber) eingetragen werden. Analog verfährt man mit dem Typ der Telefonnummer ([email protected]: [email protected]:teltype) der in unserem Beispiel mit einem Festwert belegt wird (siehe Abbildung 1). Innerhalb einer Funktion (Kombination von Objektklasse und Objekt-ID) beziehen sich gleiche Attributspfade auch immer auf die gleichen Zeilen in einer Liste. Also ist auch hier sichergestellt, dass sich der Typ und die Telefonnummer auf dieselbe Zeile der Liste der Telefonnummern-Eigenschaften beziehen. Zusammengesetzte Eigenschaften werden befüllt, wenn zumindest einer Eigenschaft ein Wert zugewiesen wird. In unserem Beispiel mit den Telefonnummern wird dem Typ immer der konstante Wert „400“ zugewiesen. Damit wird die Telefonnummern-Eigenschaft prinzipiell immer gesetzt, egal ob in den Quelldaten eine Telefonnummer angegeben ist oder nicht. Daher gibt es die Option „Muss im Aggregat definiert sein“, die bei jener Eigenschaft gesetzt wird, ohne die der Eintrag nicht sinnvoll ist, und sicherstellt, dass eine zusammengesetzte Eigenschaft nur dann angelegt wird, wenn alle Werte, die definiert sein müssen, auch wirklich definiert sind. 4. Datenimportobjekt 4.3 Setzen und Ändern von Eigenschaften Hintergrund: Berücksichtigung von Muss-Eigenschaften Im Datenimport wird die Information aus dem Datenmodell, ob eine Eigenschaft gesetzt werden muss oder nicht, generell nicht beachtet. Diese Information wird normalerweise im Rahmen der Darstellung in der Benutzerschnittstelle verwendet, um unterschiedliche Darstellungen der Eigenschaften zu erreichen und um Eingaben in bestimmte Eigenschaften zu erzwingen. Für das Datenmodell selbst hat diese Information aber keine Bedeutung, es wird also nicht erzwungen, dass in einer Muss-Eigenschaft ein Wert enthalten ist, bevor eine Transaktion abgeschlossen wird. Aber selbst für die Darstellung in der Benutzerschnittstelle kann diese Information überschrieben werden, um in einer konkreten Installation ein anderes Verhalten zu erreichen. Daher kann sich der Datenimport nicht auf diese Information verlassen und sie wird ignoriert. Als Ersatz dafür wurde bei zusammengesetzten Eigenschaften die Option „Muss im Aggregat definiert sein“ eingeführt, um unvollständig definierte Einträge in zusammengesetzten Eigenschaften zu verhindern. Verarbeitung von Listen Das Laden von Listeneigenschaften stellt immer wieder eine Herausforderung dar, da einige Randbedingungen beachtet werden müssen. Mehrere Einträge pro Datensatz Ein typisches Beispiel für einen Datensatz, in dem mehrere Einträge enthalten sind, die auf mehrere Zeilen in einer Liste abgebildet werden, sind Telefonnummern und Faxnummer von Personen oder Organisationen. Im Objektmodell sind diese Nummern als Liste von zusammengesetzten Eigenschaften abgebildet, die neben der Nummer auch noch die Art der Nummer (Typ) beinhaltet, also zum Beispiel ob diese Nummer eine Mobiltelefonnummer, eine Büronummer oder eine Faxnummer ist. In den Quelldaten hat man aber oft in einem einzigen Datensatz zwei oder mehr Spalten mit Telefonnummern und die die Art der Telefonnummer (z.B. „OfficePhone“, „MobilePhone“, „Fax“) wird durch die Spaltenbezeichnung definiert. 51 Nehmen wir an, drei Telefonnummern sollen auf drei Zeilen in der Liste Telefonnummern ([email protected]:telephones) abgebildet werden. Wie im letzten Abschnitt besprochen, wird die Spalte OfficePhone dem Eigenschaftspfad Telefonnummern.Telefonnummer ([email protected]: [email protected]:telnumber) zugeordnet und der Fixwert „400“ für den Typ „Büro“ in der Eigenschaft Telefonnummern.Typ ([email protected]:[email protected]: telnumber) gesetzt. Um die Zeile der Mobiltelefonnummer und der Faxnummer von dieser Zeile unterscheiden zu können, wird auf die anfangs erwähnten Objekt-IDs zurückgegriffen. Wir definieren also quasi eine Person in der Funktion „Person mit Büro-Telefonnummer“, eine weitere mit der Funktion „Person mit Mobiltelefonnummer“ und eine dritte mit der Funktion „Person mit Faxnummer“, also jede mit einer unterschiedlichen ObjektID. Dass es sich dabei eigentlich um dieselbe Person handelt kann man dadurch ausdrücken, dass alle diese Personen dieselben Schlüsselwerte verwenden. Noch eleganter ist es, wenn man die Objekt-IDs aufsteigend vergibt und bei den Personen mit der zweiten und dritten Funktion als Methode zur Vermeidung doppelter Objekte den Eintrag „Verwende das selbe Objekt mit niedrigerer Objekt-ID“ auswählt. Damit muss man nur mehr bei der ersten Funktion die Schlüsseleigenschaften angeben und die geeignete Suchmethode auswählen. Die Objekte der Personen mit den anderen Funktionen sind implizit bekannt und man muss nur mehr, wie in Abbildung 12 zu sehen, die zu setzenden Eigenschaften zuordnen. 4. Datenimportobjekt 4.3 Setzen und Ändern von Eigenschaften Abbildung 12 Definition mehrerer Listeneinträge pro Datensatz Eindeutige Einträge in Liste In vielen Fällen ist es gewünscht, dass in Listen keine doppelten Einträge geladen werden sollen. Das hat auch für den Import große Vorteile. Zum Beispiel vermeidet man damit, selbst wenn ein Import teilweise wiederholt werden muss, dass unerwünschte Einträge in den Listen entstehen. Sind Listen vom Objektmodell her durch das Setzen der Eigenschaft Eindeutige Einträge in Liste des Eigenschaftstyps so definiert, wird diese Einschränkung automatisch vom Datenimport berücksichtigt. Es gibt aber auch Situationen, in denen im Objektmodell Duplikate erlaubt sind, im Datenimport jedoch keine Duplikate eingefügt werden sollen. Dafür gibt es die Option „Eindeutige Einträge in Liste“, die in jener Zeile der Zuordnungen oder Objektbeziehungen definiert wird, in der diese Eigenschaft gesetzt wird. 53 Etwas komplizierter ist das bei Listen zusammengesetzter Eigenschaften. Hier kann neben der Tatsache, dass es sich um eine Liste mit eindeutigen Einträgen handelt, auch noch angegeben werden, welche Eigenschaften zum Vergleich herangezogen werden, welche Eigenschaften also die Schlüsseleigenschaften der Listeneinträge sind. Auch diese können bei zusammengesetzten Eigenschaften bereits im Objektmodell, im Typ der Eigenschaft, in der Eigenschaft Schlüssel für eindeutige Listen ([email protected]:typeuniqueattrs) vorgegeben sein. Ist das nicht der Fall oder sollen andere Eigenschaften zum Vergleich herangezogen werden, so kann man bei den entsprechenden Zuordnungen durch Setzten der Option „Aggregatsschlüssel“ definieren, welche Eigenschaften zum Vergleich mit anderen Einträgen der Liste herangezogen werden sollen. Wenn also angenommen beim Import der Typ der Telefonnummern eindeutig sein soll, also jede Person maximal eine Büronummer, eine Mobiltelefonnummer und eine Faxnummer bekommen soll, dann muss bei jeder Zuordnung zum Typ der Telefonnummer ([email protected]:[email protected]:teltype) die Option „Aggregatsschlüssel“ gesetzt werden, um diese als Schlüsseleigenschaft innerhalb der Liste der zusammengesetzten Eigenschaft Telefonnummern ([email protected]:telephones) zu definieren. Synchronisation von Listen Bisher wurden Einträge immer nur in leere bzw. bereits bestehende Listen eingefügt. Beim regelmäßigem Abgleich von Datenbeständen gibt es jedoch auch Situationen, in denen Einträge aus Listen entfernt werden müssen. Wenn die zu löschenden Einträge in Datensätzen von der Datenquelle geliefert werden, dann kann das Löschen über Skripts realisiert werden (siehe Kapitel „Zusätzliche Beispiele“ auf Seite 159). Eine andere Situation ergibt sich, wenn die Einträge nicht explizit gelöscht werden, sondern die aktuelle Liste einfach neu übertragen wird. 4. Datenimportobjekt 4.3 Setzen und Ändern von Eigenschaften 4.4 Identifikation und Duplikatsprüfung Beispiel: Abgleich der Benutzergruppen über LDAP Beim Abgleich der Benutzerinformationen aus LDAP-Quellen (z.B. Active Directory oder OpenLDAP) wird bei Benutzern jedes Mal die komplette Liste der Gruppen übertragen, in denen der Benutzer enthalten ist. Ändert sich die Zuordnung, so gibt es keine Information darüber, welche Zuordnung sich geändert hat. Nicht mehr bestehende Zuordnungen sind einfach nicht mehr enthalten. Trotzdem sollen die Einträge in der Fabasoft Components Domäne gelöscht werden. Beim Datenimport kann eine Liste über den Änderungsmodus „Löschen und neu anlegen“ neu befüllt werden. Dazu wird der Inhalt der Eigenschaft am Beginn der Transaktion gelöscht und in weiterer Folge die Eigenschaften neu befüllt. Damit fallen alle Werte, die innerhalb der Transaktion nicht mehr eingetragen werden, automatisch aus der Liste heraus. Daraus ergibt sich aber auch eine Einschränkung dieser Option: Die Liste muss innerhalb einer einzigen Transaktion neu befüllt werden. Auch werden die Listen jedenfalls geändert, selbst wenn sich die Werte im Endeffekt nicht verändern. 4.4 Identifikation und Duplikatsprüfung Objektsuche Der primäre Zweck der Klasseneigenschaften ist es, die Suche und das Erzeugen von Objekten zu steuern. Bisher war immer nur von Zuordnungen zu Objekten die Rede. Was aber, wenn zum Beispiel mehrere Personen derselben Organisation zugeordnet werden sollen? Dafür benötigt man einerseits eine oder mehrere Schlüsseleigenschaften, die ein Objekt innerhalb der Objektklasse eindeutig identifizieren, andererseits kann man konfigurieren, wie die Suche nach den Objekten ablaufen soll. Schlüsseleigenschaften Zur Definition der Schlüsseleigenschaften müssen wir noch einmal zu den Zuordnungen der Eigenschaften zurückkehren. Dort gibt es nach dem Festwert eine Aufzählungseigenschaft Schlüssel- und Änderungs- 55 modus ([email protected]:datimpmapkeyupdate). Man macht eine Eigenschaft zu einer Schlüsseleigenschaft indem man den Schlüssel- und Änderungsmodus auf den Wert „Schlüssel“ setzt. Das bewirkt automatisch, dass diese Eigenschaft zur Identifikation der Objekte genau dieser Objektklasse verwendet wird. Unterschiedliche Instanzen derselben Objektklasse, die sich nur durch die Objekt-ID unterscheiden, müssen innerhalb eines Datenimports dieselben Schlüsseleigenschaften haben, da die Schlüsseleigenschaften auch für die interne Verarbeitung verwendet werden. Unterschiedliche Objektklassen können durchaus auch unterschiedliche Schlüsseleigenschaften haben. Zulässige Typen von Eigenschaften sind: ° ° ° ° ° ° Zeichenketteneigenschaften ° ° ° ° Keine Schlüsseleigenschaften können Listeneigenschaften sein, genauso wie Zahleneigenschaften Aufzählungseigenschaften Boolesche Eigenschaften Eigenschaften mit Datum und Zeit Gleitkommazahleneigenschaften (diese sollten jedoch wegen der Rundungsproblematik nicht als Schlüssel verwendet werden) Eigenschaften mit Inhalt Eigenschaften für Werte-Verzeichnisse Objektzeigereigenschaften Eigenschaften in zusammengesetzten Eigenschaften können Schlüsseleigenschaften sein, sofern die zusammengesetzte Eigenschaft nicht eine Liste ist und die Eigenschaft, die den Wert enthält, einen Eigenschaftstyp hat, der eine Schlüsseleigenschaft sein kann und nicht mehrere Werte beinhalten kann. 4. Datenimportobjekt 4.4 Identifikation und Duplikatsprüfung Suchen und Erzeugen von Objekten Für jede Objektklasse kann man einstellen, wie die Objekte dieser Objektklasse im Zuge des Datenimports gesucht werden sollen. Dabei stellt der Datenimport Grundmechanismen zur Verfügung, von denen einige Kombinationsmöglichkeiten auswählbar sind. Diese Mechanismen sind: ° ° Die Suche nach Objekten erfolgt über einzelne Suchabfragen in der Fabasoft Components Domäne. ° ° Dieser Suchbaum kann durch eine Suche über alle Objekte der Objektklasse initialisiert werden. In einem lokalen Suchbaum im Speicher der Anwendung („Cache“) wird zu jedem Objekt sein Schlüsselwert gespeichert. Die Suche nach Objekten erfolgt unter Verwendung der Hashtabelle der Objektklasse. Daraus ergeben sich die in Abbildung 13 dargestellten Suchmethoden, die bei den Klasseneigenschaften in der Eigenschaft Vermeidung doppelter Objekte auswählbar sind. 57 Abbildung 13 Methoden zur Vermeidung doppelter Objekte Keine Prüfung vorhandener Objekte Die Objekte werden immer erzeugt, selbst wenn die Eigenschaft Erzeuge Objekte auf „Nein“ gesetzt wird. Dazu wird keine Schlüsseleigenschaft benötigt. Wenn jedoch eine oder mehrere Schlüsseleigenschaften definiert sind, dann wird ein Objekt nur dann erzeugt, wenn mindestens einer dieser Eigenschaften ein Wert zugewiesen wird. Vorteile: ° ° Bei dieser Variante wird keine Suche durchgeführt, also ist diese Methode die schnellstmögliche. Es wird kein Platz für den Suchbaum benötigt. 4. Datenimportobjekt 4.4 Identifikation und Duplikatsprüfung Nachteile: ° ° Durch die fehlende Suche können Objekte mehrfach geladen werden. Die Fehlerbehandlung ist kompliziert, da durch eine Wiederholung des Ladevorgangs Duplikate entstehen. Prüfung durch Sammeln von Schlüsseln für alle Objekte In einer Initialisierungsphase wird ein lokaler Suchbaum (Cache) aufgebaut, in dem von allen Objekten der Objektklasse die Schlüsselwerte und die Adresse des Objekts gespeichert werden. Während des Ladevorgangs werden neu erzeugte Objekte in den Suchbaum eingetragen. Daher sind immer alle Objekte bekannt und es müssen keine weiteren Suchen erfolgen. Vorteile: ° Die Suche im lokalen Suchbaum ist in der Regel sehr schnell. Nachteile: ° ° Die Initialisierung des Suchbaumes kann lange dauern. ° Durch die Parallelisierung bei der Initialisierung des Suchbaumes können Probleme auftreten, wenn Objekte vieler Objektklassen (mehr als drei) gesammelt werden. Der Speicherverbrauch ist relativ hoch, da auch für Objekte, die vom Ladevorgang nicht betroffen sind, Speicher belegt wird. Der Einsatz dieser Suchmethode ist dadurch auf Objektklassen mit maximal einer Million Instanzen beschränkt. Diese Suchmethode wird also speziell dann verwendet, wenn ein großer Prozentsatz der bestehenden Daten vom Ladevorgang betroffen ist, oder nur wenige Objekte bereits vorhanden sind. So kann die Anzahl der einzelnen Suchen reduziert werden. Der Aufwand der Initialisierung amortisiert sich bald. 59 Prüfung durch Suche für jedes Objekt Bei dieser Suchmethode werden die Objekte bei der ersten Referenzierung über eine Suche in der Fabasoft Components Domäne gesucht. Bereits gefundene oder neu erzeugte Objekte werden im lokalen Suchbaum gespeichert, um bei mehrfacher Verwendung nicht noch einmal gesucht werden zu müssen. Vorteile: ° ° Es wird keine Initialisierungsphase benötigt. Mehrfache Referenzierungen führen zu keinen weiteren Suchen. Nachteile: ° ° ° Die Suche pro Datensatz ist langsam. Der Suchbaum benötigt für jedes verwendete Objekt Speicher. Eine Optimierung der Suche auf der Datenbank ist notwendig. Diese Suchmethode ist die Standardmethode, wenn für eine Objektklasse kein Eintrag in den Klasseneigenschaften angegeben ist. Als Optimierung werden bei der Suche alle Objekte, die in einem Transaktionsblock verarbeitet werden, auf einmal gesucht, sofern bei der Objektklasse nur eine Schlüsseleigenschaft gesetzt ist. Diese Suchmethode sollte speziell dann verwendet werden, wenn bereits viele Objekte in der Fabasoft Components Domäne enthalten sind und nur wenige hinzukommen oder referenziert werden. Prüfung durch Suche für jedes Objekt (kein Cache) Speziell für den Fall, dass sehr viele Objekte geladen werden sollen, gibt es auch die Möglichkeit, den lokalen Suchbaum auszuschalten. So wird vor allem das Speicherproblem des Suchbaumes umgangen. 4. Datenimportobjekt 4.4 Identifikation und Duplikatsprüfung Vorteile: ° ° Keine Initialisierungsphase Minimaler Speicherverbrauch Nachteile: ° ° ° Langsame Suche pro Datensatz Mehrfache Referenzen führen zu mehrfachen Suchen Optimierung der Suche auf der Datenbank notwendig Außer bei sehr großen Importen kann diese Methode auch effizient eingesetzt werden, wenn es keine oder nur wenige mehrfache Referenzen auf Objekte gibt. Prüfung durch Suche über Hashtabelle Bei dieser – in konkreten Projekten kaum verwendeten – Methode wird direkt aus den Werten der Schlüsseleigenschaften ein Hashwert errechnet, aus dem die Objektadresse berechnet wird. Dazu muss zuvor in der Fabasoft Components Domäne eine Hashtabelle für diese Objektklasse angelegt und ein ausreichender Bereich von Objektadressen exklusiv für diese Hashtabelle reserviert werden. Die Objekte der Objektklasse müssen dann immer über bestimmte Funktionen erzeugt werden, die als Parameter bereits die Schlüsselwerte des Objekts enthalten. Diese können dann unter keinen Umständen geändert werden. Vorteile: ° Diese Variante ist in Spezialfällen effizient. Nachteile: ° Das Erzeugen von Objekten ist nur über einen Datenimport oder aus der Anwendungslogik heraus möglich, da spezielle Funktionen zum Anlegen der Objekte verwendet werden müssen. 61 ° ° ° Die Anzahl der Objekte ist durch die Größe der Hashtabelle limitiert. ° Die Schlüsseleigenschaften der Objekte dürfen nicht mehr geändert werden. Durch die Kollisionsbehandlung ist das Anlegen von Objekte bei hohem Füllgrad der Hashtabelle ineffizient. Die Hashtabelle muss während der Erzeugung von Objekten gesperrt werden und der Import kann daher nicht sinnvoll parallelisiert werden. Wegen dieser prinzipiellen Einschränkungen finden die Hashtabellen und damit auch die Suchmethode in Fabasoft Components/COLD kaum Verwendung. Während der Initialisierungsphase des Datenimports wird für jede Objektklasse geprüft, ob eine Hashtabelle definiert ist, um zu verhindern, dass Objekte von Objektklassen mit Hashtabelle ohne Verwendung der Hashtabelle erzeugt werden. Prüfung durch Suche im lokalen Cache Diese Methode verwendet den lokalen Suchbaum, der jedoch nicht durch eine Initialisierung vorher befüllt wird. Objekte die nicht bereits im Suchbaum enthalten sind, werden erzeugt und in den Suchbaum eingetragen. Dadurch wird ein mehrfaches Erzeugen von Objekten in einem Ladevorgang verhindert. Vorteile: ° ° ° Es wird keine Initialisierung benötigt. Es werden keine Suchen pro Datensatz durchgeführt. Die Suchen im lokalen Suchbaum sind sehr schnell. Nachteile: ° ° Objekte, die bereits vorhanden waren, werden nicht gefunden. Die Fehlerbehandlung ist kompliziert, da durch eine Wiederholung des Ladevorgangs Duplikate entstehen. 4. Datenimportobjekt 4.4 Identifikation und Duplikatsprüfung Verwende das selbe Objekt mit niedrigerer Objekt-ID Diese Methode ist nur im Zusammenhang mit einer anderen Suchmethode einsetzbar und ist somit die einzige Variante, bei der in mehreren Instanzen einer Objektklasse unterschiedliche Methoden zur Vermeidung doppelter Objekte verwendet werden dürfen. Die Anwendung wird im Rahmen der Behandlung von Listen besprochen. Suche unter Verwendung der Objektadresse Als Sonderfall kann man die Suche nach Objekten über die Objektadresse ansehen. Dafür definiert man bei den Zuordnungen der Eigenschaft eine Spalte oder einen Festwert, der die Objektadresse (in der Form „COO.<domain-major-id>.<domain-minor-id>.<store-id>.<object-id>“) enthält. Als Suchmethode wählt man „Prüfung durch Suche für jedes Objekt“. Diese Variante ist besonders effizient, da auf das Objekt direkt über seine Adresse zugegriffen wird und keine Suchen benötigt werden. Sie eignet sich einerseits dafür, fixe Objekte über konstante Werte zu hinterlegen, andererseits kann man damit auch sehr effiziente Optimierungen umsetzen, indem man Fremdschlüssel bereits auf der Datenquelle auflöst. Hat man beispielsweise durch einen Datenbankexport die Schlüsseleigenschaften und die Objektadresse aller referenzierten Objekte auf die Quelldatenbank exportiert, so kann man durch einen JOIN zwischen der Tabelle der Quelldaten und der exportierten Tabelle die Objektadressen der referenzierten Objekte ermitteln. Diese Objektadresse kann dann anstelle der Schlüsseleigenschaften zur Bestimmung der referenzierten Objekte verwendet werden. Auf diese Variante wird im Rahmen der Optimierungen genauer eingegangen. Vorteile: ° ° ° Keine Initialisierung Keine Suche pro Datensatz Kein lokaler Suchbaum 63 Nachteile: ° Die Objektadresse muss manuell (bei Konstanten) oder über die Datenbank (JOIN) ermittelt werden Suche unter Verwendung der Referenz Komponentenobjekte werden oft mit ihrer Referenz definiert, die gleichzeitig als Dokumentation dient. Definiert man als Schlüsseleigenschaft eines Komponentenobjekts die Eigenschaft Referenz ([email protected]: reference), so kann auch die komplette Referenz mit Softwarekomponente angegeben werden und es wird diese ohne Suche auf der Datenbank im Client-Cache des Fabasoft Components Kernels aufgelöst, da dieser Cache alle Komponentenobjekte enthält. Als Suchmethode muss auch hier „Prüfung durch Suche für jedes Objekt“ verwendet werden. Diese Suchmethode eignet sich hervorragend zur Definition von fixen Komponentenobjekten (z.B. ACLs, Stellen oder Portalen), die in Eigenschaften anderer Objekte eingetragen werden sollen. Welche Suchmethode soll verwen,erden? Wenn man von der Verwendung spezieller Suchmethoden (Hashtabelle, Objektadresse) absieht, werden in Abbildung 14 die wichtigsten Kriterien zur Auswahl der Suchmethode dargestellt. Die erste Entscheidung hängt davon ab, ob bestehende Objekte geändert oder referenziert werden sollen. Kann beides ausgeschlossen werden, so muss in der Fabasoft Components Domäne nicht gesucht werden. In diesem Fall kann entweder „Keine Prüfung vorhandener Objekte“ ausgewählt werden oder, sofern Objekte innerhalb des Imports mehrmals verwendet werden sollen, die „Suche im lokalen Cache“. Falls bestehende Objekte verwendet werden sollen, muss zuerst geklärt werden, ob die Verwendung eines Suchbaumes zulässig ist, was vor allem vom zur Verfügung stehenden Speicher abhängt. Es sollten dabei nicht mehr als etwa eine Million Objekte im Suchbaum gespeichert werden. Kann ein Suchbaum verwendet werden, hängt die Suchmethode noch vom Verhältnis der referenzierten zu den existierenden Objekten ab. Wird nur ein kleiner Teil der Objekte verwendet, so zahlt sich das „Sammeln der Schlüsseln für alle Objekte“ nicht aus und es sollte die „Suche für jedes Objekt“ verwendet werden. Die endgültige Entscheidung hängt aber auch von vielen 4. Datenimportobjekt 4.4 Identifikation und Duplikatsprüfung anderen Faktoren, wie zum Beispiel der Verwendung von Datenbankindizes oder der Komplexität der ACLPrüfung ab. Daher muss diese Entscheidung für jeden Anwendungsfall neu verifiziert werden. Abbildung 14 Auswahl der Suchmethoden Wenn von einer Objektklasse nur wenige 100 Instanzen referenziert werden, sollte der Defaultwert „Prüfung durch Suche für jedes Objekt“ verwendet werden. Mögliche Probleme bei Schlüsseleigenschaften Get-Aktionen Get-Aktionen bei Schlüsseleigenschaften können zur Folge haben, dass der Wert auf der Datenbank nicht mit jenem Wert übereinstimmt, der beim Lesen der Eigenschaft des Objekts berechnet wird. Daher wird die Methode „Prüfung durch Suche für jedes Objekt“ möglicherweise nicht zum selben Ergebnis kommen, wie die „Prü- 65 fung durch Sammeln von Schlüsseln für alle Objekte“, da im einen Fall auf der Datenbank gesucht wird, im anderen Fall der von der Get-Aktion berechnete Wert verwendet wird. Solche Situationen sind aber nicht nur für den Datenimport problematisch, sondern auch für die normale Suche in der Benutzerschnittstelle. Set-Aktionen Wenn der Wert einer Schlüsseleigenschaft in einer Set-Aktion geändert wird, wird diese Änderung im lokalen Suchbaum nicht berücksichtigt und die Suche kann unvorhersehbare Resultate liefern. Beispielsweise kann dadurch ein Objekt innerhalb eines Datenimports öfters referenziert werden und beim nächsten Datenimport wird es auf einmal nicht mehr gefunden, da der Schlüsselwert jetzt auf Basis des Werts der Eigenschaft des Objekts bestimmt wird, der aber durch die Set-Aktion nicht mehr dem ursprünglich gesetzten Wert entspricht. Speicher Auf 32-Bit-Betriebssystemen kann ein Prozess maximal drei Gigabyte Speicher direkt adressieren. Das ist bei der Verwendung von lokalen Suchbäumen insofern zu berücksichtigen, als pro Eintrag mit ca. 40 Byte plus der Länge des Werts der Schlüsseleigenschaft zu rechnen ist. Wenn der Import auf 64-Bit-Systemen durchgeführt wird, sind die Speichergrenzen zwar nicht relevant, jedoch sind durch die größeren Pointer 12 Byte mehr pro Objekt zu berechen. Optimierung der Suche Es ist vorteilhaft nur eine Schlüsseleigenschaft pro Objektklasse zu verwenden, da in diesem Fall die Suche effizienter ist und weniger Zugriffe auf Eigenschaften gemacht werden müssen. Bei den Suchmethoden, die pro Objekt suchen, sollte diese Eigenschaft auf der Datenbank mit einem Index auf den Wert versehen sein. Besonders gut geeignet sind die Eigenschaften Externer Schlüssel ([email protected]: objexternalkey) und Betreff ([email protected]:objsubject) falls diese nicht anderwärtig verwendet werden. 4. Datenimportobjekt 4.4 Identifikation und Duplikatsprüfung Die Eigenschaft Externer Schlüssel ist speziell für solche Zwecke definiert worden und hat nur den Nachteil, dass die Werte auf der Datenbank in der generischen Tabelle atstrval stehen, wie die Werte vieler anderer Eigenschaften auch. Daher ist diese Eigenschaft nicht so effizient indizierbar. Das lässt sich aber bei Bedarf durch die Implementierung geeigneter Tabellendefinitionen auf der Datenbank und entsprechender Indizierung entschärfen. Die Eigenschaft Betreff wird in vielen Formularen angezeigt, eignet sich daher nur dann, wenn die Schlüsselwerte „lesbare“ und für den Benutzer relevante Information beinhalten. Die Werte der Eigenschaft finden sich in der Tabelle cooobject und die Suche ist nach der Definition eines Indexes auf diese Spalte sehr effizient. Da die Suche in der Regel neben der Schlüsseleigenschaft auch die Objektklasse als Bedingung enthält, kann sie durch die Verwendung eines kombinierten Indexes nach der Objektklasse und der Schlüsseleigenschaft weiter optimiert werden. Welcher Index im konkreten Fall optimal ist, kann – sieht man von der Definition von Tabellen ab – mit den Analysewerkzeugen der Datenbank ermittelt werden. Suchbereiche Suchbereiche dienen primär der Einschränkung der Suche auf bestimmte Fabasoft Components Domänen, Fabasoft Components COO-Stores oder Fabasoft Components COO-Services. Einerseits kann damit die Suche auf jene Services eingeschränkt werden, die auch tatsächlich Daten der betreffenden Objektklasse beinhalten, andererseits kann man aber auch die Suche auf bestimmte Mandanten oder Ausschnitte der Objektmenge beschränken. Wegen der gemeinsamen Verwaltung der Schlüssel einer Objektklasse kann auch der Suchbereich nur pro Objektklasse und nicht pro Funktion eingestellt werden. Werden unterschiedliche Suchbereiche bei einer Objektklasse definiert, ist das Resultat nicht definiert. Eine wenig bekannte Möglichkeit bietet die Verwendung von Suchbereichen beim Erzeugen von Objekten: Wird genau ein Fabasoft Components COO-Store im Suchbereich definiert, so werden neue Objekte dieser Objektklasse genau auf diesem Fabasoft Components COO-Store erstellt. Es wird also die Objektplatzierung überschrieben. Das kann einerseits dafür verwendet werden, dass bei Migrationen die Änderungen auf genau definierten Fabasoft Components Services vorgenommen werden und andere Fabasoft Components Services nicht berührt 67 werden, wodurch eine Trennung der Datenbestände erreicht werden kann. Andererseits kann diese Trennung auch für explizite Verteilung der Last auf mehrere Fabasoft Components Services verwendet werden, wenn zum Beispiel ein Datenbestand auf mehrere Datenpakete aufgeteilt wird und von mehreren Clients aus möglichst schnell in eine Fabasoft Components Domäne eingespielt werden soll. Hintergrund: DesignPatterns 2001 Schon bei den Fabasoft DesignPatterns im Jahr 2001 wurde in einer Demo der Import von 10 Mio. Objekten in einer Stunde live vorgeführt. Dafür wurden die Quelldaten auf fünf Clients gleichmäßig aufgeteilt. Bei der Optimierung stellte sich heraus, dass die Fabasoft Components COO-Services, vor allem aufgrund von Datenbank-Locks, den geforderten Durchsatz nicht erreichen konnten. Was mit einem einzelnen Datenservice aber auch mit einer zufälligen Verteilung der Objekte über die Objektplatzierung nicht erreicht werden konnte, ließ sich durch die Implementierung einer Objektplatzierung über die Suchbereiche bewerkstelligen, indem in jedem der fünf verwendeten Datenimportobjekte ein anderer Suchbereich und damit ein anderes Service zugeteilt wurde. So optimiert konnte der Import auf der im Vergleich zu heutigen Rechnern langsamen Hardware in der geforderten Zeit durchgeführt werden. Objektsperren Objektsperren dienen der Synchronisation von Änderungen an einem Objekt und sorgen dafür, dass jedes Objekt von maximal einer einzigen Transaktion modifiziert wird. Dieser Mechanismus benötigt erheblich viel Zeit, da für jede Sperre ein RPC an das betreffende Fabasoft Components COO-Service abgesetzt wird und von diesem in einer eigenen Transaktion die Sperre auch in der Datenbank eingetragen wird. Beim Lösen der Sperre werden nach dem Commit alle Objektsperren eines Fabasoft Components COO-Services in einer Transaktion wieder gelöscht. Sind die Objektsperren aktiviert, kann es dadurch natürlich auch zu dem Fall kommen, dass ein Objekt bereits in einer anderen Transaktion gesperrt ist. In diesem Fall werden vom Datenimport keine Änderungen auf diesem Objekt durchgeführt, Änderungen an anderen Objekten werden jedoch ganz normal durchgeführt. Der Datensatz wird zur Fehlerbehandlung in das 4. Datenimportobjekt 4.4 Identifikation und Duplikatsprüfung 4.5 Parameter der Verarbeitung Redo-Log geschrieben und damit können mit einem Roll-Forward die fehlenden Änderungen zu einem späteren Zeitpunkt nachgetragen werden. Gerade bei großen Datenübernahmen kann man organisatorisch (z.B. durch ein Wartungsfenster) dafür sorgen, dass gleichzeitig mit dem Datenimport keine Änderungen auf den betroffenen Objekten durchgeführt werden. Daher werden beim Datenimport die Objektsperren nicht verwendet, außer man schaltet diese explizit bei den Klasseneigenschaften durch das Setzen der Eigenschaft Objekte sperren ([email protected]: datimplockobjects) auf den Wert „Ja“ ein. 4.5 Parameter der Verarbeitung Transaktionen und Threads Während das Lesen der Quelldaten immer sequenziell von einem einzelnen Thread durchgeführt wird, werden Änderungen in einem oder mehreren parallelen Worker Threads durchgeführt (siehe Abbildung 4). Dazu wird vom Reader Thread eine bestimmte Anzahl von Datensätzen in einen Datenblock zusammengefasst, der jeweils an einen einzelnen Worker Thread zu Verarbeitung übergeben wird. Dieser Worker führt daraufhin die Änderungen aller Datensätze eines Blockes in einer Transaktion durch und führt das Commit dieser Transaktion durch. Das dient vor allem dazu, den Overhead, der für jede Transaktion notwendig ist, möglichst klein zu halten. Daneben kann dieser Mechanismus auch gezielt bei der Optimierung eingesetzt und auf die jeweilige Situation abgestimmt werden. Parallele Threads Ein wichtiger Faktor beim optimalen Tuning von Datenimporten ist die Verwendung von Parallelität bei der Verarbeitung von Datenimporten. Bei der Verwendung von parallelen Threads werden die Datenblöcke, die vom Reader sequentiell erstellt werden, von einer fixen Anzahl von Worker Threads aus einer Queue gelesen. Jeder Worker Thread nimmt sich also genau einen Datenblock von der Queue und verarbeitet diesen vollständig bevor er den nächsten Datenblock aus der Queue entnimmt. 69 Die Verarbeitungs-Threads laufen parallel, sofern sie nicht durch den Zugriff auf gemeinsame Ressourcen synchronisiert werden. Zu den gemeinsamen Ressourcen zählen ° ° ° ° ° Zu modifizierende Objekte Nummeratoren Hashtabellen Skripts vor und nach dem Commit Fabasoft Components Kernel Der Fabasoft Components Kernel stellt für jede Verarbeitung eine zentrale Ressource dar und führt in vielen Fällen auch dazu, dass auch auf Multiprozessorsystemen maximal jene CPU-Last vom importierenden Prozess verbraucht wird, die einer einzelnen CPU entspricht. Während der Ausführung von RPCs zu Fabasoft Components COO- und Fabasoft Components MMC-Services kann jedoch der Fabasoft Components Kernel von einem anderen Thread verwendet werden. Daher kann besonders in den Fällen, bei denen die Suche nach Objekten oder das Schreiben der Daten auf der Datenbank erhebliche Zeit in Anspruch nimmt, diese Zeit am Ladeclient von anderen parallelen Worker Threads genutzt werden. Die Skripts werden später in einem eigenen Abschnitt behandelt. Aufgrund von Einschränkungen der Scripting Engine wird die Verarbeitung der Skripts serialisiert. Die wichtigste Einschränkung ist aber jene der zu ändernden Objekte des Datenmodells. Die Einschränkung lautet, dass ein Objekt nicht gleichzeitig in mehreren Transaktionen geändert werden darf. Normalerweise dient der Mechanismus der Objektsperre dazu, solche Zugriffe zu verhindern. Doch wenn die Objektsperren wegen des großen Ressourcenbedarfs nicht verwendet werden, muss garantiert werden, dass innerhalb eines Datenimports mehrere Worker Threads nicht gleichzeitig dasselbe Objekt ändern. Daher gibt es einen internen Synchronisationsmechanismus, der das verhindert. Zuerst wird dabei festgestellt, ob ein Objekt in der Transaktion potenziell erzeugt oder verändert wird. Dazu werden alle Zuordnungen herangezogen, die Veränderungen hervorrufen können. Zudem werden bei Rückwärtsverkettungen beide Richtungen der Zuweisung berücksichtigt. Nicht berücksichtigt können jene Änderungen wer- 4. Datenimportobjekt 4.5 Parameter der Verarbeitung den, die von Methoden durchgeführt werden, da diese Änderungen nicht explizit modelliert sind und daher dem Datenimport nicht bekannt sind. Von jenen Objektklassen, deren Objekte potenziell verändert werden, wird im Rahmen vor dem Suchen bzw. Erzeugen der Objekte eine Liste der verwendeten Schlüssel geführt und jeder Schlüssel kann dabei exklusiv nur von einem Thread verwendet werden. Trifft ein Thread auf ein bereits gesperrtes Objekt wird die Ausführung dieses Threads solange angehalten, bis das betroffene Objekt wieder freigegeben wird, also bis der blockierende Thread die Verarbeitung des aktuellen Datenblocks beendet hat. Eine andere häufig anzutreffende Einschränkung ist die Vergabe von Nummeratorwerten, also die aufsteigende Nummerierung von Objekten, sei es über einfache Nummeratoren oder Schlüssel-Nummeratoren. Während der Schlüsselvergabe werden die Nummeratorobjekte gesperrt und diese Sperre wird erst nach dem Commit der aktuellen Transaktion wieder aufgehoben. Daher steht der Nummerator nur einem Thread zur Verfügung. Der Auflösung dieses Problems ist ein eigener Abschnitt dieses Buches gewidmet. Hashtabellen müssen beim Erzeugen von Objekten wegen der Kollisionsbehandlung exklusiv gesperrt werden, bis die Objekte auf die Datenbank persistiert sind und führen daher auch zu einer Serialisierung der Verarbeitung. Diese kann nicht umgangen werden. Gruppenwechsel Oft werden einzelne Objekte durch mehrere Datensätze verändert (z.B. beim Setzen von Listen). In der Verarbeitung bringt es Vorteile, wenn Objekte in möglichst wenigen Transaktionen geändert werden. Einerseits müssen dann weniger Änderungen geschrieben werden, da weniger unvollständige Stände des Objekts committet werden, andererseits kann dadurch die Parallelität erhöht werden, da ein Objekt zu jedem Zeitpunkt nur an einer einzigen Transaktion beteiligt sein darf. Daher wurde die Option „Gruppenwechsel“ implementiert, die bei den Zuordnungen von Datenbankspalten angegeben werden kann. Damit wird die Anzahl der Datensätze, die vom Reader Thread in einen Datenblock gegeben wird, dynamisch verändert. Das wird erreicht, indem die Änderung einer oder mehrerer Datenbankspalten (im Normalfall die Schlüsselspalten eines zu ändernden Objektes) bewirken, dass ein Datenblock vom Reader Thread nicht mehr weiter befüllt wird und zur Weiterverarbeitung an einen Worker Thread gegeben wird. 71 Für die Blockgröße werden eine minimale und eine maximale Größe angegeben. Jeder Datenblock wird jedenfalls bis zur minimalen Größe befüllt und dann werden weitere Datensätze hinzugefügt bis entweder die maximale Blockgröße erreicht ist oder sich der Wert mindestens einer der Spalten, bei denen die Option gesetzt ist, verändert. Voraussetzung für den effizienten Einsatz dieser Option ist jedoch, dass die Datensätze sortiert von der Datenquelle geliefert werden, was bei manchen Datenquellen (z.B. LDAP) implizit der Fall sein kann, bei anderen Datenquellen (z.B. Datenbanken, die über OLE DB angebunden sind) über SQL-Anweisungen mit einem ORDER BY explizit herbeigeführt werden muss. Ist das für die Spalten mit Gruppenwechsel gewährleistet, so kann man garantieren, dass alle Datenbereiche mit gleichen Gruppenwechsel-Spalten, die nicht mehr Datensätze umfassen als Maximale Blockgröße minus Minimale Blockgröße in einem Block und somit in einer Transaktion verarbeitet werden. Werden Änderungen also vor allem an einer Objektklasse durchgeführt, dann empfiehlt es sich den Gruppenwechsel dazu zu verwenden, möglichst alle Änderungen eines Objekts in einer einzelnen Transaktion durchzuführen. Speziell bei der Synchronisation von Listen ist dieser Mechanismus unerlässlich, da der Synchronisationsmechanismus voraussetzt, dass die Liste in einer einzigen Transaktion wieder aufgebaut wird. Übergehen von Methoden Das Übergehen von Methoden ist eine mächtige Tuningmaßnahme, die nur mit großer Vorsicht verwendet werden darf. Worum geht es dabei? In Fabasoft Components werden sowohl beim Erzeugen von Objekten als auch beim Commit der Transaktion Methoden auf die einzelnen Eigenschaften und auf alle erzeugten bzw. geänderten Objekte ausgeführt. In diesen Methoden ist ein Teil der Anwendungslogik implementiert und daher benötigen diese Methoden oft sehr viel Zeit. Bei Migrationen ist diese Anwendungslogik jedoch oft nicht notwendig, da etwa Konsistenzprüfungen bereits vor dem Datenimport durchgeführt oder andere Berechnungen bereits im Rahmen des Imports gemacht werden können (z.B. die Zusammensetzung des Objektnamens aus einzelnen Eigenschaften). In diesen Fällen kann also die Anwendungslogik durch Logik des Datenimports ersetzt werden. In manchen Fällen müssen die 4. Datenimportobjekt 4.5 Parameter der Verarbeitung Methoden während der Migration überhaupt unterdrückt werden, da sie unerwünscht sind, wie zum Beispiel der Eigenschaftskonstruktor der Eigenschaft Prozesse ([email protected]:workflow), der beim Erzeugen des Objekts einen definierten Workflow instanziert. Folgende Aktionen können übergangen werden: ° ° ° ° ° Die Konstruktoren von Eigenschaften Die Aktion vor dem Schreiben der Eigenschaften (Set-Aktionen) Der Objektkonstruktor ([email protected]:ObjectConstructor) Die Aktion vor dem Schreiben der Objekte ([email protected]:ObjectPrepareCommit) Die Aktion nach dem Schreiben der Objekte ([email protected]:ObjectCommitted) Damit kann man meistens alle relevanten Methoden, die im Rahmen der Erstellung der Objekte aufgerufen werden, deaktivieren. Die Deaktivierung wird durch die Registrierung von dynamischen Pre-Wrappern implementiert, die vor der Ausführung der eigentlichen Methoden den Fehlerstatus [email protected]:COOSTERR_BREAK setzen, wodurch der Fabasoft Components Kernel die Verarbeitung der Methode abbricht. In seltenen Fällen ist bei Aktionen eine der Eigenschaften Keine Wrapper-Methoden ausführen oder Muss ausgeführt werden auf „Ja“ gesetzt, dann kann auch die Ausführung vom Datenimport nicht verhindert werden. Methoden ohne Prüfung übergehen? Im dynamischen Pre-Wrapper wird normalerweise geprüft, ob es sich bei der aufgerufenen Methode wirklich um die Methode der richtigen Eigenschaft bzw. Objektklasse handelt. Sonst würden zum Beispiel bei [email protected]:ObjectPrepareCommit die Aufrufe bei allen Objektklassen deaktiviert. Wenn aber selbst diese Überprüfung zu aufwendig ist oder der erwähnte Seiteneffekt gewünscht ist, kann durch das Setzen von Methoden ohne Prüfung übergehen? auf „Ja“ diese Prüfung deaktiviert werden. Diese Option sollte aber nur von Experten und mit entsprechender Vorsicht eingesetzt werden. 73 4.6 Importoptionen und Protokoll Protokoll: Modus, Objekt, max. Einträge Während des Datenimports besteht die Möglichkeit ein Protokoll der Datensätze, der beteiligten Objekte und der Fehler zu erstellen. Es kann dabei zwischen ° ° ° „Kein Protokoll“ „Protokolliere Fehler“ und „Vollständiges Protokoll“ gewählt werden, je nachdem, ob überhaupt eine Protokolldatei angelegt werden soll, ob darin nur die Fehler enthalten sein sollen oder ob alle Datensätze im Protokoll enthalten sein sollen. Wenn das „Vollständige Protokoll“ ausgewählt ist, dann werden die Daten aller Quelldatensätze und zusätzlich die Objektadressen aller erzeugten oder geänderten Objekte ins Protokoll geschrieben. Genauso wie bei „Protokolliere Fehler“ werden auch die Fehlermeldungen im Protokoll vermerkt. Dieses Protokoll wird während des Datenimports auf eine lokale Datei geschrieben, die nach Verarbeitung aller Datensätze an die Liste der Protokolle im Protokoll-Objekt gemeinsam mit den Statistikinformationen angehängt wird. Wird ein vollständiges Protokoll oder ein Fehlerprotokoll geschrieben, so ist in den Skripts (Filterskript für Rohdaten, Skript für Objekte und Skript für Objekte nach dem Commit) ein COM-Objekt definiert, das erlaubt, eigene Informationen ins Protokoll zu schreiben. Änderungen sofort anzeigen Diese Option stammt noch aus den Zeiten des Fabasoft Win32-Clients, bei dem nach dem Commit eines Objekts auch alle angezeigten Fenster von Änderungen verständigt wurden und diese entsprechend aktualisiert wurden. Um diesen Vorgang zu vermeiden, wird bei Datenimporten standardmäßig die Transaktionsvariable 4. Datenimportobjekt 4.6 Importoptionen und Protokoll 4.7 Skripts TV_NOUIREFRESH der Softwarekomponente [email protected] gesetzt. Bei Datenimporten, bei denen der Datenimport vom Web- oder AT-Service durchgeführt wird, hat diese Option keinerlei Auswirkungen. Objekte nicht aktualisieren Jedes Objekt, das von einer Transaktion betroffen ist, wird normalerweise – sofern es sich nicht um Komponentenobjekte handelt – auf seine Aktualität hin geprüft. Dafür wird beim entsprechenden Fabasoft Components COO-Service das Änderungsdatum des Objekts ausgelesen. Kann man sicherstellen, dass während des Datenimports keine Objekte auf anderen Fabasoft Components Kernel-Instanzen geändert werden, die später noch einmal vom Datenimport betroffen sind, dann kann man auf diese Aktualisierung verzichten, indem man diese Eigenschaft auf den Wert „Ja“ setzt und erreicht damit eine erhebliche Performancesteigerung. In Version 7 ist ein neuer Mechanismus zur Verteilung von Änderungsmeldungen implementiert, bei dem die Fabasoft Components COO-Services, wenn ein Objekt in einer Transaktion modifiziert wurde, aktiv über Multicast-Messages alle Fabasoft Components Kernel-Instanzen von Änderungen der Objekte benachrichtigen. Wird dieser Mechanismus verwendet, so kann der Fabasoft Components Kernel entscheiden, ob ein Objekt im Cache noch aktuell ist, ohne jedes Mal Aktualisierungsanfragen an das Fabasoft Components COO-Service stellen zu müssen. Daher zeigt diese Option hier kaum Auswirkungen. 4.7 Skripts In die Verarbeitung der Datenimporte kann an drei Stellen durch Verwendung von Skripts eingegriffen werden. Diese Skripts können unter Microsoft Windows als VBScript oder JavaScript implementiert werden, unter Linux jedoch ausschließlich als JavaScript. Der globale Kontext der Skripts wird beim Start des Datenimports einmalig aufgerufen. Dort können globale Variablen definiert werden oder Initialisierungen vorgenommen werden. Danach wird für jeden Datensatz eine Funktion Main (bzw. beim Filterskript für Rohdaten alternativ MainEx) aufgerufen, der die entsprechenden Parameter übergeben werden. 75 Die MainEx-Variante dieser Funktion wurde deshalb implementiert, weil mit dem zunehmenden Einsatz von Linux die Einschränkung auf VBScript zur Implementierung der Skripts beseitigt wurde. Diese Einschränkung war eine Folge der Verwendung von SafeArrays zur Parameterübergabe im Filter für Rohdaten. Filterskript für Rohdaten Das Skript für Rohdaten dient der Berechnung von Werten auf Basis der Eingangsdaten aus der Datenquelle. Hier können auch Kriterien definiert werden, um gezielt Datensätze herauszufiltern. Dieses Skript wird vom Reader Thread ausgeführt, wobei beim Start des Imports einmalig der globale Skript-Kontext ausgeführt wird und dann pro Datensatz, der von der Datenquelle geliefert wird, die Funktion Main bzw. alternativ dazu die Funktion MainEx aufgerufen wird. Dieser Funktion werden die Werte der Datenquelle übergeben und diese können in der Funktion geändert werden. Zusätzlich zu den „normalen“ Datenbankspalten, die von der Datenquelle stammen, gibt es noch die Möglichkeit zur Verwendung „berechneter“ Spalten, also Spalten, die nicht von der Datenquelle befüllt werden, sondern von diesem Skript gesetzt werden. Diese berechneten Spalten können bei den Zuordnungen genauso wie die eigentlichen Datenbankspalten verwendet werden. Zur Unterscheidung zwischen Datenbankspalten und berechneten Spalten dient ein „@“ am Beginn der berechneten Spalten. Die Funktion Main(columns, data, changed, skip) ist die ursprüngliche Form der Funktion, die in diesem Skript ausgeführt wird. Bei dieser Funktion wird als erster Parameter (columns) ein 0-basiertes Array von Zeichenketten übergeben, in dem die Spaltennamen aller bei den Zuordnungen verwendeten Spalten enthalten sind. Einträge, die in der Tabelle der Zuordnungen mehrfach verwendet werden, sind hier nur einmal enthalten. Neben den Datenbankspalten sind hier auch die berechneten Spalten enthalten, also alle Einträge, die in der Tabelle Zuordnungen in der Eigenschaft Spalte enthalten sind, selbst wenn die Zeile den Schlüssel- und Änderungsmodus „Ignorieren“ hat. Diese Spaltennamen hängen mit dem zweiten Parameter – den Daten – zusammen, in dem der Wert der jeweiligen Spalte unter demselben Index im Parameter data zu finden ist, wie der Spaltenname im Parameter columns. Der Parameter data ist also auch ein Array – wieder von Zeichenketten – in dem die Daten enthalten sind. NULL-Werte werden durch leere Zeichenketten abgebildet, sodass kein Unterschied zwischen einem leeren String und einem nicht vorhandenen Wert gemacht wird. Berechnete Spalten sind ebenfalls mit Leerstrings initialisiert. Ändert man etwas an den Daten, befüllt man also 4. Datenimportobjekt 4.7 Skripts zum Beispiel eine berechnete Spalte mit einem Wert, so muss man darauf achten, dass wieder nur Zeichenketten an den richtigen Index im Parameter data geschrieben werden. Außerdem werden Änderungen nur dann übernommen, wenn der Parameter changed auf „True“ gesetzt wird. Wird changed auf dem Initialisierungswert „False“ belassen, werden die Änderungen ignoriert. Außerdem können in diesem Skript Datensätze gänzlich von der weiteren Verarbeitung ausgeschlossen werden, indem der Parameter skip auf den Wert „True“ gesetzt wird. In der Funktion MainEx(params) werden diese Parameter nicht in Form von Arrays bzw. booleschen Variablen, sondern als DICTIONARY übergeben, das alle diese Parameter beinhaltet. Dies ist notwendig, da JavaScript das Schreiben von SafeArrays nicht unterstützt und es damit unter Linux, wo ja VBScript nicht zur Verfügung steht, nicht möglich war, ein Filterskript zu schreiben, das Rohdaten berechnet oder verändert. Nebenbei lässt sich damit auch wesentlich kompakterer Skript-Code schreiben, da das mühsame Suchen nach dem richtigen Index einer Spalte entfällt. Beispiele dafür finden sich im Kapitel „Zusätzliche Beispiele“ auf Seite 159. Filter für Objekte Es gibt zwei Filter für Objekte, die sich einzig durch den Zeitpunkt des Aufrufes innerhalb der in Abbildung 15 beschriebenen Verarbeitungsfolge eines Datenpaketes im Worker Thread unterscheiden. Wie beim Filterskript für Rohdaten wird der globale Kontext des Skripts auch bei diesen Skripts bei der Initialisierung des Datenimports einmalig ausgeführt. Später wird dann pro Datensatz einmal die Funktion Main aufgerufen. Im Unterschied zum Filterskript für Rohdaten werden jetzt aber nicht die Quelldaten an das Skript übergeben, sondern die an dem Datensatz beteiligten Objekte. Als Parameter werden drei Arrays übergeben, die – wiederum über den Array-Index zusammenhängend – die Referenz der Objektklasse, die Objekt-ID und das konkrete Objekt beinhalten. Man verwendet also die Referenz der Objektklasse und die Objekt-ID um den richtigen Index in der Objektliste zu finden und kann dann direkt auf den Wert zugreifen. Beachten muss man jedoch, dass der Wert des Objekts nicht in jedem Fall gesetzt sein muss. Wird ein Objekt nicht gefunden und auch nicht erzeugt oder kann es (falls das konfiguriert ist) nicht gesperrt werden, so ist der Eintrag in der Objektliste leer. Daher sollte man gerade bei der Verwendung von VBScript, wegen dessen anachronistischen Verwendung von 77 Zuweisungen zu Variablen, zuerst mit einer Abfrage (z.B. mit IsObject(value(n))) feststellen, ob in der betreffenden Zelle ein Wert enthalten ist, bevor man diesen einer Variable zuweist oder anderweitig verwendet. In JavaScript verwendet man dafür einen Vergleich auf != null. Abbildung 15 Verarbeitung der Datensätze im Worker Thread Hat man die betreffenden Objekte identifiziert und ihre Gültigkeit festgestellt, so kann man darauf Aktionen ausführen oder Eigenschaften lesen und setzten. Man kann dabei auch beliebige Objekte verwenden, die sonst nicht im Datenimport verwendet werden, wobei immer darauf geachtet werden muss, dass man dazu nicht die globale Transaktion cootx verwendet, sondern die als vierten Parameter übergebene Transaktion threadtx, denn das ist jene Transaktion, in der die Objekte gesucht, erzeugt und verändert wurden. Neu erzeugte Objekte sind nur in dieser Transaktion bekannt und die Verwendung einer anderen Transaktion könnte daher zu Problemen und Inkonsistenzen führen. 4 Datenimportobjekt 4.7 Skripts Als Anwendungsfälle für das Filterskript für Objekte seien die Versionierung und die Protokollierung von Änderungen genannt. Dazu finden Sie auch Beispiel-Skripts bei den Konfigurationsbeispielen im Kapitel „Zusätzliche Beispiele“ auf Seite 159. Filter für Objekte nach dem Commit Gleich wie das vorhergehende Skript wird auch beim Filterskript für Objekte nach dem Commit pro Datensatz die Main-Funktion mit denselben Parametern aufgerufen. Jedoch nur dann, wenn das Commit der Transaktion erfolgreich durchgeführt werden konnte. Als Anwendungsfall ist im Kapitel „Zusätzliche Beispiele“ auf Seite 159 ein Skript für die Archivierung der Inhalte der Objekte hinterlegt. Man kann aber genauso gut auch hier Protokollierungen durchführen oder – wie bereits einmal im Rahmen eines periodischen Abgleichs durchgeführt – den Datensatz, der in dieser Datenzeile verarbeitet wurde, in einer Datenbank als verarbeitet markieren. Grundsätzlich sollte man jedoch alle diese Skripts mit großer Vorsicht einsetzen, da Skripts selbst und oft auch die darin aufgerufenen Funktionen (z.B. die Versionierung oder Archivierung) langsam sind und den Datendurchsatz des Datenimports erheblich mindern. Durch eine unsachgemäße Verwendung von Skripts sind schon ganze Migrationen gefährdet worden, indem sie derart ineffizient eingesetzt wurden, dass die Migration in der zur Verfügung stehenden Zeit nicht mehr durchführbar war. In einem Fall konnte durch ein Redesign der Filterskripts die Verarbeitung um den Faktor 100 beschleunigt werden. 79 5 Was man unbedingt 5 beachten sollte … 5.1 Nummeratoren Nummeratoren sind Zahleneigenschaften, die automatisch mit sequenziell aufsteigenden Werten befüllt werden. Eine Möglichkeit ist, diese Zahlen global zu vergeben, wie zum Beispiel bei der Eigenschaft Einfache Namenszahl ([email protected]:nameid) der Objektklasse Einbringer ([email protected]: Applicant), durch die jeder Einbringer eine eindeutige ID erhält. Die andere Variante sind Schlüssel-Nummeratoren, bei denen die Zahlen für jeden Wert der für diesen Nummerator definierten Schlüsseleigenschaften getrennt vergeben werden. Prominentes Beispiel dafür ist die Eigenschaft ESt-Nr ([email protected]:incnr) der Eingangsstücke ([email protected]:Incoming), die pro Jahr vergeben wird. Hier wird in Abhängigkeit vom Wert der Eigenschaft Jahr ([email protected]:year) ein anderer Zähler zur Vergabe der Nummer verwendet. Während beim Nummerator der Wert beim Erzeugen der Eigenschaft vergeben werden kann, kann der Wert beim Schlüssel-Nummerator erst ermittelt werden, wenn die Schlüsseleigenschaft festgelegt ist, also erst vor dem Commit des Objekts, wenn die Schlüsseleigenschaft bereits gesetzt ist. Nummerator-Eigenschaften Die Berechnung einer einfachen Nummerator-Eigenschaft wird im Konstruktor der Eigenschaft durchgeführt. Dort wird zuerst das Nummerator-Objekt gesperrt, danach wird der Wert ausgelesen, um 1 erhöht und wieder gesetzt. Im Normalbetrieb wird diese Änderung des Nummerator-Objekts sofort in einer eigenen Transaktion auf die Datenbank geschrieben und das Nummerator-Objekt damit wieder entsperrt, damit es auch andere Benutzer verwenden können, selbst wenn das nummerierte Objekt (z.B. der Einbringer) noch in Bearbeitung und noch nicht comittet ist. Im Fall des Datenimports werden die Zugriffe auf das Nummerator-Objekt jedoch in der Transaktion durchgeführt, in der die Objekte erzeugt und geändert werden. Damit erspart man sich das oftmalige Sperren des Nummerator-Objekts, da dieses dann für die ganze Transaktion gesperrt bleibt und für alle neu erzeugten Objekte ohne neuerliche Sperre verwendet werden kann. Dieser Performancegewinn bringt allerdings auch Nachteile mit sich. Offensichtlich ist, dass immer nur eine Transaktion diesen Nummerator verwenden kann. Alle anderen Transaktionen müssen warten, bis diese Transaktion abgeschlossen ist und der Numme- 81 rator damit wieder freigegeben wird. Dabei können durchaus auch Timeouts auftreten, wodurch selbst beim Datenimport Probleme entstehen können. Daher sollte auch beim Import von Objekten mit Nummeratoren immer nur ein Thread verwendet werden, außer man deaktiviert den Nummerator. Dazu muss der Wert des Nummerators aus einer Spalte der Datenquelle oder einer berechneten Spalte befüllt werden, indem man einen Eintrag in der Tabelle der Zuordnungen definiert, durch den der Nummerator-Eigenschaft ein Wert zugewiesen wird. Bei dieser Zuordnung setzt man zusätzlich die Optionen „Methoden zum Erzeugen des Wertes übergehen“ und „Methoden zum Speichern des Wertes übergehen“. Dadurch wird jene Aktion nicht ausgeführt, die den Wert der Nummerator-Eigenschaft über das Nummerator-Objekt berechnet. Die Eigenschaft erhält daher den Wert, ohne dass das Nummerator-Objekt involviert ist und gesperrt werden muss. Dadurch wird die Einschränkung betreffend der Parallelisierung aufgehoben. Um den Wertebereich, die Gültigkeit und Eindeutigkeit des Werts der Nummerator-Eigenschaft muss man sich selbst kümmern. Nach dem Abschluss des Imports darf man nicht vergessen, das Nummerator-Objekt, das ja im Rahmen des Datenimports nicht auf den korrekten Wert gesetzt wurde und daher bei dem alten Wert weiterzählen würde, auf den höchsten verwendeten Wert zu setzen, da es sonst zu doppelten Schlüsselwerten kommen könnte. Dazu ruft man die Aktion Schlüsselwert korrigieren ([email protected]: UpdateValue) auf jenes Objekt auf, das im Rahmen des Datenimports den höchsten vergebenen Wert erhalten hat. Üblicherweise wird das in einem eigenen Datenimport gemacht. Dabei werden die betroffenen Datensätze auf der Quelldatenbank in einer View über die Quelldaten ermittelt und im Filterskript für Objekte diese Aktion aufgerufen. Diese Vorgehensweise ist nur zulässig, wenn sichergestellt ist, dass während des Datenimports nicht manuell oder durch andere Maßnahmen Objekte angelegt werden, die denselben Nummerator verwenden. Diese würden potenziell Nummern aus einem Bereich erhalten, die für Objekte des Datenimports bereits vergeben sind. Um das zu verhindern, müssen in solchen Fällen schon vor dem Datenimport die Nummerator-Werte um jene Anzahl erhöht werden, die im Rahmen des Imports vergeben wird. Damit kann jederzeit ein neues Objekt manuell angelegt werden und es erhält jedenfalls eine andere Nummer als die vom Datenimport angelegten Objekte. 5. Was man unbedingt beachten sollte 5.1 Nummeratoren Schlüssel-Nummerator Schlüssel-Nummeratoren werden, wie schon in der Einleitung erwähnt, nicht im Konstruktor der Eigenschaft berechnet, sondern erst später, wenn bereits alle Eigenschaften gesetzt sind, durch die Aktion Erzeugen der Nummerator-Werte ([email protected]:GenerateIDs) gesetzt. Wird ein Objekt im Rahmen eines Use-Cases vom Benutzer angelegt, so wird im Objektkonstruktor, der bei den betroffenen Objekten als TriStep durch die Methodendefinition [email protected]:TriStep implementiert ist, zuerst eine Initialisierung vorgenommen ([email protected]:PreGUI), danach der Dialog angezeigt ([email protected]: DispGUI) und danach noch ein Nachbearbeitungsschritt aufgerufen ([email protected]:PostGUI). Die Berechnung der Werte des Schlüssel-Nummerators wird als Pre-Wrapper der PostGUI-Aktion, also direkt nach dem Bearbeiten der Eigenschaften, durchgeführt. Diese Vorgehensweise kann in dieser Form bei einem Datenimport nicht funktionieren, da die Eigenschaften des Objekts vom Datenimport erst nach der Verarbeitung des Objektkonstruktors gesetzt werden, zu einem Zeitpunkt also zu dem die PostGUI-Aktion bereits durchgeführt wurde. Daher gibt es grundsätzlich zwei Vorgehensweisen beim Datenimport: 1. Wird der Nummerator für die Vergabe der Werte verwendet, so muss im Skript für Objekte vor dem Commit die Aktion Erzeugen der Nummerator-Werte ([email protected]:GenerateIDs) explizit aufgerufen werden. Diese sperrt die betroffenen Nummerator-Objekte, was die bei den normalen Nummeratoren besprochenen Probleme hervorruft. Diese Variante ist relativ langsam, dafür aber einfach umzusetzen. Außerdem kann sie selbst beim Import von Daten während des Produktionsbetriebs ohne die Gefahr von Inkonsistenzen eingesetzt werden. 2. Werden die Nummeratorwerte über den Datenimport gesetzt, so kann der Aufruf der Aktion entfallen, aber es müssen im Nachhinein für jeden Schlüsselwert die Nummerator-Wert-Objekte durch den expliziten Aufruf der Aktion Schlüsselwert korrigieren ([email protected]:UpdateValue) auf das Objekt mit den höchsten vergebenen Werten gesetzt werden. Die Berechnung der IDs in der Datenquelle ist zwar nicht kompliziert, erfordert aber eine gewisse Erfahrung im Umgang mit der Programmierung von Abläufen in SQL. Mit dieser Variante erreicht man eine bedeutend bessere Performance und kann damit 83 auch die Datenimporte parallelisieren. Allerdings darf man auch die Nacharbeiten nicht vergessen, damit man nicht im Nachhinein mit inkonsistenten Daten konfrontiert ist, wenn die Benutzer nach dem Import manuell neue Objekte erzeugen und diese einen bereits im Rahmen des Datenimports vergebenen Wert bekommen. Alles in allem sollte man die Warnung „Die Eigenschaft x der Objektklasse y ist ein Nummerator, nur 1 Thread sollte verwendet werden“, die beim Datenimport ausgegeben wird, wenn Objekte einer Objektklasse mit Nummerator-Eigenschaften erzeugt werden, durchaus ernst nehmen und sich eingehend mit dem Thema befassen. Wenn man das nicht tut, kann das einerseits den Datenimport sehr bremsen, andererseits kann es sogar zur Vergabe von doppelten Werten bei Nummerator-Eigenschaften kommen. 5.2 Protokoll Das Protokoll des Datenimports wurde mit Version 4 auf XML umgestellt. Dadurch sind die Protokolle strukturiert und automatisiert auswertbar geworden. Als zusätzlichen Vorteil kann man seitdem auch vom Filterskript für Rohdaten und aus beiden Filterskripts für Objekte Daten in das Protokoll schreiben. Dazu gibt es im globalen Scope des Skripts das Objekt coolog, das gesetzt ist, wenn als Protokollmodus „Protokolliere Fehler“ oder „Vollständiges Protokoll“ eingestellt ist. Wird kein Protokoll geschrieben, ist dieses Objekt im Skript auch nicht gesetzt. Von den Methoden, die auf dieses Objekt anwendbar sind, sind die folgenden zum Schreiben von Informationen in das Protokoll verwendbar: InsertComment Parameter: ° data: Eingangsparameter (BSTR) Mit InsertComment wird ein XML-Kommentar in das Protokoll geschrieben, der den in data angegebenen Text enthält. 5. Was man unbedingt beachten sollte 5.1 Nummeratoren 5.2 Protokoll LogString Parameter: ° ° ° data: Eingangsparameter (BSTR) address: Optionaler Eingangsparameter (BSTR) nodename: Optionaler Eingangsparameter (BSTR) Mit LogString wird der angegeben Text als Wert in ein XML-Element mit dem Namen Entry geschrieben Der Wert wird dabei laut XML-Escaping behandelt, also „&“ durch „&amp;“ ersetzt, „<“ durch „&lt;“ usw., damit der Wert beim Lesen des XML wieder genau dem übergebenen Wert entspricht. Ist ein Wert address angegeben, wird dieser als Wert des Attributs address in das Protokoll geschrieben. Mit dem Parameter nodename kann der Name des XML-Elements geändert werden. LogXML ° ° ° xmldata: Eingangsparameter (BSTR) address: Optionaler Eingangsparameter (BSTR) nodename: Optionaler Eingangsparameter (BSTR) Im Unterschied zu LogString wird bei LogXML der Wert ohne XML-Escaping in das Protokoll geschrieben, sodass ganze XML-Fragmente eingefügt werden können. Mit den folgenden vier Funktionen können auch komplexere XML-Strukturen erzeugt werden: EnterElement ° name: Eingangsparameter (BSTR) Hiermit wird ein XML-Element mit dem übergebenen Namen begonnen. 85 LeaveElement ° name: Eingangsparameter (BSTR) LeaveElement schließt ein mit EnterElement begonnenens XML-Element. Um Problemen, die durch den parallelen Zugriff auf das Protokoll entstehen könnten vorzubeugen, wird das Protokoll bei einem EnterElement solange exklusiv für den bearbeitenden Thread gesperrt, bis alle begonnenen Elemente wieder mit LeaveElement beendet wurden. AddAttribute ° ° name: Eingangsparameter (BSTR) value: Eingangsparameter (BSTR) Nach einem EnterElement können so lange mit AddAttribute Attribute zu dem XML-Element hinzugefügt werden, bis eine beliebige andere Funktion auf das Objekt ausgeführt wird. AddValue ° value: Eingangsparameter (BSTR) Mit dieser Funktion wird der Wert value zum Text des XML-Elements hinzugefügt und dabei entsprechend escaped. 5.3 Roll-Forward Schreiben des Roll-Forward-Logs Das Roll-Forward-Log ist jene Protokolldatei, in die bei jedem Datenimport die Rohdaten jener Datensätze geschrieben werden, bei denen Verarbeitungsfehler aufgetreten sind. Entweder sind das einzelne Datensätze, wenn etwa bei der Konvertierung von Werten auf den Typ der Eigenschaft ein Fehler aufgetreten ist oder ein 5. Was man unbedingt beachten sollte 5.2 Protokoll 5.3 Roll-Forward Objekt nicht gesperrt werden konnte, oder alle Datensätze einer Transaktion, wenn in Rahmen des Commits ein Fehler aufgetreten ist. Daher finden sich auch oft viel mehr Datensätze im Roll-Forward-Log, als Fehler aufgetreten sind. Da diese Daten erst am Ende jeder Transaktion geschrieben werden, ist im Roll-Forward-Log der Stand der Rohdaten nach Ausführung des Filterskripts für Rohdaten enthalten, also inklusive der Änderungen und der berechneten Spalten des Filterskripts für Rohdaten. Während der Ausführung des Ladevorgangs werden die Daten in der Datei gesammelt und am Ende des Imports im Log-Objekt in der Inhaltseigenschaft Roll-ForwardLog abgelegt. Verarbeiten des Roll-Forward-Logs Der Inhalt des Roll-Forward-Logs wird beim Roll-Forward als Datenquelle verwendet. Das ist aber auch der einzige Unterschied zu einem „normalen“ Datenimport. Es wird genauso das Filterskript für Rohdaten ausgeführt, das bei Bedarf diesen Spezialfall berücksichtigen muss, da die geänderten Spalten des ersten Imports möglicherweise nicht noch einmal geändert werden dürfen. Und – wie bei allen anderen Datenquellen auch – wird bei diesem Import wieder ein neues Roll-Forward-Log mit allen bei der neuerlichen Verarbeitung aufgetretenen Fehlern geschrieben. Datensätze mit systematischen Fehlern werden daher immer wieder im Roll-Forward-Log landen. Auch werden alle Datensätze von Transaktionen, bei denen einer der Datensätze den Abbruch der Transaktion verursacht, ins Roll-Forward-Log geschrieben, was dazu führen kann, dass Änderungen von Datensätze, die eigentlich korrekt verarbeitbar wären durch die Verarbeitung eines fehlerhaften Datensatzes letztendlich nicht committet werden können. In diesem Fall sollte vor dem Roll-Forward die Transaktionsgröße auf „1“ gestellt werden. Damit werden nur mehr jene Datensätze ins neue Roll-Forward-Log geschrieben, die tatsächlich zu Fehlern führen und man kann sich dann gezielt um die Behebung der Ursachen dieser Fehler kümmern. In vielen Fällen ist die Auswahl der Algorithmen zur Vermeidung doppelter Objekte aus Performancegründen nicht für eine nochmalige Ausführung eines Datenimports und damit auch nicht für die Durchführung eines RollForwards geeignet und muss vor dem Roll-Forward angepasst werden. 87 Ein Roll-Forward darf nur von einem Datenimport verarbeitet werden, bei dem dieselben Spalten in derselben Reihenfolge verwendet werden, da im Roll-Forward-Log keine Spaltennamen angegeben sind und daher Unterschiede bei der Verwendung nicht erkannt werden können. 5.4 Starten des Datenimports Das Starten des Datenimports erfolgt gerade in der Entwicklungsphase meistens über das Kontextmenü des Datenimportobjektes. Damit kann interaktiv der Import angestoßen und der Fortschritt direkt am Bildschirm mitverfolgt werden. Wird der Import im Fabasoft Win32-Client der Version 6 angestoßen, kann man den Datenimport über den Dialog auch abbrechen, was im Fall des Fabasoft Webbrowser-Clients oder des Fabasoft Windows-Clients nicht möglich ist, da hier der Datenimport am Webserver ausgeführt wird und zu diesem keine permanente direkte Verbindung besteht. Doch für die Durchführung größerer Migrationen eignen sich Skripts besser, da hier ganze Abfolgen von Datenimporten, Skripts und evt. auch Datenbankexporten aufgerufen werden können und damit lange dauernde Vorgänge ohne Interaktion und Überwachung ablaufen können. Ein derartiges Skript findet sich im Kapitel „Zusätzliche Beispiele“ auf Seite 159. Ferngesteuertes Importieren Für den Aufruf über ein Skript gibt es die Aktion Ferngesteuertes Importieren ([email protected]: ImportRemote) bei der eine ganze Reihe zusätzlicher Parameter zur Verfügung stehen: showdialog <BOOLEAN> Mit diesem Parameter kann man im Fall des Fabasoft Win32-Clients der Version 6 angeben, ob der Fortschrittsdialog angezeigt werden soll oder nicht. Soll der Dialog nicht angezeigt werden, muss dieser Parameter auf „False“ gesetzt werden. Im Fall der anderen Fabasoft Clients hat der Parameter keine Auswirkungen. 5. Was man unbedingt beachten sollte 5.3 Roll-Forward 5.4 Starten des Datenimports closedialog <BOOLEAN> Falls der Fortschrittsdialog angezeigt wird, kann über diesen Parameter gesteuert werden, ob der Dialog nach der Beendigung des Importvorgangs automatisch geschlossen wird, indem man den Parameter auf den Wert „True“ setzt, oder ob auf die Bestätigung des Dialogs durch Drücken der Schaltfläche Close gewartet werden soll. reportcreatedobjects <BOOLEAN> Wird dieser Parameter auf „True“ gesetzt, dann wird als Ausgangsparameter createdobjects eine Liste aller erzeugten Objekte zurückgegeben. Das setzt voraus, dass der Import synchron ausgeführt wird. createdobjects <OBJECT> In dieser Objektliste wird die Liste aller erzeugten Objekte zurückgegeben, falls der optionale Parameter reportcreatedobjects auf „True“ gesetzt wurde. rollforward <BOOLEAN> Durch Setzten dieses Parameters auf „True“ werden die Daten nicht von der konfigurierten Datenquelle, sondern aus dem Roll-Forward-Log, das im Protokollobjekt hinterlegt ist, gelesen. asynchron <BOOLEAN> Mit diesem Parameter steuert man, ob der Aufruf der Aktion Ferngesteuertes Importieren ([email protected]:ImportRemote) sofort wieder beendet wird und der eigentliche Datenimport asynchron in einem eigenen Thread durchgeführt wird („True“), oder ob auf das Ende des Datenimports gewartet werden soll und daher der Aufruf der Aktion erst nach der Beendigung des Datenimportvorganges beendet wird („False“). Der Datenimport selbst wird jedenfalls in einem eigenen Thread durchgeführt. Für den Datenimport ist es vorteilhaft, wenn durch die Ausführung des Datenimports nicht permanent eine Aktion ausgeführt wird, da damit Ressourcen nicht freigegeben werden können und der Speicherbedarf höher ist. Andererseits ist 89 es natürlich bei einer Folge von Datenimporten notwendig, das Ende eines Imports abzuwarten bevor der nächste Import gestartet wird. Das kann sehr effizient durch die Verwendung des im Parameter synchobj zurückgegebenen COM-Objekts erfolgen. paramobject: <COMINTERFACE> Dieses COM-Objekt wird, falls es gesetzt ist, als Kontext an die Skripts übergeben und kann dort zum Beispiel verwendet werden, um bestimmte Objekte in Objektlisten dieses Objektes zu sammeln. Auch anwendungsspezifische Protokolle können damit realisiert werden. finishedscript <CONTENT> Dieses Skript wird nach der Beendigung des Datenimports aufgerufen und kann Nachbearbeitungsschritte eines Datenimports beinhalten. Einer dieser Schritte kann auch der Start eines weiteren Imports sein. Als globale Parameter steht neben dem Runtime (coort) und der Transaktion (cootx) auch noch das Objekt des Parameters paramobject als cooparam zur Verfügung. Weiters stehen die Anzahl der verarbeiteten Datensätze (records), die Anzahl der erzeugten Objekte (created), die Anzahl der geänderten Objekte (refreshed) und die Anzahl der aufgetretenen Fehler (errors) zur Verfügung. Das aufgerufene Datenimportobjekt wird dem Skript als Parameter cooimport übergeben. synchobj <COMINTERFACE> Einfacher als mit dem finishedscript kann mit dem syncobj eine Abfolge von Datenimporten, Datenexporten und anderen Aktionen realisiert werden, indem man den Datenimport mit async gleich „True“ aufruft und danach auf das im Parameter syncobj zurückgegebene Objekt entweder die Funktion Wait aufruft, die auf das Ende des Datenimports wartet, oder periodisch anhand des Resultats der Funktion IsFinished das Ende des Imports erkennt. Während dieser Zeit kann der Datenimport durch den Aufruf von Cancel abgebrochen werden. 5. Was man unbedingt beachten sollte 5.4 Starten des Datenimports table <STRING> In der Version 7 wurde ein weiterer Parameter eingefügt, um beim Aufruf des Imports die Tabelleneigenschaft dynamisch überschreiben zu können. Speziell bei Datenquellen, die in der Tabelleneigenschaft den Dateinamen der Quelldatei beinhalten (zum Beispiel beim Import aus einer CSV-Datei) kann damit beim Aufruf der Dateiname übergeben werden, ohne das Datenimportobjekt vorher ändern zu müssen. 91 6 93 6 Optimierungen Bei der Optimierung von Datenmigrationen muss man im ersten Schritt, bevor man an die Umsetzung oder Optimierung der einzelnen Datenimporte geht, eine Gesamtstruktur erarbeiten. Ein gutes Design in dieser Phase bewirkt, dass in späteren Phasen durch Skalierung und Parallelisierung weit bessere Resultate erzielt werden können, als durch einzelne noch so intelligente Optimierungsmöglichkeiten erreicht werden könnte. Datenimporte im Nachhinein umzuschreiben, führt zu Zeitverlust bei der Entwicklung, und Optimierungsschritte müssen danach oft erneut durchgeführt werden. 6.1 Struktur des Datenimports Die Struktur des Datenimports orientiert sich meist an den Abhängigkeiten und Voraussetzungen des Datenmodells und basiert daher auf dem Metadatenkatalog, einer Aufstellung aller im Zielsystem verwendeter Objektklassen und Eigenschaften. Daraus ergeben sich direkt die Eigenschaften, die in der Ziellösung angezeigt werden und die daher auch mit Daten zu befüllen sind. Die darin enthaltenen Objektzeigereigenschaften ergeben die möglichen Referenzen zwischen den einzelnen Objektklassen, wobei in der konkreten Lösung meist nicht alle Möglichkeiten, die das Objektmodell bietet, auch wirklich verwendet werden. Abhängigkeiten Aus den Referenzen, die im Metadatenkatalog definiert sind, ergibt sich implizit eine Reihenfolge der Importe. Diese ist jedoch nicht linear zu sehen, sondern viel mehr als eine Liste voneinander abhängiger Einzelschritte, wie man sie in einer Projektplanung kennt und dort oft als Balkendiagramm darstellt. Schätzt man die Zeiten für die einzelnen Vorgänge ab und setzt die verwendeten Ressourcen ein, so kommt man schnell zu einem kritischen Pfad, der die Dauer der gesamten Migration definiert. Wie kommt man aber nun zu den Abhängigkeiten und wie kann man diese auflösen? Voraussetzungen Einerseits gibt es Abhängigkeiten, die durch eine Referenz auf eine andere Objektklasse gegeben sind, die beim Erzeugen des Objekts bereits gesetzt werden muss. Es wird also der vollständige Import von Objekten einer Klasse A für den Import der Objekte der Klasse B vorausgesetzt. Zum Beispiel müssen die Sachgebiete bereits definiert sein, bevor Sachakten angelegt werden können, da es in der Anwendungslogik nicht vorgesehen ist, dass Sachakten existieren, die keinem Sachgebiet zugeordnet sind. Genauso ist es unbedingt notwendig, dass jene Objekte, die in die Objektliste eines Ordners gelegt werden sollen, zuvor angelegt werden, bevor sie in den Ordner gelegt werden können. Es ergibt sich also eine zwingende Reihenfolge der Datenimporte. Referenzen Objekte, die einander referenzieren – also in Objektzeigereigenschaften einer anderen Objektklasse enthalten sind – können jedoch oft auch unabhängig voneinander angelegt und im Nachhinein verknüpft werden. Beispiel: Import von Organisationen und Personen Es sollen Organisationen und Personen angelegt werden, wobei jede Person als Ansprechpartner in einer Organisation einzutragen ist. Es kann pro Organisation auch mehrere Ansprechpartner geben. Es gibt jetzt drei mögliche Strukturierungen für diesen Datenimport: 1. Alle Datensätze werden mit einem einzigen Datenimport geladen. Das ist in vielen Fällen eine sehr effiziente Variante, da hier die wenigsten Updates erfolgen, allerdings hat sie den Nachteil, dass die Komplexität steigt, wenn auch an anderen Stellen der Migration Organisationen oder Personen angelegt werden. 2. Organisationen werden zuerst geladen und beim Laden der Personen wird auch die Beziehung zwischen der Organisation und der Person erstellt. Hier werden die Objektklassen sauber getrennt, doch das vollständige Laden der Organisationen ist eine 6 Optimierungen 6.1 Struktur des Datenimports Voraussetzung für das Laden der Personen und damit müssen die beiden Importe hintereinander durchgeführt werden. 3. Organisationen und Personen werden in eigenen Datenimporten angelegt und die Verbindung zwischen den beiden Objektklassen in einem eigenen Import erstellt. Die Objekte beider Objektklassen können nun unabhängig voneinander geladen werden. Das Erstellen der Verbindung zwischen den zwei Objektklassen setzt jedoch den vollständigen Import aller Objekte beider Objektklassen voraus. Je mehr Beziehungen als Voraussetzungen gelten, desto stärker sind die Einschränkungen im Bezug auf die Reihenfolge der Importe und damit auch auf die Parallelisierbarkeit. Für ein gut parallelisierbares Design ist es daher notwendig, direkte Voraussetzungen zu vermeiden und Referenzen erst später setzen zu können. Hintergrund: Konkretes Migrationsszenario Bei einer kürzlich optimierten Datenübernahme gab es zwei zentrale Objektklassen, von denen jeweils noch drei bzw. vier Objektklassen abhängig waren. Die beiden zentralen Klassen waren über einen bidirektionalen Link verbunden. In der Originalimplementierung wurden die Objekte der einen Objektklasse importiert und danach, beim Importieren der zweiten Objektklasse, wurde der Link auf die erste Objektklasse gesetzt. Von beiden Objektklassen mussten mehrere Millionen Objekte erzeugt werden. Allein diese beiden Importe überschritten die zur Verfügung stehende Migrationsdauer. Diese Abhängigkeit wurde dahingehend aufgelöst, dass die Objekte der beiden Objektklassen parallel angelegt wurden und erst in einem neuen, späteren Importvorgang verknüpft wurden. Die wichtigste Voraussetzung dafür war eine effiziente Suche nach den angelegten Objektinstanzen. Diese wurde über einen Datenbankexport und einen JOIN auf der Quelldatenbank realisiert. Trotz des Mehraufwands eines zusätzlichen Imports und der Datenbankexporte war der Import allein dadurch viel schneller als in der Originalimplementierung. 95 Wie man aus diesem Beispiel sieht, sind es besonders die Objektklassen mit einer hohen Anzahl von Objekten, die hierbei einer Rolle spielen. Bei Objektklassen mit wenigen 100 Instanzen lohnt sich meistens der Aufwand der Optimierung nicht. Migrationszwischenformat Einen ersten Ansatz für eine Strukturierung der Datenimporte bietet das so genannte Migrationszwischenformat. Nach einfachen Regeln werden aus dem Metadatenkatalog pro Objektklasse zwei Importe abgeleitet (oder sogar generiert). Der erste Import erzeugt die Objekte der Objektklasse und setzt so viele Eigenschaften wie möglich. Ausnahmen sind Objektzeiger, die Objekte referenzieren, die zum Zeitpunkt des Imports noch nicht erzeugt wurden, und Listeneigenschaften. Diese ausständigen Eigenschaften werden später mit dem zweiten Datenimport befüllt. Darüber hinaus kann es für manche Objektklassen oder Eigenschaften spezielle Importe geben. Diese sind zum Beispiel notwendig um Schlüssel-Nummeratoren zu setzen oder um spezielle Eigenschaften bei mehreren Objektklassen zu laden. Typische Beispiele dafür sind Manuelle Unterschriften oder Benutzer/Gruppen mit Änderungsberechtigungen. Durch die direkte Ableitung der Importe aus dem Metadatenkatalog ist die Struktur der Datenimporte einfach durchschaubar und durch den Metadatenkatalog bereits zu einem guten Teil dokumentiert. Es kann daraus direkt eine Datenbankdefinition abgeleitet werden, die als Grundlage für eine organisatorische Aufteilung der Migrationsentwicklung verwendet werden kann. In einigen Migrationen wurden die Tabellen des Zwischenformats vom Auftraggeber oder einer von diesem beauftragten Drittfirma mit den Daten aus dem Altsystem befüllt und der Datenimport aus dem Zwischenformat von Fabasoft Consultants umgesetzt. Die organisatorischen Vorteile dieser Vorgehensweise und auch die Reduktion der Komplexität überwiegen oft den dadurch entstehenden Mehraufwand durch die höhere Anzahl von einzelnen Datenimporten. Falls notwendig können aber auch auf Basis der Tabellen weiter optimierte Importe abgeleitet werden, die als Datenbasis dann eine View auf mehrere Tabellen des Zwischenformats verwenden. 6 Optimierungen 6.1 Struktur des Datenimports 6.2 Optimierungsoptionen des Imports Zusammenfassen von Importen Das Zusammenfassen von Importen macht vor allem beim Import von mehreren Objektklassen Sinn, die über einen Objektzeiger verbunden sind und deren Anzahl relativ gleich ist (1:1-Beziehung oder 1:n-Beziehung mit kleinem n). Damit kann durch den gemeinsamen Import ein späteres Update der Objekte vermieden werden. Das wirkt sich insofern besonders stark aus, dass Updates wesentlich aufwendiger sind, als das Setzen der Eigenschaft in der Transaktion, in der das Objekt erzeugt wird. Aufteilung von Importen Gerade den gegenteiligen Schritt muss man oft setzen, wenn man die Migration auf Parallelisierung optimiert. Dann kann es durchaus Sinn machen, unterschiedliche Updates auf mehrere Importe aufzuteilen, die dann entweder parallel oder aber in verschiedenen Phasen des Imports durchgeführt werden, um so den Ablauf der Migration zu optimieren. 6.2 Optimierungsoptionen des Imports Es gibt viele Ansatzpunkte für Optimierungen. Die wichtigsten davon hängen direkt mit der Konfiguration der Datenimportobjekte zusammen. Hier kann man die meiste Performance verlieren oder gewinnen. In Abbildung 16 sind die wichtigsten Punkte zusammengefasst, die man als Checkliste für die Optimierung verwenden kann. 97 Abbildung 16 Optimierungsoptionen des Datenimports Algorithmen zur Vermeidung doppelter Objekte Optimiert man einen Datenimport, so fängt man generell mit der Auswahl der Schlüsseleigenschaften und der Suchmethoden an. Hier kann man durch die Einhaltung einiger weniger Regeln die richtige Methode auswählen. Die einzelnen Methoden sind im Kapitel „Identifikation und Duplikatsprüfung“ auf Seite 55 ausführlich mit ihren Vor- und Nachteilen aufgelistet. Wichtig bei der Optimierung ist auch, dass bei Algorithmen, die die Suche auf der Datenbank verwenden, auch entsprechende Indizes definiert sind, sodass die Datenbank die Suche effizient durchführen kann. Ein häufiger Fehler dabei ist, dass während der Entwicklung der Migration nur mit Testdatenbeständen gearbeitet wird, die nur einen Bruchteil der Echtdaten umfassen und dadurch Performanceprobleme, die erst bei weiterer Befüllung der Datenbank auftreten, nicht erkannt werden. Wird in einer fast leeren Datenbank bei der Suche ein Tablescan 6 Optimierungen 6.2 Optimierungsoptionen des Imports gemacht, fällt das kaum auf. Läuft derselbe Import aber über eine Million Datensätze, dann ist ein Tablescan bei der Suche nicht mehr vertretbar. In diesem Fall helfen die Werkzeuge der Datenbanken bei der Optimierung der Indizes. Dabei darf aber nicht übersehen werden, dass jeder Index auch bei Datenänderungen (INSERT, UPDATE oder DELETE) angepasst werden muss, also auf der Datenbank Last verursacht. Daher ist es gut, Indizes, die während der Migration nicht verwendet werden, vor der Migration zu löschen und danach wieder anzulegen. Die primären Indizes dürfen jedoch nicht gelöscht werden, da sie einerseits beim Laden der Objekte in den Cache benötigt werden und andererseits – wie die Clustered Indizes beim SQL-Server – auch die Art der Speicherung der Werte angeben. Sortierung Einen nicht zu unterschätzenden Einfluss auf die Performance des Imports hat die Sortierung der Datensätze. In vielen Fällen werden Objektlisten befüllt, was effizienter erfolgen kann, wenn Änderungen eines Objektes in einer einzigen Transaktion zusammengefasst werden, als wenn diese über den gesamten Import verteilt werden. Pro Transaktion wird bei jedem geänderten Objekt neben der eigentlichen Änderung auch zumindest das Änderungsdatum angepasst, was durch das Zusammenfassen von Änderungen entsprechend seltener durchgeführt werden muss. Zudem ist auch das Caching auf allen Ebenen, von der Datenbank über das Fabasoft Components COO-Service bis zum Kernel-Cache, effizienter, wenn die Verwendung von Objekten zeitlich begrenzt ist und daher diese Objekte auch wieder aus dem Cache entfernt werden können, ohne sofort wieder geladen werden zu müssen. Gruppenwechsel Der Gruppenwechsel optimiert die Verarbeitung sortierter Datenbestände noch weiter, indem die Änderungen, die ein bestimmtes Objekt betreffen, innerhalb einer einzigen Transaktion durchgeführt werden können. Neben den Performancevorteilen ist das auch eine Voraussetzung für die Parallelisierung, da Änderungen, die ein und dasselbe Objekt betreffen, zu jedem Zeitpunkt nur von einer einzigen Transaktion gemacht werden dürfen. Werden für einzelne Objekte dennoch mehrere Transaktionen benötigt, schützt im Normalfall der interne Sperrmechanismus der Datenimporte vor inkonsistenten Änderungen indem ein Worker Thread solange 99 angehalten wird, bis alle zu ändernden Objekte nicht mehr an Transaktionen anderer Worker Threads beteiligt sind. Transaktionsgröße Die Transaktionsgröße, die durch die Anzahl der Datensätze pro Commit bestimmt wird, hat weniger Bedeutung für die Performance. Bei Verwendung des Gruppenwechsels sollte sie größer als die maximale Anzahl von Datensätzen pro Objekt sein, wobei sie auch dort nur in Ausnahmefällen über 1.000 liegen sollte. Die optimale Transaktionsgröße ergibt sich aus einem Kompromiss zwischen der Ressourcenbelegung, die bei großen Transaktionen zunimmt, und dem Transaktions-Overhead der durch viele kleine Transaktionen verursacht wird. Ein nicht zu vernachlässigender Faktor ist die Verzögerung der Datenbank beim Schreiben der Logs auf die Festplatten, denn das Schreiben muss synchron mit dem Commit erfolgen. Hintergrund: Rechenbeispiel Wenn man bei den aktuellen Festplatten von ca. acht Millisekunden durchschnittlicher Zugriffszeit ausgeht, so wird allein dadurch die Anzahl der sequenziellen Transaktionen pro Sekunde auf ca. 120 begrenzt. Würde man nur einen Datensatz pro Transaktion importieren, könnte man damit gerade einmal 7.000 Datensätze pro Minute verarbeiten. Aber schon mit einer Transaktionsgröße von 24 Datensätzen könnte man – gäbe es nur dieses Limit – 10 Mio. Datensätze in der Stunde verarbeiten. Eine kurze Testreihe auf einem einzelnen Server hat bei einem konkreten Import eine optimale Einstellung von 64 Datensätzen pro Commit ergeben, wobei die gemessenen Werte zwischen 16 Datensätzen und 1.024 Datensätzen sich um weniger als 10 % unterschieden haben. 6 Optimierungen 6.2 Optimierungsoptionen des Imports Parallelisierung Es gibt drei verschiedene Ansätze, wie Parallelisierung erreicht werden kann ° ° ° Verwendung von mehreren Threads Verteilung der Datensätze eines Imports auf mehrere Clients (Fabasoft Components Kernels). Parallele Durchführung mehrerer verschiedene Importe Die einfachste und ungefährlichste Art zur Parallelisierung eines Datenimports ist die Verwendung von Threads. Diese Vorgehensweise hat jedoch auch das geringste Potential. Dabei werden mehrere Datenpakete gleichzeitig auf demselben Fabasoft Components Kernel verarbeitet. In diesem Fall werden jene Zeiten, in denen der Fabasoft Components Kernel nicht von einem Worker Thread belegt wird, von anderen Worker Threads genutzt. Vor allem sind das jene Zeiten, in denen RPCs auf die Fabasoft Components Backendservices gemacht werden, um Suchen durchzuführen, Objekte zu laden oder die Transaktionen zu committen. Die Anwendungslogik, die ebenfalls Zeit außerhalb des Fabasoft Components Kernels verbraucht, wird im Allgemeinen bei Datenimporten auf das Notwendigste reduziert und spielt im Verhältnis zum Kernel-Anteil nur eine untergeordnete Rolle. Um das Problem der Limitierung des Fabasoft Components Kernels zu umgehen kann man den Import auch auf mehrere Fabasoft Components Kernels verteilen, jedoch kann die Verteilung nicht mehr automatisch erfolgen, wie bei den Threads, sondern muss manuell konfiguriert werden. Voraussetzung dafür ist jedoch, dass die einzelnen Datenpakete komplett unabhängig voneinander importierbar sind und so aufgeteilt werden, dass gleichzeitige Änderungen an referenzierten Objekten von mehreren Fabasoft Components Kernels ausgeschlossen werden können. Mit dieser Methode kann auf Seite der Clients gut skaliert werden. In einer konkreten Migration wurden die Daten dafür in 16 Pakete geteilt, die parallel geladen wurden. Damit war – obwohl auf den Clients noch einiges an Anwendungslogik aktiv war – die Datenbank für einige Stunden voll ausgelastet. Bei einer derartigen Aufteilung steigt auch der Verwaltungsaufwand erheblich, der zum Starten, Überwachen und auch für die Koordination der Synchronisationspunkte der jeweiligen Importabfolgen auf den einzelnen Clients notwendig ist. 101 Weniger Zusatzaufwand ergibt sich durch das parallele Starten voneinander unabhängiger Importe auf unterschiedlichen Clients oder Fabasoft Components Kernels. Dabei sind jedoch die Abhängigkeiten zu berücksichtigen, die teilweise in den Methoden der Anwendungslogik, aber auch in den Konfigurationen versteckt sein können. Daher setzt diese Art der Parallelisierung eine genaue Kenntnis der Lösung voraus. Außerdem beschränkt sich die Anzahl der parallel durchführbaren Datenimporte oft auf wenige Objektklassen. Manchmal kann man jedoch Abhängigkeiten durch zusätzliche Importe auflösen, wie das schon im Rahmen der „Struktur des Imports“ dargelegt wurde. 6.3 Client-Optimierung Unabhängig von den Einstellungen des Datenimports gibt es einige wenige Aspekte, die am Fabasoft Components Kernel (je nachdem von wo man die Daten importiert am Fabasoft Win32-Client der Version 6, am Weboder am AT-Server) beachtet werden sollten. Client-Cache Der Client-Cache ist für jeden Datenimport eine kritische Ressource. Für den Cache ist ein Datenimport eine Extremsituation, da mit einer sehr hohen Geschwindigkeit immer neue Objekte erzeugt, geändert oder gespeichert werden. Bei ausreichend großen Datenimporten wird der Cache vollständig durch den Datenimport definiert, wodurch auf Webservern das Arbeiten anderer Benutzer stark beeinträchtigt wird. Der im Cache-Limit angegebene Wert (die Defaulteinstellung ist 20.000 Objekte und lässt sich über die Arbeitsumgebung ändern) ist nur ein Richtwert. Je nachdem, wie das Verhältnis zwischen der Anzahl der Objekte im Cache zur konfigurierten Cachegröße ist, wird der Cache mehr oder weniger oft aufgeräumt. Ist also der Cache zuerst nur mäßig befüllt und startet ein Import mit einer großen Geschwindigkeit, kann es durchaus passieren, dass der Client-Cache ein Vielfaches seiner konfigurierten Größe erreicht, bis der Aufräumprozess beginnt die Objekte wieder aus dem Cache zu entfernen. Das ist aber kein Grund zur Besorgnis, darauf ist der Fabasoft Components Kernel ausgelegt und mit dieser Situation kommt er gut zurecht, solange dabei nicht die Größe des physischen Speichers erreicht wird und das System anfängt Daten auf die Auslagerungsdatei zu schreiben. 6 Optimierungen 6.2 Optimierungsoptionen des Imports 6.3 Client-Optimierung Kritisch wird es auf 32-Bit-Systemen jedenfalls, wenn die Prozessgröße den Bereich der maximalen Prozessgröße von zwei bzw. drei Gigabyte erreicht. Der Aufräumprozess des Clients hatte vor der Version 7 auch noch eine weitere Einschränkung: Er konnte nicht arbeiten, solange Aktionen im Fabasoft Components Kernel aufgerufen wurden. Öffnet man am Fabasoft Win32Client der Version 6 ein Objekt zum Bearbeiten, dann wird dadurch eine Aktion aufgerufen, die erst mit dem Schließen des Fensters wieder verlassen wird. Läuft gleichzeitig ein Datenimport, so steigt die Anzahl der Objekte im Cache permanent an, bis das Fenster zur Bearbeitung des Objekts geschlossen wird, der Datenimport fertig ist oder die Speichergrenze erreicht ist. Letzteres führt häufig zum Absturz des Clients. Daher durfte man bis Version 6 auch nicht größere Datenimporte über die Aktion Ferngesteuertes Importieren ([email protected]:ImportRemote) synchron starten. Der Start der Aktion allein reichte aus, dass der Cache nicht mehr aufgeräumt werden konnte. Daher musste der Import über ein Skript aus dem Betriebssystem aufgerufen und der Parameter async auf „True“ gesetzt werden. Dann gab es verschiedene Möglichkeiten festzustellen, ob der Datenimport bereits fertig ist. Im Anhang befindet sich ein Skript, das sequenziell mehrere Datenimporte verarbeitet. In Version 7 wird der Cache auch aufgeräumt, wenn ein Fenster geöffnet ist, daher ist es dort kein Problem mehr, Datenimporte synchron zu starten. Welche Werte für die Cachegröße effizient sind, hängt davon ab, wie man gerade importiert. Legt man beispielsweise mit jedem Datensatz ein Objekt an, das im Import nicht wieder verwendet wird, so werden die Informationen im Cache nicht wieder verwendet und sind somit überflüssig. Eine große Cachegröße hat in diesem Fall nur die Auswirkungen, dass der Speicherverbrauch höher ist und der Zugriff auf den großen Cache langsamer ist, als bei einem kleinen Cache. Werden jedoch beim Import Objekte immer wieder verwendet, verändert oder referenziert, dann hilft der Cache bei der Vermeidung von unnötigen Ladevorgängen. Netzwerkanbindung Die performante Anbindung der Clients ist eine Grundvoraussetzung für einen schnellen Datenimport. Während die Bandbreite dabei nur beim Laden von Inhalten ausschlaggebend ist, wirkt sich die Netzwerkverzögerung 103 direkt auf die Kommunikation zwischen Fabasoft Components Kernel und Fabasoft Components COO-Services aus, die durch eine hohe Anzahl von Roundtrips charakterisiert ist. Daher sollten sich die Migrationsclients und die Fabasoft Components Backendserver im gleichen LAN befinden und nicht über WAN-Strecken vernetzt sein. Es sollten sich auch keinesfalls Firewalls zwischen den Clients und den Fabasoft Components Backendservern befinden. Client-CPUs Es liegt auf der Hand, dass die Performance eines Datenimports von schnellen CPUs am Client profitiert, denn oft ist es die Client-CPU, die stark ausgelastet und daher die kritische Ressource des Datenimports ist. Ein Multiprozessorsystem kann erst bei der Verwendung von mehreren Threads oder mehreren Importen auf einem Client Vorteile bringen. Einschränkend wirkt da der Fabasoft Components Kernel, der keine parallele Verarbeitung von Kernel-Methoden zulässt. Damit ist das Potential für eine Parallelisierung innerhalb eines Prozesses eher gering und beschränkt sich vor allem auf die Ausnützung der RPC-Zeiten durch andere Threads. Eine echte Verwendung von mehreren Prozessoren kann auch beim Datenimport derzeit nur durch das Starten von mehreren Fabasoft Components Kernels auf einem Client erreicht werden. Festplatte Die Festplatte des Client-Rechners wird beim Import von Inhalten stark belastet, denn alle Inhalte werden zuerst in das DOCDIR-Verzeichnis der lokalen Festplatte kopiert und von dort erst wieder entfernt, wenn das Inhaltsobjekt aus dem Client-Cache entfernt wird. Hat man also bei einem derartigen Import einen Cache mit 20.000 Objekten konfiguriert, dann liegen potenziell mehr als 20.000 Inhalte im DOCDIR, also möglicherweise mehrere Gigabyte. Alle diese Dateien müssen auch erst einmal dorthin geschrieben werden. Eine schnelle Festplatte wirkt sich daher beim Import von Inhalten direkt auf die Performance des Imports aus. Kritisch ist jedenfalls die Verwendung von Virenscannern, die im laufenden Betrieb alle Dateien scannen, auf die zugegriffen wird. Das führt zu wesentlichen Performanceeinbußen beim Import. 6 Optimierungen 6.3 Client-Optimierung 6.4 Fabasoft Components COO-Service-Optimierung Anzahl der Clients Eine wichtige Maßnahme bei der Implementierung von großen Datenmigrationen ist die Parallelisierung der Datenimporte auf mehreren Clients. Hier lässt sich durch Hardwareeinsatz eine Vervielfachung des Durchsatzes erreichen, was allerdings mit entsprechenden Kosten und mit einigem Aufwand bei der Parallelisierung und der Durchführung der Migration verbunden ist. 6.4 Fabasoft Components COO-Service-Optimierung Die Fabasoft Components COO-Services als Schnittstelle zwischen Client und Datenbank haben vor allem die Aufgabe, Objekte zu cachen und für das Laden und Speichern der Daten in die Datenbank zu sorgen. Dabei sollten drei Aspekte berücksichtigt werden – die Anzahl der Worker Threads, die Cache-Einstellungen und Objektsperren. Anzahl der Worker Threads Eine wichtige Voraussetzung für einen effizienten Import ist die korrekte Einstellung der Anzahl der Worker Threads. Stehen weniger Threads zur Verfügung, als benötigt werden, so müssen ankommende RPCs warten, bis wieder ein Thread frei geworden ist. Nachdem aber ein Datenimport nicht warten soll, muss gewährleistet sein, dass immer ausreichend viele Threads verwendbar sind. Ein Datenimport benötigt auf jenen Services, die vom Import betroffen sind, maximal so viele aktive Backend Threads wie im Datenimport Worker Threads definiert sind. Außerdem werden während der Initialisierung beim Sammeln von Schlüsseln für jede zu sammelnde Objektklasse zwei Threads auf den jeweiligen Services benötigt. Multipliziert man diesen Wert mit der Anzahl der parallelen Datenimporte kommt man auf einen Richtwert, der jedenfalls ausreichend ist. Zusätzlich muss am primären Fabasoft Components COO-Service für jeden parallelen Datenimport ein Worker Thread zur Verfügung stehen. 105 Berechnungsbeispiel Folgender Import ist definiert: ° ° ° ° ° Es werden Personen und Organisationen in getrennten Datenimporten erzeugt. Über die Objektplatzierung ist festgelegt, dass jede Objektklasse ein eigenes Service verwendet. Es wird die „Suche für jedes Objekt“ verwendet. Pro Datenimport sind vier Threads eingestellt. Auf jedem der drei Clients werden ein Personenimport und ein Organisationenimport gestartet. Damit braucht man auf den betroffenen Services für jeden der drei Clients mindestens vier Threads für die vier Worker Threads des Imports, was einen Wert von je zwölf Threads pro Fabasoft Components COO-Service und auf dem primären Fabasoft Components COO-Service auch zumindest sechs Threads allein für den Datenimport ergibt. Bei höherer Parallelisierung können entsprechend hohe Werte erreicht werden. Die eigentliche Auswirkung der Worker Threads am Fabasoft Components COO-Service ist jedoch, dass jeder Thread, sobald er einmal verwendet wird, eine Datenbankverbindung öffnet und im Normalfall nicht mehr freigibt. Damit hat die Erhöhung der Worker Threads auf dem Fabasoft Components COO-Service eine direkte Auswirkung auf den Ressourceverbrauch der Datenbank, da auch hier mehr Sessions geöffnet werden. Daher muss vor der Erhöhung des Werts abgeklärt werden, ob die gewünschte Anzahl von Sessions überhaupt auf der Datenbank unterstützt ist (zwei Sessions pro Fabasoft Components COO-Service sollten zusätzlich eingerechnet werden). Sonst kann es sein, dass mitten in der Migration, wenn die maximale Anzahl der Sessions auf der Datenbank erreicht ist, viele Fehler auftreten und man unter Umständen viel Zeit durch ein Restore oder durch die Fehlerbehebung verliert. 6 Optimierungen 6.4 Fabasoft Components COO-Service-Optimierung 6.5 Optimierungen am Datenbanksystem Fabasoft Components COO-Service-Cache Beim Cache am Fabasoft Components COO-Service ist die Situation ähnlich wie beim Client-Cache. Auch hier profitiert die Migration nur dann vom Cache, wenn dieser so groß ist, dass Objekte wiederverwendet werden, bevor sie aus dem Cache herausfallen. Nimmt man zum Beispiel die Situation her, dass zuerst alle Objekte einer Objektklassem mit 100.000 Instanzen angelegt werden und im nächsten Schritt Updates auf diese Objekte gemacht werden, so muss diese Objektmenge komplett im Cache Platz finden, damit dieser effizient arbeiten kann. Bis Version 6.1 wird dabei die Anzahl der Objekte im Cache spezifiziert und – wie beim Client-Cache – als Richtwert für den Aufräumprozess verwendet. Also auch hier kann der Maximalwert den Richtwert deutlich überschreiten. In Version 7 wird hingegen der maximal verwendete Speicherplatz definiert und zudem die Speicherung optimiert, sodass im Endeffekt mehr Objekte im Cache gehalten werden können und keine Gefahr mehr besteht, dass das Fabasoft Components COO-Service beim Erreichen der Speichergrenzen abstürzt. Objektsperren Objektsperren werden im Normalbetrieb in der Tabelle cooobjlock2 abgespeichert. Beim Sperren wird für jedes Objekt einzeln ein INSERT in diese Tabelle durchgeführt. Das bedeutet, dass für jede Sperre eine Transaktion auf der Datenbank ausgeführt wird. Durch das Setzen eines Registry-Eintrags werden die Sperren nicht mehr in die Datenbank geschrieben, sondern nur am Fabasoft Components COO-Service im Speicher gehalten. Das ist natürlich wesentlich schneller, hat aber den Nachteil, dass bei einem Neustart des Fabasoft Components COO-Services diese Information verloren geht. Im Fall einer Migration ist dieses Risiko jedoch kalkulierbar, da ein Neustart des Fabasoft Components COO-Services nur kontrolliert erfolgt und im Offline-Betrieb keine Sperren von Benutzern gehalten werden. 6.5 Optimierungen am Datenbanksystem Die Optimierung von Datenbanksystemen wird von vielen Experten fast wie eine eigene Wissenschaft betrieben. In diesem Kapitel werden nur jene Eckpunkte erläutert, die speziell für den Betrieb von Fabasoft Components Domänen im Rahmen von Datenmigrationen wichtig sind. 107 Backup, Restore und Recovery Große Datenmigrationen sollen, wenn das organisatorisch möglich ist, offline durchgeführt werden, also ohne gleichzeitigen Produktionsbetrieb. Jede Migration beginnt mit einem Backup des kompletten Datenbestandes, damit bei unvorhergesehenen Problemen auf einen gesicherten Stand zurückgesetzt werden kann. Auch am Ende jeder Migration steht ein Backup, damit man, falls ein Systemfehler auftritt, nicht alle Änderungen der Migration wiederholen muss. Das bedeutet aber, dass alle Datenbank-Logs die zwischen diesen beiden Backups anfallen, nur für das Recovery innerhalb der Migrationsphase relevant sind und da sind Zwischenbackups an definierten Synchronisationspunkten meistens die einzige Möglichkeit, einen konsistenten Stand sichern zu können. Die Datenbank-Logs sind also für ein Recovery unerheblich und müssen daher nicht gesichert werden, was erheblich Speicherplatz einspart. Am Microsoft SQL-Server kann man dafür das Recovery-Model der betroffenen Datenbanken auf „Simple“ stellen. In Oracle kann man die Archivierung der Redo-Logfiles in dieser Zeit deaktivieren. Indizes und Statistiken Indizes sind Datenstrukturen, über die man performant nach Daten suchen kann. Doch sie sind nur dann performant, wenn sie auch wirklich verwendet werden. Bei einer Datenmigration werden zwei Arten von Indizes verwendet: 1. Die Indizes der Primärschlüssel Diese Indizes, die von Fabasoft Components mit dem Tabellennamen erweitert um „PK“ benannt werden, garantieren einerseits ein gewisses Maß an Datenkonsistenz und werden andererseits beim Laden der Objektinformationen aus der Datenbank benötigt. Diese Indizes dürfen daher in keinem Fall geändert oder deaktiviert werden. 6 Optimierungen 6.5 Optimierungen am Datenbanksystem 2. Die Indizes für die Suche nach Objekten Für das Sammeln von Schlüsseln und für die Suche nach einzelnen Objekten werden Queries abgesetzt. Im ersten Fall nach Objekten einer oder mehrerer Objektklassen, im anderen Fall nach den entsprechenden Schlüsselwerten. Beide Arten von Queries kann man mit Indizes optimieren. Welche das sind, hängt nicht zuletzt vom verwendeten Datenbanksystem ab, das in der Regel genau zu diesem Zweck eigene Tools anbietet, mit denen man diese Queries aufzeichnen und optimieren kann. Im Microsoft SQL-Server gibt es dafür den Profiler, der eine Session aufzeichnet und den Database Engine Tuning Advisor, der die abgesetzten SQL-Statements analysiert und Vorschläge für Indizes errechnet. Bei Oracle sind diese Funktionen im Enterprise Manager integriert, bei dem man einfach die einzelnen SQLStatements auswählen kann, für die dann Optimierungsvorschläge generiert werden. Diese Vorschläge für Indizes enthalten in der Regel genau die Spalten, nach denen gesucht wird, und möglicherweise noch die Objektadressen. Verwendet man diese Indizes, hat man meistens schon die Optimierung auf der Datenbank durchgeführt. Alle zusätzlichen Indizes, die zum Beispiel für häufige Suchen im Produktivbetrieb definiert wurden, behindern die Datenmigration und sollten zuvor unbedingt gelöscht werden. Der Neuaufbau dieser Indizes nach der Migration ist wesentlich effizienter als das permanente Mitführen der Indizes während der Migration. Eine wesentliche Information für die Verwendung von Indizes sind die Statistiken. Diese werden für die korrekte Optimierung der Zugriffspläne benötigt. Je nach Datenbank und Konfiguration wird die Berechnung der Statistiken automatisch durchgeführt oder muss manuell angestoßen werden. Gerade wenn man einen Initialimport durchführt, muss man darauf achten, dass die Statistiken regelmäßig aktualisiert werden, da erst dadurch die besten Zugriffspfade verwendet werden können. 109 Beispiel: Auswirkungen von Statistiken Bei der Optimierung einer Datenmigration auf Oracle wurde durch einige wenige Indizes die Durchlaufzeit um 60 % reduziert, ohne die Migration selbst anzugreifen. Die Optimierung wurde aber erst wirksam, wenn man die Statistiken nach ca. 10 % der Migrationszeit neu berechnete, da bis dorthin die Statistiken verwendet wurden, die auf den ursprünglich leeren Tabellen basierten. Um die Berechnung der Statistiken während der Laufzeit zu vermeiden, wurden schlussendlich vor der Migration Backups der Statistiken eingespielt, die nach einem Testlauf gezogen wurden und daher von vornherein jene Zugriffstrategie ausgewählt wurde, die bei einer befüllten Datenbank ideal ist. Tabellendefinitionen Fabasoft Components implementiert zur Speicherung der Objekte ein generisches Datenmodell, das auf der Datenbank eigentlich in sieben Tabellen passt, pro Wertetyp eine und eine zusätzliche für die zusammengesetzten Eigenschaften. ° ° ° ° ° ° ° atstrval atintval atdateval atcontval atobjval atfloval ataggval Vom System werden bereits Tabellen definiert, die für eine weniger generische aber dafür effizientere Speicherung sorgen. Die Tabellen cooobject, cooversion und coocontent beinhalten einige Eigenschaf- 6 Optimierungen 6.5 Optimierungen am Datenbanksystem ten der Objektklasse Objekt und der Inhalte. Derartige Tabellen lassen sich aber auch lösungsspezifisch definieren und haben mehrere Vor- und Nachteile: Vorteile: ° Effizientere Speicherung: In den generischen Tabellen wird mit jedem Wert die objid, attrid, aggrid und lineid mitgespeichert. Das sind also 24 Byte Information, um den Wert zu identifizieren. In einer Tabelle, in der ja mehrere Werte zusammengefasst sind, wird pro Zeile nur die objid und bei Tabellen für zusammengesetzte Eigenschaften noch die aggrid gespeichert. Die attrid ist implizit durch die Datenbankspalte definiert und die lineid ist implizit definiert, da die Tabellen keine Listen enthalten können. ° Bessere Indizierbarkeit: Will man in diesen Tabellen effizient suchen, so benötigt man Indizes darauf. Diese Indizes sind wesentlich kleiner und somit schneller als die Indizes auf den generischen Tabellen. Zudem kann man auch mehrspaltige Indizes definieren. Wenn also zum Beispiel bei Personen häufig nach Vor- und Nachname gesucht wird, dann kann man dafür einen gemeinsamen Index auf der Tabelle anlegen. ° Entlastung der generischen Tabellen: Bei großen Installationen können die generischen Tabellen sehr stark anwachsen, möglicherweise auf mehrere hundert Millionen Datensätze. Dadurch werden aber auch die Operationen auf diesen Daten immer ineffizienter. Durch die Definition einer eigenen Tabelle für zum Beispiel 50 Werte lassen sich mit einem Datensatz in der neuen Tabelle bis zu 50 Werte aus den generischen Tabellen entfernen, wodurch diese wieder deutlich kleiner und damit performanter werden. Nachteile: ° Verwendbarkeit: Listeneigenschaften (mit Ausnahmen von kleinen Zeichenkettenlisten) können nicht in eigenen Tabellen abgelegt werden. ° Indizierung: Alle Eigenschaften, nach denen gesucht wird, müssen eigens indiziert werden, was einen erhöhten Verwaltungsaufwand bedeutet. Die Größe der notwendigen Indizes ist jedoch generell kleiner als bei der Verwendung der generischen Tabellen. 111 ° Beim Laden von Objekten aus der Datenbank müssen diese Tabellen zusätzlich gelesen werden. Das ergibt zusätzliche SQL-Statements. Die Definition von Tabellen kann bei der Datenmigration geringfügig Zeit sparen, speziell wenn es darum geht das Workingset der Datenbank klein zu halten oder eine Schlüsselsuche zu optimieren. Wichtig ist aber vielmehr, die Tabellen bereits vor der Migration zu definieren, da der nachträgliche Aufbau der Tabellen sehr zeitaufwändig ist. Bei der Definition der Tabellen sollte man beachten, dass man keine Eigenschaften definiert, die nie oder nur ganz selten Werte beinhalten, da jede zusätzlich Spalte, die definiert aber nicht verwendet wird, in jedem Datensatz unnötig Speicherplatz verbraucht und die SQL-Statements vergrößert. Wenn auch die Auswirkungen einer einzelnen Eigenschaft nicht ins Gewicht fallen, kann sich bei sehr vielen Eigenschaften schon ein relevanter Wert ergeben. Auto-Grow Die dynamische Erweiterung von Datenbankdateien ist ein sehr angenehmes Feature, das den Administratoren erspart, das Datenwachstum überwachen zu müssen. In einer Migration hat dieses Feature aber definitiv nichts zu suchen, da jede nachträgliche Erweiterung einer Datenbankdatei einerseits zu einer Wartezeit führt und andererseits zur Fragmentierung der Datenbankdateien auf der Festplatte führen kann. Besser ist es jedenfalls, die Datenbank schon in der endgültigen Größe zu definieren und auch den Platz für die Indizes schon ausreichend zu dimensionieren. Device-Placement Genauso wissenschaftlich wie emotionell wird die Diskussion geführt, welches Disk-Layout oder welcher RAIDLevel für welche Dateien ideal oder zumutbar ist. Die Meinungen reichen hier von „1 RAID 10 aus mindestens zwei dedizierten Festplatten für jede Datei“ bis zu „1 RAID 50 für alles“, und jeder bringt seine Argumente vor. Von meiner Warte aus möchte ich nur auf die Wichtigkeit des Themas hinweisen, ich überlasse diese Diskussion aber gerne anderen (siehe z.B. [MoNo03]). 6 Optimierungen 6.5 Optimierungen am Datenbanksystem 6.6 Fabasoft Components MMC-Service-Optimierung 6.7 Von Objekten und Töpfen 6.6 Fabasoft Components MMC-Service-Optimierung Die Fabasoft Components MMC-Services sind die Zugriffsschicht von Fabasoft Components auf das Dateisystem. Sie implementieren selbst nicht viel Funktionalität und daher hängt die Performance der Fabasoft Components MMC-Services vor allem von der Performance des darunter liegenden Dateisystems und des Storagesystems ab. Die Anzahl der Worker Threads ist auch hier zu konfigurieren, wobei die Anzahl der verwendeten Threads meistens geringer ist, als die Zahl der importierenden Clients. Um unter Microsoft Windows das NTFS performanter zu machen, sollten in der Registry unter HKLM\SYSTEM\CurrentControlSet\Control\Filesystem folgende Werte eintragen werden: ° ° ° NtfsDisable8dot3NameCreation=1 NtfsDisableLastAccessUpdate=1 NtfsMftZoneReservation=2, 3 oder 4 (Default=1) Diese Änderungen wirken sich auf alle Volumes des Servers aus und der Server muss rebootet werden, damit diese wirksam werden. 6.7 Von Objekten und Töpfen Nachdem jetzt so viel von Optimierungen die Rede war, kommen wir zur eigentlichen Frage: Warum ist der Datenimport so langsam? Das erinnert mich an die Frage, warum die Töpfe im Geschirrspüler nicht sauber geworden sind. Antwort des Herstellers: Es ist sicher nicht die Maschine schuld, nicht das Wasser und wohl auch nicht das Waschmittel, sondern man hat das Geschirr falsch eingeräumt. 113 Beim Datenimport ist das genauso: Schuld ist weder Fabasoft Components/COLD, noch das Fabasoft Components COO-Service, noch der Rechner an sich – auch wenn es auf jeder Ebene noch ein wenig besser ginge – sondern es ist die Konfiguration, die einen schnellen von einem langsamen Datenimport unterscheidet. Nur, wie findet man heraus, was man an der Konfiguration zu ändern hat? Beim Geschirrspüler werden vom Hersteller in der Bedienungsanleitung einige Regeln aufgestellt, wie zum Beispiel, dass man Töpfe nicht ineinander stellen soll. Genauso gibt es hier eine Checkliste, über die man bereits vor dem Import einige „Problemzonen“ identifizieren kann. Wenn das noch nicht zu den gewünschten Ergebnissen führt, dann muss man während der Laufzeit des Imports den Problemen auf den Grund gehen. Diese zwei Phasen nenne ich statische- bzw. dynamische Problemanalyse. Statische Problemanalyse Bei der statischen Problemanalyse geht es zuerst einmal darum, auf Basis des Objektmodells Probleme zu erkennen und zu adressieren. Dazu betrachtet man zuerst die Eigenschaften der Objektklassen, die man importiert. Nummeratoren Auf die Nummeratoren wurde bereits eingehend eingegangen. Um Nummerator-Eigenschaften muss man sich jedenfalls kümmern, da nicht zuletzt die Datenkonsistenz davon abhängt. Rückwärtsverkettungen Rückwärtsverkettungen implementieren eine bidirektionale Verbindung zwischen zwei Objekten. Setzt man also eine Referenz in der einen Richtung, wird automatisch die Referenz in die andere Richtung mitgeführt. Dafür wird ein zusätzliches Objekt erzeugt, das auf der Seite der Rückwärtsverkettungseigenschaft die Liste der referenzierten Objekte verwaltet. Die Änderungen werden durch Aktionen des Fabasoft Components Kernels konsistent durchführt. Diese Funktionalität kann damit auch nicht deaktiviert werden. Das bedeutet, dass diese referenzierten Objekte implizit verändert werden. Bei der Änderung der Rückwärtsverkettungseigenschaften werden jedenfalls die Rückwärtsverkettungsobjekte gesperrt, was einen erheblichen negativen Einfluss auf die Performance hat. 6 Optimierungen 6.7 Von Objekten und Töpfen Methoden von Eigenschaften Die dritte Klasse von Eigenschaften, die bei der statischen Problemanalyse betrachtet werden, sind jene Eigenschaften, die eine Konstruktoraktion oder eine Aktion vor dem Speichern der Eigenschaft eingetragen haben. Darunter fallen auch die normalen Nummerator-Eigenschaften. Konstruktoraktionen werden beim Erzeugen eines Objekts jedenfalls durchgeführt, Aktionen vor dem Speichern der Eigenschaften nur, wenn der Eigenschaft in der aktuellen Transaktion ein Wert zugewiesen wurde. Das kann aber auch durch einen Initialisierungswert, den Konstruktor oder durch andere Methoden geschehen und ist damit auch nicht auf Eigenschaften beschränkt, die im Datenimport verwendet werden. Ein Beispiel für eine Eigenschaft mit Konstruktoraktion ist die Eigenschaft Workflow ([email protected]: workflow), die auf Ebene der Objektklasse Objekt also für jedes Objekt definiert ist und die aus der Konfiguration ausliest, ob ein Workflow initialisiert werden soll und diesen auch gleich anlegt. Auch wenn kein Workflow erzeugt wird, so wird doch die Methode aufgerufen und die Konfiguration nach diesbezüglichen Einträgen durchsucht. Bei diesen Eigenschaften sollte untersucht werden, ob der Konstruktor oder die Aktion vor dem Speichern der Eigenschaft beim Import überhaupt ausgeführt werden müssen bzw. ob man deren Funktion durch Mittel des Datenimports ersetzen kann. Der Entwickler kann auch bereits bei der Implementierung vorsehen, dass eine Aktion im Fall des Datenimports nicht oder nur teilweise ausgeführt werden muss, indem er die Transaktionsvariable TV_BATCHMODE oder TV_LOADMODE der Softwarekomponente [email protected] prüft und, wenn diese gesetzt ist, beispielsweise Konsistenzprüfungen übergeht oder andere Eigenschaften nicht ändert, die vom Datenimport selbst gesetzt werden. Methoden von Objekten Folgende Methoden werden im Lauf des Erzeugens und Speicherns jedes Objekts aufgerufen: ° ° Die Objektkonstruktoren ([email protected]:ObjectConstructor) Die Aktionen vor dem Schreiben der Objekte ([email protected]:ObjectPrepareCommit) 115 ° Die Aktionen nach dem Schreiben der Objekte ([email protected]:ObjectCommitted) Während die erste auf alle geänderten Objekte aufgerufen wird, bevor noch die Set-Aktionen der einzelnen Eigenschaften aufgerufen werden, wird die zweite aufgerufen, nachdem das Commit erfolgreich abgeschlossen wurde. Viele Lösungen stecken viel Anwendungsfunktionalität in diese Aktionen hinein, um Konsistenzprüfungen durchzuführen, Werte zu berechnen oder weiterführende Änderungen durchzuführen. Im Fall des Datenimports sind nicht alle dieser Aktionen unbedingt notwendig und können entweder gänzlich vernachlässigt oder durch entsprechende Konfiguration ersetzt werden. Analyse Bei einer statischen Analyse betrachtet man vor allem die bei den Eigenschaften eingetragenen Methoden zum Erzeugen, Lesen und Setzen der Werte. Bei all diesen Aktionen und deren Wrappern wird überprüft, ob diese beim Datenimport ausgeführt werden müssen. Gegebenenfalls können sie durch Setzen von entsprechenden Optionen übergangen und im Bedarfsfall deren Funktionalität durch weitere Zuordnungen ersetzt werden. Dynamische Problemanalyse Im Gegensatz zur statischen Problemanalyse, die auf Basis des Objektmodells durchgeführt wird, bedient sich die dynamische Problemanalyse diverser Werkzeuge, die zum Aufspüren der Problemursachen dienen. Alle diese Werkzeuge setzen voraus, dass ein problematischer Import gerade ausgeführt wird bzw. die Ausführung begonnen werden kann. Diese Werkzeuge sind ° ° ° Unter Microsoft Windows der Performance-Monitor bzw. unter Linux die entsprechenden SNMP-Counter Der Trace-Kernel zur Identifikation von Bottlenecks am Client Datenbankanalyse-Tools 6 Optimierungen 6.7 Von Objekten und Töpfen Performance Monitor/SNMP Auf jeder Ebene, die vom Datenimport betroffen ist, gibt es eine Vielzahl von Performance-Countern, die Hinweise auf Bottlenecks liefern können. Damit kann die Ebene des Problems meist rasch eingegrenzt werden. Auf der betreffenden Ebene können Werkzeuge zur detaillierten Analyse eingesetzt werden. Prozessorzeit Der häufigste Grund für eine schlechte Performance ist die Auslastung der CPU am Client, auf dem der Import gestartet wurde, also auf dem der Fabasoft Components Kernel ausgeführt wird. Dabei ist zu beachten, dass auf einem Multiprozessorsystem die Gesamtlast meistens nicht mehr als der Anteil einer CPU betragen kann. Aussagekräftiger ist daher die Prozessorzeit des coo- bzw. w3wp-Prozesses. Unter Microsoft Windows wird dieser in Prozent einer CPU angegeben und kann folglich mehr als 100 % erreichen, wobei das derzeit eher eine Wunschvorstellung als Realität ist. Hat man also eine Last um die 100 %, dann bedeutet das, dass der Client ein Bottleneck darstellt, da zu viel am Client berechnet werden muss. Als Konsequenz muss man zum Beispiel anhand eines Kernel-Traces versuchen, die Aktionen zu identifizieren, die diese Last verursachen und diese entweder zu ersetzen oder zumindest so umzuschreiben, dass weniger CPU-Zeit für deren Verarbeitungen benötigt wird. Ist die Last am Fabasoft Components COO- oder MMC-Service sehr hoch, so kann man dagegen strukturell etwas unternehmen, indem man die Last auf mehrere Services verteilt. Eine Verringerung des Cache führt oft zu einer Verringerung des CPU-Verbrauchs des Service, jedoch oft auf Kosten der Gesamtperformance. Wird am Server, auf dem die Fabasoft Components MMC-Services betrieben werden, außer von den Fabasoft Components Services nennenswert Zeit benötigt, ist das möglicherweise auf einen Virenscanner zurückzuführen, der jedenfalls zur Migrationszeit deaktiviert werden sollte. Eine hohe Auslastung der CPU auf der Datenbank weist auf ineffiziente Abfragen hin, bei denen viele Datensätze durchsucht werden müssen, um ein Ergebnis zu berechnen. Wird also für eine Query ein Tablescan gemacht und diese Tabelle ist vollständig im Speicher enthalten, so wird diese Operation mit großem CPU-Auf- 117 wand durchgeführt. Das hat neben dem CPU-Aufwand auch noch zur Folge, dass der Datenbank-Cache permanent von dieser Tabelle belegt wird und nicht für andere Aufgaben zur Verfügung steht. Dieses Verhalten kann sich stark ändern, wenn entweder durch neu berechnete Statistiken die Zugriffspfade optimiert werden und daher der Aufwand für die Bearbeitung sinkt, oder aber durch das Datenwachstum die Daten nicht mehr vollständig im Cache gehalten werden können und dadurch das CPU-Problem auf Kosten eines Disk-Problems scheinbar behoben wird. Oft kann man bei der Beobachtung der CPU-Auslastung die Phasen einer Transaktion beobachten, wie die Verarbeitung am Client beginnt, eventuell Suchen auf der Datenbank durchgeführt werden, dann die Aktionen am Client durchgeführt werden und schließlich die Änderungen über das Fabasoft Components COO-Service bis zur Datenbank kommen. Auch aufgrund der Länge dieser Phasen kann man bestimmen, welche Komponente das Bottleneck ist. Um das besser beobachten zu können, ist es hilfreich, die Transaktionsgröße zu verändern, um zu Transaktionszeiten von ca. 10-20 Sekunden zu kommen. Festplatte Alle Daten kommen irgendwo von einer Festplatte und landen schlussendlich auf einer. Damit ist auch die Geschwindigkeit des Disksystems mitentscheidend für die Performance des Datenimports. Neben den Festplatten der Quell- und Zielsysteme wird auch die des Ladeclients beim Laden von Inhalten beansprucht. Ein entscheidender Faktor ist aber zumeist die Festplatte am Fabasoft Components MMC-Service und auf der Datenbank. Folgende Performancecounter sind unter Microsoft Windows verfügbar und für die Analyse interessant: ° ° ° ° ° ° % Disk Time Disk Queue Length Disk Read Bytes/s Disk Write Bytes/s Avg. Disk sec/Read Avg. Disk sec/Write 6 Optimierungen 6.7 Von Objekten und Töpfen Die Performance der Datenquelle ist erfahrungsgemäß von geringer Bedeutung, kann aber gerade bei der Verwendung von mehreren Ladeclients auch eine Rolle spielen. Bottlenecks auf Seite der Datenquelle können meistens durch die Konfiguration mehrerer Datenquellen entschärft werden, um die Quelldaten performant liefern zu können. Die lokale Festplatte des Ladeclients als Datenquelle zu verwenden, ist beim Laden von Inhalten keine gute Variante, sofern auch das DOCDIR-Verzeichnis auf dieser Platte liegt, da die Daten lokal auf die Festplatte kopiert werden, was im Allgemeinen langsam ist. Am Client ist die Festplatte im Fall vom Import von Inhalten ein limitierender Faktor. Alle Inhalte werden hier zuerst von der Quelle gelesen und in das DOCDIR-Verzeichnis kopiert, bevor sie beim Commit auf das Backen übertragen werden. Daher sollte man darauf achten, dass hier eine schnelle Festplatte verwendet wird und diese ausschließlich für das DOCDIR-Verzeichnis verwendet wird. Bei Importen ohne Inhalte wird die lokale Festplatte nicht wesentlich belastet. Beim Schreiben von Inhalten am Fabasoft Components MMC-Service wird nach dem Schreiben einer Datei ein Flush auf das Disksystem ausgeführt, um die neue Datei auf dem Medium zu sichern. Das bedeutet aber, dass dieser Zugriff synchron durchgeführt wird und effektiv auf die Festplatte gewartet wird. Dieser Schritt kann von einem SAN-System oder einem RAID-Controller mit Write-Buffer stark profitieren, da das Flush nicht auf die Disk warten muss. Hier spielt auch die Auswahl des RAID-Levels eine Bedeutung. Wie groß der Unterschied zwischen den einzelnen Konfigurationen konkret ist, hängt aber stark vom verwendeten Disk-Subsystem und dessen Cache ab. Besonders kritisch ist die Geschwindigkeit des Festplattensystems der Datenbank. Allerdings ist hier nicht jeder Disk-Zugriff schlecht oder bremst den Datenimport. Schließlich müssen die Daten der Datenbank ja irgendwie geschrieben werden. Die Daten der Tabellen werden asynchron zur Verarbeitung auf die Platten geschrieben und werden bis dorthin lediglich im Datenbank-Cache geändert. Die Konsistenz wird dabei über die Datenbank-Logdateien garantiert, die – und das ist ein kritischer Pfad – beim Commit synchron auf die Platte geschrieben werden müssen. Im Unterschied zu den Daten, die meist ein zufälliges Zugriffsmuster aufweisen, werden die Logs sequenziell geschrieben. Dem kann Rechnung getragen werden, indem die Logs auf dedizierten Festplatten platziert werden, die beim sequenziellen Schreiben kaum Bewegungen des Schreib- und Lesekopfes benötigen. Die Daten hingegen werden auf möglichst viele Platten verteilt, damit die zufälligen Zugriffe auf 119 möglichst vielen Einheiten parallel servisiert werden können. Mit Buffered-Write (auch Write-Back-Buffering genannt) kann man das Problem der Logs entschärfen, wenn das Storagesystem garantiert, dass selbst bei einem Absturz oder Stromausfall keine Änderungen verloren gehen. Die Verwendung von möglichst vielen Platten für die Datenbereiche ist auch heute noch Voraussetzung für den effizienten Betrieb einer Datenbank. Üblicherweise arbeitet ein Datenbanksystem anfangs, wenn der Datenbestand klein ist, optimal. In dieser Phase werden fast ausschließlich Schreiboperationen durchgeführt. Erst wenn die verwendete Datenbankgröße die Dimension des Buffer-Cache erreicht hat, kann es zu einem Einbruch der Performance kommen. Das erkennt man daran, dass auf einmal auch der Wert für „Disk Read Bytes/sec“ erhebliche Werte aufweist. Spätestens dann zeigt sich, ob die Indizes greifen und damit nicht wegen Tablescans ganze Tabellen im Speicher gehalten werden müssen. Reads wirken sich immer direkt auf die Performance aus, da diese in der Regel synchron zur Verarbeitung durchgeführt werden müssen, wenn also die Anwendung auf das Ergebnis einer Query wartet. Gelesen wird aber nur, was sich nicht gerade im Buffer-Cache befindet, sodass durch eine Vergrößerung des BufferCache die Wahrscheinlichkeit steigt, dass diese Daten im Cache gehalten werden können. Wie bei den Clientund Service-Caches schon angesprochen, ist eine effiziente Ausnützung der Caching-Mechanismen durch die Anwendung hilfreich. Damit kann die Menge der Daten, die in einer Phase benötigt wird, klein gehalten werden und die Wahrscheinlichkeit steigt, dass diese Daten bereits im Cache vorhanden sind. Diese Optimierung steht aber oft im Widerspruch zur Parallelisierung, die meistens darauf aufbaut, dass parallel mehrere verschiedene Datenbereiche verändert werden und so das Workingset vervielfacht wird. Wenn also das Bottleneck auf der Datenbank zu finden ist, kann potenziell eine spürbare Beschleunigung der Verarbeitung durch eine Reduktion der Parallelität erreicht werden. Der Wert von „Disk Write Bytes/sec“ ist bei den Logbereichen meistens relativ kontinuierlich, da hier bei jedem Commit Daten anfallen. Bei den Datenbereichen werden Änderungen im Normalfall im Hintergrund geschrieben, jedenfalls aber bei jedem Checkpoint (in Oracle z.B. beim Log-Switch). Ein Log-Switch ist eine synchrone Operation, die zu einer Wartezeit der Clients führt. Daher sollte die Anzahl der Checkpoints oder Log-Switches auch gering gehalten werden, was in Oracle beispielsweise eine entsprechende Dimensionierung der Logdateien voraussetzt. Erkennt man also aus den Countern, dass regelmäßig hohe Spitzen in der Schreiblast der Datenbereiche auftreten, in Verbindung mit Pausen in der sonstigen Verarbeitung, sollte man sich um die Checkpoints küm- 6 Optimierungen 6.7 Von Objekten und Töpfen mern. Die andere problematische Situation, dass Daten vorzeitig geschrieben werden müssen, entsteht wenn der Buffer-Cache zu klein ist und damit geänderte Blöcke vermehrt auf die Festplatten geschrieben werden müssen, um Platz für andere Datenblöcke freizumachen. Um diese Schreibprozesse vom Normalbetrieb unterscheiden zu können, benötigt man aber die Counter der Datenbanken, denn eine kontinuierliche Schreiblast auf den Datenbereichen ist durchaus normal und beeinträchtigt nicht die Performance, da sie asynchron zur eigentlichen Verarbeitung erfolgt. Die Werte von „Avg. Disk sec/Read“ und „Avg. Disk sec/Write” geben an, wie lange das Lesen bzw. Schreiben eines Datenblockes auf die Disk benötigt. Diese sollten speziell bei den Platten mit Datenbank-Logs im Bereich von maximal drei bis vier Millisekunden bleiben. Diese Zeit benötigt jede Transaktion mindestens auf der Datenbankseite für das Commit, denn jedes Datenbanksystem baut auf die Konsistenz des Datenbank-Logs auf und damit müssen die Daten der Transaktion und die Entscheidung, ob ein Commit oder Rollback durchgeführt wurde, auf dem Log persistiert sein, bevor das Commit beendet wird. Die Situation kann einerseits durch das Storagesystem beeinflusst werden, anderseits durch die Verwendung von größeren Transaktionen verbessert werden, da damit die Anzahl der Commits reduziert wird. Die „Disk Queue Length“ ist ein Maß für die Parallelität der Datentransferoperationen. Ist dieser Wert hoch, übersteigt er also deutlich die Anzahl der physischen Platten, steigt damit die Wartezeit der Prozesse auf die Daten an. Datenbanksysteme sind darauf ausgelegt, dass viele Vorgänge parallel bearbeitet werden, und daher profitieren diese direkt von einer höheren Parallelität am Speichersystem. Counter des Fabasoft Components Kernels Es gibt neben der bereits besprochenen CPU einige interessante Counter, mit denen die Aktivitäten des Fabasoft Components Kernels analysiert werden. Der Cache ist die zentrale Ressource des Fabasoft Components Kernels, wobei gerade hier nicht bei allen Imports eine hohe Hitrate erreicht werden kann. Die betreffenden Counter sind: ° ° % Cache-Hit Rate % Cache Used 121 ° ° ° ° ° Cached Objects Objects Completely Loaded (Bulk)/sec Objects Completely Loaded (Single)/sec Objects Partially Loaded (Bulk)/sec Objects Partially Loaded (Single)/sec Mit diesen Performanceindikatoren kann die Aktivität des Cache gut nachvollzogen werden. Ob der Cache effizient funktioniert, hängt vom Workingset des Imports ab. Werden sehr viele Objekte linear abgearbeitet, kann nur wenig Information aus dem Cache wiederverwendet werden und die Cache-Hit-Rate wird relativ niedrig werden (unter 80 %). Werden viele Operationen auf einem begrenzten Datenbestand durchgeführt, wird die Rate steigen. Ziel des Cache ist, die Werte der Counter „Object Completely/Partially Loaded (Bulk/Single)/sec“ durch die Zwischenspeicherung der Objekte niedrig zu halten. Diese geben die Anzahl der RPCs an, die zum Laden von Daten an das Fabasoft Components COO-Service abgesetzt werden. Vom Import werden dabei die in Zuordnungen verwendeten Eigenschaften aller beteiligten Objekte in Bulk-Operationen von den Services geladen. Werden jedoch in Aktionen Eigenschaften verwendet, die nicht direkt aus der Definition des Datenimports ersichtlich sind, so werden die darin referenzierten Objekte meist einzeln geladen und das führt zu einer großen Anzahl von einzelnen RPCs, die man dann unter „Objects Completely/Partially Loaded (Single)/sec“ beobachten kann. Eine Optimierung sollte daher diese RPCs verhindern, indem diese Aktionen unterbunden werden oder durch zusätzliche Zuordnungen ersetzt werden. Werden von einer Objektklasse zusätzlich zu den Eigenschaften, für die Zuordnungen definiert sind, auch noch andere Eigenschaften benötigt, so können diese beim Laden der Eigenschaften der Objekte gleich mitgenommen werden, indem eine Zuordnung zu dieser Eigenschaft definiert wird und der Änderungsmodus auf „Ignorieren“ gestellt wird. Um welche Eigenschaften es sich handelt, kann man aus dem Kernel-Trace ablesen. Nicht lösen lassen sich damit Objektbeziehungen, die nicht im Datenimport definiert sind, sondern die erst von der Methode verwendet werden. 6 Optimierungen 6.7 Von Objekten und Töpfen Die Objektsperren spiegeln sich in den Countern ° ° Objects Locked/sec Objects Unlocked/sec wieder. Wie schon öfters erwähnt, sind Objektsperren generell langsam und für die Funktion eines Imports nicht notwendig, vorausgesetzt die Daten werden nicht gleichzeitig von anderen Stellen aus geändert. Daher ist auch als Defaultwert beim Datenimport eingestellt, dass die Objekte nicht gesperrt werden. Ist diese Voraussetzung nicht erfüllt, weil der Import beispielsweise im laufenden Betrieb durchgeführt wird, so sollte die Objektsperre aktiviert werden. Damit werden nur jene Objekte modifiziert, bei denen die Sperre erfolgreich war. Neu erzeugte Objekte brauchen und können nicht gesperrt werden, da sie von keiner anderen Stelle aus geändert werden können, bis sie das erste Mal committet wurden. Implizite Sperren, wie zum Beispiel die der Rückwärtsverkettungsobjekte, können durch Mittel des Datenimports nicht verhindert werden. In diesem Fall muss in der Implementierung der Methoden berücksichtigt werden, dass im Fall eines Imports keine Sperre erfolgen sollen (TV_BATCHMODE). Der Wert des Counters „Queries/sec“ gibt die Anzahl der Suchabfragen wieder, die vom Fabasoft Components Kernel gemacht werden. Dieser Counter beinhaltet Suchen, die vom Datenimport selbst gemacht werden und zur Identifikation von Objekten notwendig sind, aber möglicherweise auch solche, die implizit von Methoden aus durchgeführt werden und zu einer Verzögerung des Datenimports führen können. Der Kernel-Trace hilft in diesem Fall den Kontext der Query herauszufinden. Die Versionierung von Objekten ist in den meisten Fällen unerwünscht, wird aber vielfach automatisch ausgelöst, wenn etwa der Bearbeiter wechselt. Diese automatische Versionierung, die in der Aktion vor dem Schreiben der Objekte ([email protected]:ObjectPrepareCommit) durchgeführt wird, bremst den Datenimport erheblich. Die Versionierung erkennt man am Counter „Versions Created/sec“, der bei vorliegender Versionierung einen Wert ungleich 0 liefert. Um die Versionierung zu verhindern, kann man die Aktion vor dem Schreiben der Objekte übergehen oder die Versionierung für die betroffene Objektklasse gänzlich deaktivieren. 123 Counter der Fabasoft Components Services Für alle Services gleich sind die Counter des Performanceobjekts COO-RPCs, die Informationen zu den RPC-Verbindungen und zu den Worker Threads der Services beinhalten. Während der Datendurchsatz, enthalten in den Countern ° ° ° Bytes Received/sec Bytes Sent/sec Bytes Total/sec ein Maß für die Netzwerkbelastung durch das Service ist, aber meistens nur einen Bruchteil der zur Verfügung stehenden Kapazität nutzt, ist der Wert von ° Allocated Threads von entscheidender Bedeutung. Wie im Abschnitt über die Serviceoptimierung bereits ausgeführt, führt eine zu geringe Anzahl von Worker Threads zu Wartezeiten und verlängert so die Ladezeit. Ist also die Anzahl der Allocated Threads über eine längere Zeit gleich der konfigurierten Anzahl der Worker Threads, sollte man diese Anzahl erhöhen. Das kann man direkt in der Registry unter HKEY_LOCAL_MACHINE\ SOFTWARE\Fabasoft\Fabasoft Components Server\Domain n.m\Service x im Wert Worker Threads tun oder aber in der Fabasoft Components Domäne am Serviceobjekt umstellen und Syncronize Registry Entries aufrufen. Jedenfalls muss das Service danach neu gestartet werden, um die Änderung zu übernehmen. Interessant am Fabasoft Components COO-Service sind die Cache-Counter ° ° ° ° ° % Cache Hit Rate % Cache Used Cached Objects Cache max MB Objects Loaded/sec 6 Optimierungen 6.7 Von Objekten und Töpfen 6.8 Der Kernel-Trace wobei die Counter von Version 6 auf Version 7 durch die Umstellung des Cachelayouts geändert wurden. In jedem Fall gibt die „% Cache Hit Rate“ einen Wert für die effiziente Nutzung des Cache an, wobei es aber sehr von der Struktur des Datenimports und damit des verwendeten Workingsets abhängt, ob der Cache überhaupt effizient nutzbar sein kann. Davon hängt es auch ab, ob eine Vergrößerung des Cache die Trefferquote erhöhen kann oder nur den Speicher mehr auslastet. Eine niedrige „% Cache Hit Rate“ und damit ein konstant hoher Wert von „Objects Loaded/sec“ weist jedenfalls auf ein Bottleneck hin. 6.8 Der Kernel-Trace Der Kernel-Trace ist eine schier unerschöpfliche Quelle der Information, allerdings oft nur für „Wissende“. An dieser Stelle sollen nur ein paar grundlegende Informationen erörtert werden, die man einfach aus dem Trace extrahieren kann und die für eine erfolgreiche Optimierung von Datenimporten notwendig sind. Erstellen der Traces Es gibt zwei Arten von Trace-Kernels: Der eine wird im jeweiligen plattformspezifischen Unterverzeichnis von „Setup/ComponentsBase/Trace“ der Installations-CD mit dem Produkt mitgeliefert und generiert die ausführliche Version der Traces. Der Umfang der ausgegebenen Traceausgaben wirkt jedenfalls abschreckend und bietet dem neugierigen Einsteiger zuerst einen derartigen Überfluss an Unverständlichkeiten, dass der Trace im besten Fall beim Fabasoft Support landet. Mit diesem Überfluss an Information ist auch ein erheblicher Aufwand bei der Generierung verbunden, sodass der eigentliche Datenimport sehr langsam wird. Allein um bis zum Datenimport zu kommen, vergeht bereits beträchtlich viel Zeit, speziell wenn man den Datenimport vom Webserver aus durchführt. Alternativ dazu gibt es für manche Versionen einen so genannten „EEE“-Trace-Kernel, der vom Support zur Verfügung gestellt werden kann, und der wesentlich weniger Informationen beinhaltet, als der vollständige Trace-Kernel. 125 Interpretation des Traces Das grundlegende Problem bei der Optimierung von Datenimporten ist die Frage: In welchen Methoden wird viel Zeit verbraucht? Genau das kann aber mit dem Kernel-Trace einfach herausgefunden werden, indem man aus dem Trace jene Zeilen herausfiltert, die den Start und das Ende eines Methodenaufrufs markieren. Vorausgesetzt man hat mit nur einem Thread einige wenige Datensätze importiert, sind vielfach die Schuldigen schnell gefunden. Der Header jeder Zeile des Traces enthält folgende Informationen: 05688: 7.027: KS|0x974|0x9F8|coo: <Info> 1. eine fortlaufende Zeilennummer 2. eine Zeitinformation relativ zum Startzeitpunkt des Traces 3. KS bedeutet, dass es sich um einen Kernel-Tracepunkt handelt 4. 0x974 ist die Prozess-ID 5. 0x9F8 ist die Thread-ID 6. und coo ist der Name der Anwendung 7. danach folgt die eigentliche Information Filtert man also den Trace nach „MethodImp::Call“, so erhält man eine Liste mit den Start- und Endpunkten der Methodenaufrufe. Die Zeile mit [email protected]:ImportDataSelectedObjects oder [email protected]:ImportRemote markiert dabei den Beginn des Imports. Danach wird ein Thread gestartet, der den eigentlichen Import vornimmt. In diesem Thread wird das Datenimportobjekt gesperrt, damit man einen Import nicht gleichzeitig durchführen kann, eventuell wird die Initialisierung der Suchbäume gemacht, der Reader Thread gestartet und erst danach werden die Worker Threads erzeugt. 6 Optimierungen 6.8 Der Kernel-Trace Verarbeitung eines Datenblocks In den Verarbeitungs-Threads werden die Datenblöcke, die von dem Reader Thread zusammengestellt werden, in folgender Weise verarbeitet: Phase 1: Suchen und Anlegen von Objekten Für jede Objektklasse werden entsprechend der Konfiguration die Objektinstanzen gesucht und angelegt. Dafür werden folgende Schritte durchgeführt: ° ° ° ° ° Objekte im lokalen Suchbaum suchen Objekte über die Objektsuche suchen Laden der Eigenschaften der Objekte Objekte erzeugen ([email protected]:ObjectCreate) ° Eigenschaftskonstruktoren (Die entsprechenden Eigenschaften finden sich im Trace neben dem Text AttrSet) ° Objektkonstruktor ([email protected]:ObjectContructor) Objekte sperren ([email protected]:ObjectLock) Phase 2: Setzen der Eigenschaften der Objekte Es werden pro Datensatz die Zuordnungen ausgewertet und dabei die Eigenschaften gesetzt. ° Aufruf des Filterskripts für Objekte (vor dem Commit) Commit der Transaktion ° ° Aufruf der Aktion vor dem Schreiben der Objekte Aufruf der Aktion zum Setzen der Eigenschaften 127 ° ° Commit auf der Datenbank Aufruf der Aktion nach dem Schreiben der Objekte Nachbearbeitung Zuletzt wird noch das Filterskript für Objekte nach dem Commit aufgerufen Interpretation Meistens ist es nicht wichtig, einen Kernel-Trace in seiner Gesamtheit zu verstehen. Im ersten Ansatz hilft es, anhand der aufgerufenen Methoden diejenige Stelle zu identifizieren, an der die meiste Zeit verbraucht wird und die dort verwendeten Funktionalitäten auf ihre Notwendigkeit hin zu untersuchen und Maßnahmen zu ergreifen, um diese zu entschärfen. 6 Optimierungen 6.8 Der Kernel-Trace 129 7 Konfiguration 7 der Datenquellen Nach dieser doch eher tiefer gehenden Betrachtung der Analyse- und Optimierungsmöglichkeiten widmet sich dieses Kapitel einem Thema, das viel direkter mit dem Datenimport zu tun hat: den konkret zur Verfügung stehenden Datenquellen und deren Konfiguration. Jeder Datenimport verarbeitet die Daten, die von einer bestimmten Datenquelle, die im Datenimportobjekt definiert ist, geliefert werden. Folgende Datenquellen stehen zur Verfügung: 1. ODBC (Open Database Connectivity): Sowohl auf der Microsoft Windows-Plattform als auch unter Linux steht mit ODBC eine allgemeine Programmschnittstelle für Datenbanken zur Verfügung, für die es auf beiden Plattformen eine Vielzahl von Treiberimplementierungen für verschiedene Datenbanken und Datenformate gibt. 2. Ausschließlich unter Microsoft Windows ist alternativ zu ODBC die Programmschnittstelle OLE DB (Object Linking and Embedding Database) für die Anbindung von Datenbanken definiert. Auch über diese Schnittstelle kann ein Datenimport seine Quelldaten bekommen. Von Fabasoft wird im Rahmen der Softwarekomponente [email protected] ein eigener Fileprovider auf Basis des OLE DB-Protokolls mitgeliefert, über den Inhalte des Dateisystems geladen werden können. 3. Speziell um aus Druckdateien Daten extrahieren zu können wurde der Report-Umsetzer implementiert, über den auf Basis von Beschreibungen, wo sich auf einem Formular die relevanten Informationen befinden, die Quelldaten für den Import extrahiert werden können. 4. Ganz spezielle Datenquellen lassen sich über Datenquellen-Skripts definieren, durch die (derzeit nur auf der Microsoft Windows-Plattform mit VBScript) auf beliebige Daten zugegriffen werden kann oder Daten algorithmisch generiert werden können. Mit Fabasoft Components Version 7 kommen einige andere Datenquellen hinzu: ° LDAP (Lightweight Directory Access Protocol) ist ein standardisiertes Protokoll für den Zugriff auf Directories, wie zum Beispiel das Microsoft Active Directory oder OpenLDAP, die unter anderem für die Verwaltung von Benutzerdaten und Gruppen verwendet werden. Mit der Anbindung von LDAP-Verzeichnissen ist eine 131 automatisierte Übernahme und Aktualisierung von Log-in-Informationen aus dem Directory in die Benutzerverwaltung der Fabasoft Components Domäne möglich. ° Seit kurzem stehen auch jene Datenbankanbindungen direkt für den Datenimport zur Verfügung, die von den Fabasoft Components Services verwendet werden. Über diese ADE DB genannte Schnittstelle sind vor allem die Datenbanksysteme Oracle (über die OCI-Schnittstelle) und PostgreSQL über deren datenbankspezifisches Interface angebunden und können damit auch ohne Installation von ODBC-Treibern verwendet werden. ° CSV-Dateien (Comma-Separated Values) sind sehr gut zum Transport von Tabellen in Textform geeignet. Zwar gibt es auf Microsoft Windows und Linux ODBC-Treiber, mit denen diese gelesen werden können, jedoch müssen diese am jeweiligen System extra konfiguriert werden. Daher wurde eine CSV-Datenquelle implementiert, mit deren Hilfe man auf jeder Plattform gleich in einfacher Weise CSV-Dateien importieren kann. Für die Definition der Datenquelle gibt es im Fabasoft Win32-Client der Version 6 einen Wizard. Diesen startet man durch Auswahl des Menübefehls Datenquelle definieren aus dem Kontextmenü des Datenimportobjekts. Zuerst wählt man aus den möglichen Typen der Datenquelle die gewünschte aus. Danach kommt man je nach Datenquelle zu unterschiedlichen Dialogen, wie sie in Abbildung 17 für ODBC und OLE DB dargestellt sind. 7. Konfiguration der Datenquellen 133 Abbildung 17 Auswahl einer ODBC- bzw. OLE DB Datenquelle Bei ODBC kommt man auf den Auswahldialog für ODBC-Datenquellen, der von Microsoft Windows zur Verfügung gestellt wird und über den eine File- bzw. Machine Data Source erstellt und ausgewählt werden kann. FileDatenquellen werden durch eine Datei definiert, deren Name zur Definition der Datenquelle verwendet wird. Eine Machine Data Source ist in der Microsoft Windows-Registry definiert und kann daher nicht so einfach im Rahmen der Migration verteilt werden. Bei OLE DB wird stattdessen der Auswahldialog für die UDL-Datei angezeigt. In einer UDL-Datei werden die Verbindungsparameter zur OLE DB-Datenquelle als Text abgelegt. Als Verbindungsinformation können entweder der Dateipfad zur UDL-Datei oder aber die Parameter aus der Datei selbst verwendet werden. Der nächste Schritt ist in jedem Fall die Auswahl der zu importierenden Tabelle. Angeboten werden alle Tabellen und Views im aktuellen Schema (siehe Abbildung 18). Abbildung 18 Auswahl der Tabelle und der Spalten In unserem Beispiel wird die Tabelle Customers der Northwind-Datenbank gewählt und nach der Bestätigung mit OK kommt die Auswahl der Spalten: Hier können die zu verwendenden Spalten ausgewählt werden, sodass nach der Bestätigung dieses Dialogs mit OK das Datenimportobjekt mit den Verbindungsinformationen und einer Liste von Zuordnungen mit den gewählten Spalten gefüllt wird. Im Fall der Auswahl der Skriptdatenquelle wird eine Liste der verfügbaren Skriptobjekte und Skript-Komponentenobjekte angezeigt. Nach der Auswahl des entsprechenden Skripts folgt auch hier die Auswahl der verfügbaren Spalten. 7. Konfiguration der Datenquellen 7.1 ODBC 7.1 oDBC ODBC (Open Database Connectivity) ist sowohl auf der Microsoft Windows-Plattform als auch unter Linux verfügbar. Auf Basis dieser standardisierten Programmschnittstelle ist eine ganze Reihe von Treibern für die Anbindung verschiedenster Datenbanken verwendbar. Während in Microsoft Windows ODBC standardmäßig mit dem Betriebssystem mitinstalliert wird, muss es unter Linux explizit konfiguriert werden. Einige der am häufigsten verwendeten Treiber und deren Konfiguration sind im Folgenden beschrieben. Microsoft Excel Microsoft Excel eignet sich sehr gut zur manuellen Erfassung von Listen, da die meisten Benutzer mit dem Medium vertraut sind. Entwirft man entsprechende Vorlagen, können zum Beispiel Daten aus vielen Abteilungen zusammengetragen werden, die automatisiert weiterverarbeitbar sind. Ein Problem entsteht allerdings gerade durch die Tatsache, dass die Benutzer beim Eingeben der Daten viele Freiheiten nutzen und es im Nachhinein mit einem erheblichen Aufwand verbunden ist, die Daten wieder in ein Zielformat zu konsolidieren. Außerdem eignet sich das Medium nur für kleine Datenmengen und nicht für alle Datentypen. Kritisch bei Microsoft Excel ist die Verwendung von Datumswerten, da Microsoft Excel diese intern als Fließkommawerte speichert und nur in der Darstellung umrechnet. In der Datenquelle erscheinen jedoch nicht die formatierten Werte, sondern die internen Daten. Diese müssen daher im Filterskript für Rohdaten erst auf Datumswerte umgerechnet werden. Auch bei Zahlen mit Nachkommastellen unterscheiden sich die formatierten Werte in der Anzeige von Microsoft Excel von den Werten, die über OLE DB an den Datenimport geliefert werden und oft noch weitere Kommastellen oder die für Fließkommawerte typischen periodischen Dreier oder Neuner enthalten. Um Daten von Microsoft Excel als Datenquelle über ODBC zur Verfügung zu haben, muss man in Microsoft Excel jedenfalls eine Spaltenüberschrift für jede Datenspalte definieren, die als Spaltenname auch im Datenimportobjekt verwendbar ist. Als Datenbereich ist entweder eine ganze Seite verwendbar, die unter dem Namen des 135 Datenblattes mit einem „$“ am Ende als Tabellenname erreichbar ist, oder man definiert einen Namen für einen ausgewählten Datenbereich, unter dem die Daten dann auch für den Datenimport verfügbar sind (siehe Abbildung 19). Abbildung 19 Definition eines Datenbereichs in Microsoft Excel Jedenfalls ist darauf zu achten, dass während des Datenimports die Datei nicht in Microsoft Excel geöffnet ist, da die Datei sonst nicht vom Treiber geöffnet werden kann. CSV und Tab-Separated Files CSV-Dateien (Comma-Separated Values) oder Tab-Separated Files sind Text-Dateien, die pro Zeile einen Datensatz enthalten und bei denen die einzelnen Spalten durch ein bestimmtes Trennzeichen voneinander getrennt werden. Was auch immer das Trennzeichen ist, ein Beistrich, ein Strichpunkt oder ein Tabulator-Zei- 7. Konfiguration der Datenquellen 7.1 ODBC chen, diese Dateien eignen sich gut für den Datentransport, wenn man einige grundlegende Konventionen für die Daten vereinbart: Trennzeichen Meistens werden Beistriche, Strichpunkte oder Tabulatorzeichen als Spaltentrennzeichen verwendet und CRLF als Zeilentrennzeichen. Strichpunkte und Tabulatorzeichen eignen sich besonders gut, da sie im Gegensatz zu Punkten oder Beistrichen nicht als Dezimal- oder Datumstrennzeichen verwendet werden und daher (außer in Zeichenketten) nicht vorkommen können. Zeichenketten Zeichenketten, die Sonderzeichen oder aber auch das Trennzeichen enthalten, werden oft unter Anführungszeichen gesetzt. Damit auch Anführungszeichen in diesen Texten enthalten sein können, werden diese im Text verdoppelt. Die Verwendung eines Escape-Characters (z.B. Backslash „\“) ist eher unüblich. Zahlenformat Es muss festgelegt werden, welches Zeichen als Komma und welches optional auch als Tausendertrennzeichen verwendet wird. Ob eine sprachspezifische Version oder ein standardisiertes Format verwendet wird, liegt im Ermessen der beteiligten Personen. Format für Datum und Zeit Hier gilt ähnliches wie bei den Zahlenformaten. Zusätzlich kann hier auch noch die Verwendung von Zeitzonen eine Rolle spielen. Zeichensatz Umlaute und Sonderzeichen werden in verschiedenen Zeichensätzen unterschiedlich kodiert und daher ist es wichtig, die Datei mit demselben Zeichensatz zu lesen wie sie geschrieben wurde. Wenn man vom Unicode- 137 Zeichensatz und dessen Repräsentationen UTF-8 und UTF-16 absieht, so kann man bei der Verarbeitung meistens nicht automatisch auf den richtigen Zeichensatz schließen, sondern muss diesen explizit festlegen, um vor Überraschungen sicher zu sein. Spaltenbezeichnungen Die Spaltennamen werden in CSV-Dateien in der Regel in der ersten Zeile der Datei spezifiziert. Dabei sollte beachtet werden, dass diese keine Sonderzeichen enthalten sollten (speziell ein „@“-Zeichen am Beginn einer Spaltenbezeichnung würde zu Problemen beim Datenimport führen). Auch sollten sich die Bezeichnungen von Spalten nicht nur durch die Groß-/Kleinschreibung unterscheiden, da diese Unterscheidung nicht an allen Stellen berücksichtigt wird. Datentypen Die Spaltenbreiten werden oft von den Treibern aufgrund des Inhalts der ersten Datensätze analysiert. Besser ist es jedoch definitiv festzulegen, welchen Datentyp eine Spalte hat und welche maximale Länge deren Inhalte haben kann. Für den Datenimport ist eine Erkennung des Datentyps nicht erforderlich, da die Werte beim Import immer als Zeichenketten gespeichert und erst beim Setzen der Eigenschaften auf den Datentyp der Eigenschaft konvertiert werden. Leerwerte am Zeilenende Einige Datenquellen optimieren die CSV-Datei dahingehend, dass alle Spaltentrennzeichen weggelassen werden, denen bis zum Zeilenende keine Spalte mehr folgt, die einen Wert enthält. Diese Optimierung wird nicht von allen Treibern unterstützt und sollte daher nur verwendet werden, wenn sie definitiv von allen beteiligten Programmen richtig erkannt wird. Der Microsoft Text-Treiber kann die üblichen CSV-Dateien lesen und kann über Konfigurationsdateien, die im Verzeichnis der Quelldatei liegen müssen, relativ genau konfiguriert werden (siehe [MSDN04]). 7. Konfiguration der Datenquellen 7.1 ODBC 7.2 OLE DB In Fabasoft Components Version 7 wurde als Alternative zum ODBC-Text-Treiber ein eigener Datenquellentyp implementiert, der ohne Verwendung von ODBC einen Import von CSV-Dateien implementiert. Oracle unter Linux Während unter Microsoft Windows ODBC bereits bei der Installation des Betriebssystems mitinstalliert wird, gibt es dafür unter Linux ein eigenes Projekt namens unixODBC (siehe [Gorh06]). Dort können auch die jeweiligen Versionen für die einzelnen Plattformen bezogen werden. Der erste Schritt der Konfiguration von ODBC-Treibern unter Linux ist also die Installation des unixODBC-Pakets. Der nächste Schritt ist die Installation des ODBC-Treibers für die jeweilige Datenbank. Für Oracle können diese Pakete von der Webseite [Orac07] für die jeweilige Client-Version heruntergeladen werden. Die weitere Konfiguration ist einerseits in der Installationsanleitung der Pakete, andererseits für Version 6.1 in einem White-Paper im Fabasoft technet beschrieben. 7.2 OLE DB Die von Microsoft in vielen Produkten unterstützte Datenbankschnittstelle OLE DB bietet einige Vorteile gegenüber der älteren ODBC-Technologie, wie sie auch unter Linux verfügbar ist, jedoch werden die meisten Features von Fabasoft Components/COLD nicht benötigt. Trotzdem ist sie gegenüber ODBC zu bevorzugen, da es in der Regel die aktuelleren Treiber für OLE DB gibt. Für die Rückwärtskompatibilität gibt es einen OLE DB-Treiber für ODBC-Datenquellen, mit dem unter Verwendung der OLE DB-Schnittstellen auf ODBC-Datenquellen zugegriffen werden kann, was aber in der Regel für Fabasoft Components/COLD-Datenimporte nicht besser ist, als gleich direkt ODBC zu verwenden. Die Verbindungsparameter zur OLE DB-Datenquelle können in der Datenquelle entweder gleich direkt angegeben werden, oder durch eine Datei mit der Endung „udl“. Erstellt man eine UDL-Datei, so kann man im Microsoft Datei-Explorer über den Kontextmenübefehl Properties die Verbindungsparameter interaktiv festlegen. 139 Definition der OLE DB-Datenquelle Will man zum Beispiel eine Datenquelle zur Microsoft SQL Server Demo-Datenbank Northwind, die am lokalen SQL-Server liegt, definieren, so kann man wie folgt vorgehen: 1. In einem beliebigen Ordner eine Textdatei anlegen und dieser die Endung „.udl“ geben, z.B. „Northwind.udl“. 2. Den Kontextmenübefehl Properties auswählen (auf Deutsch Eigenschaften) (siehe Abbildung 20). Abbildung 20 Definition einer OLE DB-Datenquelle 3. Auf der Registerkarte „Provider“ den „OLE DB-Treiber für den Microsoft SQL Server“ auswählen. 4. Danach auf der Registerkarte „Connection“ den Namen des Servers eingeben oder auswählen, „Use Windows NT Integrated security“ als Log-in-Information angeben und unter Punkt 3. die gewünschte Datenbank auswählen. 7. Konfiguration der Datenquellen 7.2 OLE DB Abbildung 21 Konfiguration der OLE DB-Quelldatenbank 5. Mit „Test Connection“ kann die Verbindung zur ausgewählten Datenbank geprüft werden. 6. Werden die Eingaben mit OK bestätigt, so werden die Parameter in der UDL-Datei als Textinformation abgespeichert. Diese Datei kann man zum Beispiel im Microsoft Editor öffnen und sieht dann folgende Information: [oledb] ; Everything after this line is an OLE DB initstring Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=Northwind;Data Source=FOMDEMO Quelltext 2: UDL-Datei mit Parametern 141 Im Datenimport kann nun als Datenquelle entweder der Dateiname der UDL-Datei verwendet werden, oder man kopiert aus dieser Datei die Zeile, die mit „Provider=…“ beginnt in die Datenquelleneigenschaft. Welche Variante man bevorzugt, hängt davon ab, ob man lieber die Datenquelle an einer Stelle im Dateisystem haben will und in mehreren Datenimporten verwenden kann, oder ob man ohne externe Dateien auskommen will und daher die Verbindungsinformation direkt ins Datenimportobjekt eintragen will. Microsoft SQL Server Microsoft SQL Server ist die am meisten verbreitete Datenbank auf der Microsoft Windows-Plattform. Mit der „Microsoft SQL Server Desktop Engine“ (MSDE) 2000 bzw. der „SQL Server 2005 Express Edition“ stehen auch freie Versionen der Datenbank zur Verfügung, die im Rahmen von Migrationen als Medium für Zwischendatenbanken eingesetzt werden können. Die Limitierungen – vor allem der Datenbankgröße auf zwei bzw. vier Gigabyte – müssen dabei jedoch beachtet werden. Views, Cursors, Functions und Stored Procedures können die Vorbereitung der Rohdaten für den Import wesentlich erleichtern, indem schon vor dem Import Überprüfungen der Wertebereiche oder der Datenkonsistenz auf den Quelldaten durchgeführt werden. Bei einigen Migrationen, bei denen Daten in anderen Formaten geliefert wurden (z.B. in CSV-Dateien oder als Microsoft Excel-Arbeitsmappen) wurden die Daten zuerst mit DTS-Paketen auf den SQL-Server übernommen, bevor die konsolidierten Daten dann importiert wurden. Damit konnten Konsistenzprüfungen durchgeführt und so einige Fehlerquellen ausgeschlossen werden. Außerdem kann man über Cursors auf Basis der Rohdaten Nummeratoren nachbilden und so die aufwendige Durchführung der Berechnung der Nummeratoren beim Ladevorgang ersetzen. Microsoft Access Ähnlich flexibel sind auch die Möglichkeiten von Microsoft Access, jedoch sollte man diese nur bei kleineren Datenbeständen verwenden und wenn keine zu hohen Anforderungen an die Parallelität und Performance gestellt werden. Als OLE DB-Treiber wird hier der „Microsoft Jet 4.0 OLE DB Provider“ verwendet und in der Datenquelle der Name der MDB-Datenbankdatei angegeben. 7. Konfiguration der Datenquellen 7.2 OLE DB File Provider Der File Provider ist ein OLE DB-Provider, der auf der Microsoft Windows-Plattform mit der Softwarekomponente [email protected] mitgeliefert wird, und mit dem Verzeichnisbäume iteriert werden können. Er eignet sich besonders zur Übernahme von Datenbeständen in identisch strukturierte Ablagen in einer Fabasoft Components Domäne. Mit dem File Provider werden nicht die Inhalte an den Datenimport übergeben, sondern die Datei- und Ordnernamen, also genau jene Informationen, die vom Datenimport benötigt werden, um die Inhaltseigenschaften von Objekten zu setzen bzw. die Hierarchien aufzubauen. Das zu übernehmende Verzeichnis wird im Datenimportobjekt als Tabellenname eingetragen (z.B. „c:\“ oder „d:\myfolder“). Darauf wird beim Importieren vom OLE DB-Provider eine Tabelle mit folgenden Spalten erzeugt: SPALTE BESCHREIBUNG FolderName Name des Verzeichnisses FolderPath Pfad des Verzeichnisses ParentName Name des übergeordneten Verzeichnisses von <Folder> ParentPath Pfad des übergeordneten Verzeichnisses von <Folder> Tabelle 5: Spalten des File Providers Weil meistens nicht für alle Dateien Objekte in der Fabasoft Components Domäne erstellt werden sollen, können die Dateierweiterungen der zu berücksichtigenden Dateitypen im Datenimportobjekt im Tabellennamen konfiguriert werden. Die Dateierweiterungen werden vor dem Tabellennamen in einer geschwungenen Klammer getrennt durch Strichpunkte angegeben. 143 Gültige Angaben sind beispielsweise ° ° ° {doc} c:\ {doc;xls;tif} c:\myfolder c:\myfolder Für jede der angegebenen Dateierweiterungen werden zusätzliche Spalten „<ext>Name“ und „<ext>Path“ angelegt, wobei „ext“ für die Dateierweiterung steht. Wenn nur ein Startverzeichnis angegeben wird und keine Erweiterungen, dann wird als Default „{doc;xls;tif}“ angenommen. Beispiel: Import aus Dateisystem Die Angabe von „{doc} c:\“ als Tabelle im Datenimportobjekt erzeugt die Spalten FolderName, FolderPath, ParentName, ParentPath, docName und docPath. Für die Datei „c:\myfolder\temp\test.doc“ wird dann der folgende Datensatz erzeugt: FolderName = temp FolderPath = c:\myfolder\temp ParentName = myfolder ParentPath = c:\myfolder docName = test docPath = c:\myfolder\temp\test.doc Nach der Angabe der Datenquelle müssen noch die Zuordnungstabelle und die Tabelle der Objektreferenzen im Datenimportobjekt so gesetzt werden, dass automatisch die Ordnerobjekte und (für das Beispiel) die darin liegenden Word-Objekte erzeugt werden. Der Trick dabei ist, dass mit den Folder*-Spalten ein Ordnerobjekt und mit den Parent*-Spalten ein weiteres Ordnerobjekt – unterscheidbar durch die Werte in der ID-Spalte in der Zuordnungstabelle – beschrieben wird. 7. Konfiguration der Datenquellen 7.2 OLE DB In der Objektreferenzen-Tabelle wird dann das Ordnerobjekt, das durch die Folder-Spalten definiert wurde, dem Ordnerobjekt, das durch die Parent-Spalten definiert wurde, in der Eigenschaft Objektliste ([email protected]:objchildren) zugeordnet. Weiters werden erzeugte Inhaltsobjekte (hier Word-Objekte) dem Ordnerobjekt zugewiesen. In dem in Abbildung 22 abgebildeten Datenimportobjekt ist der Datenimport für dieses Beispiel konfiguriert, wobei folgende Punkte speziell zu beachten sind: 1. Damit beim mehrfachen Importieren derselben Dateien bereits früher erzeugte Objekte wiedergefunden werden, wird der genaue (vollständige) Dateiname in der Eigenschaft Betreff ([email protected]: objsubject) der Objekte gespeichert. Im Objektnamen wird nur der lesbarere Name ohne Pfad und Dateierweiterung eingetragen. 2. Die Inhaltseigenschaft Inhalt ([email protected]:contcontent) in der zusammengesetzten Eigenschaft Hauptinhalt ([email protected]:content) eines erzeugten Word-Objekts wird auf den genauen Dateinamen docPath gesetzt. Fabasoft Components/COLD erkennt beim Importieren, dass es sich um einen gültigen Dateinamen handelt und das Zielattribut eine Inhaltseigenschaft ist und kopiert die Datei in das Inhaltsattribut des Objekts. Die Originaldatei wird nicht gelöscht. Gesetzte Optionen sind „Muss im Aggregat definiert sein“, damit keine Hauptinhaltseigenschaft erzeugt wird, wenn der Dateiname nicht gesetzt ist, und „Dateiname“, damit der Wert immer als Dateiname interpretiert wird, selbst wenn die Datei nicht mehr existieren würde. Anderenfalls würde der Wert der Spalte über eine temporäre Datei als Inhalt dem Hauptinhalt zugewiesen, was in diesem Fall nicht sinnvoll ist. 3. Die Eigenschaft Endung der Datei ([email protected]:contextension) in der zusammengesetzten Eigenschaft Hauptinhalt ([email protected]:content) eines erzeugten Word-Objekts wird auf den Festwert „doc“ gesetzt, damit beim Bearbeiten des Objekts automatisch die korrekte Applikation gestartet werden kann. 145 Abbildung 22 Konfiguration eines Imports mit OLE DB File Provider 7. Konfiguration der Datenquellen 7.2 OLE DB 7.3 Report-Umsetzer 7.3 report-Umsetzer Mit dem Report-Umsetzer steht in Fabasoft Components/COLD eine Datenquelle zur Verfügung, mit der Werte aus Textdateien gelesen werden können, wie sie beispielsweise beim Ausdruck von Formularen oder Reports generiert werden. Die Definition der Feldnamen und der Datenbereiche erfolgt in einem Report-Umsetzer-Objekt oder einem Report-Umsetzerkomponentenobjekt. Wie bei den Datenimportobjekten unterscheiden sich die beiden Objektklassen nur durch die Tatsache, dass Komponentenobjekte als Elemente von Softwarekomponenten leichter extrahiert und in anderen Fabasoft Components Domänen wieder eingespielt werden können. Dafür benötigen sie eine Referenz und die Zuordnung zu einer Softwarekomponente. Abbildung 23 Konfiguration des ReportUmsetzers 147 Hier eine Beschreibung der einzelnen Eigenschaften: EIGENSCHAFT BESCHREIBUNG Steuerzeichen ignorieren Wird die Eigenschaft auf „Ja“ gesetzt, so werden alle Steuerzeichen (< ASCII(32)) unterdrückt. OEM-zu-ANSIKonvertierung Je nach Einstellung werden die gelesen Daten im ANSI- oder OEM-Zeichensatz interpretiert Kennung für neue Seite Zeichenkette, die die Dokumente innerhalb einer Druckdatei voneinander trennt (Codes „{CR}“, „{LF}“, „{TAB}“ und „{FF}“ stehen für die Steuerzeichen „0xD“, „0xA“, „0x9“ und „0xC“). Ein „^“-Zeichen bedeutet, dass die gesuchte Zeichenkette am Anfang einer Zeile stehen muss. Kennung für Zeilenende Zeichenkette, die die einzelnen Zeilen des Dokuments trennt. Es sind hier dieselben Codes verwendbar, wie bei der Kennung für neue Seite. Seitentrennung gehört zu Beim Import von Druckdateien trennt die unter Kennung für neue Seite eingestellte Kennung für neue Seite die einzelnen gedruckten Dokumente (Objekte). Je nach Druckjob gehört die Zeile mit dem Trennstring nun entweder an den Anfang eines neuen Dokuments, an das Ende des gerade gelesenen Dokuments oder ist überhaupt nicht Teil des Dokuments. Diese Information kann für die Bestimmung von Positionen innerhalb des Dokuments wichtig sein. Folgende Optionen stehen zur Verfügung: ° ° ° Nächster Seite: Die Trennzeile ist der Anfang eines neuen Dokuments Vorheriger Seite: Die Trennzeile ist das Ende des gerade gelesenen Dokuments Keiner Seite: Die Trennzeile ist nicht Teil eines Dokuments 7. Konfiguration der Datenquellen 7.3 Report-Umsetzer 7.4 Skript-Datenquelle 7.4 EIGENSCHAFT BESCHREIBUNG Ignoriere erste Spalten der Zeilen Anzahl der Spalten, die am Anfang jeder Zeile ignoriert werden sollen. Auf einigen Host-Systemen werden in den ersten Zeichen jeder Zeile Steuerinformationen für den Druck gespeichert, die für den Datenimport irrelevant sind. Ignoriere erste Zeilen der Seite Anzahl der Zeilen, die am Anfang jedes Dokuments ignoriert werden sollen Umsetzung von Feldern In dieser Liste werden die Felder definiert, die aus der Quelldatei extrahiert und an den Datenimport übergeben werden sollen. Es werden dafür rechteckige Bereiche relativ zum Seitenanfang oder relativ zu einer Textmarke definiert. skript-Datenquelle Die Implementierung einer Datenquelle über ein Skript erlaubt die Anbindung sehr unterschiedlicher Datenquellen bzw. die Interpretation von Dateiformaten, die von den anderen Datenquellentypen nicht unterstützt werden. Die Implementierung der Skripts kann unter Microsoft Windows in VBScript erfolgen, indem in einem SkriptObjekt oder einem Skript-Komponentenobjekt die folgenden Funktionen implementiert werden. Die Beispiele zeigen die Implementierung eines Readers für spezielle XML-Dateien. Im globalen Skriptbereich können allgemeine Initialisierungsaufgaben erledigt sowie Variablen deklariert und initialisiert werden, die in mehreren Funktionen benötigt werden oder deren Werte über mehrere Aufrufe einer Funktion hinweg erhalten bleiben sollen. Der globale Bereich wird beim Laden des Skripts einmalig durchgeführt. 149 Beispiel: Globaler Skriptbereich 'LANGUAGE="VBScript" Dim xmldoc, xmlcustomer Dim columnNames Dim coort Set coort = CreateObject("Coo.Runtime") Set xmlcustomer = Nothing Quelltext 3: Globaler Skriptbereich Initialisierung Die Funktion Init wird zu Beginn des Datenimports einmalig aufgerufen. Als Parameter wird der Wert der Eigenschaft Tabelle aus dem Datenimportobjekt übergeben. Hier werden typischerweise Dateien geöffnet oder Variablen gesetzt, die vom Wert der Tabellen-Eigenschaft abhängig sind. Beispiel: Öffnen der XML-Datei Function Init(table) Set xmldoc = CreateObject("MSXML2.DOMDocument.6.0") xmldoc.async = False If xmldoc.load(table) Then Set xmlcustomer = xmldoc.documentElement.firstChild End If End Function Quelltext 4: Öffnen der XML-Datei Die in der Eigenschaft Tabelle definierte XML-Datei wird mit dem XML-Parser geparst und die Variable, die den aktuellen Datensatz enthält, auf den ersten Knoten der Liste gesetzt. 7. Konfiguration der Datenquellen 7.4 Skript-Datenquelle Anfangsdatensatz Diese Funktion muss nicht implementiert werden und wird genau dann einmal aufgerufen, wenn in der Eigenschaft Anfangsdatensatz ein Wert größer als „1“ eingetragen ist. Dann wird dieser Wert minus 1 als Parameter übergeben. Beispiel: Überspringen von Datensätzen Function Skip(rows) Do While Not(xmlcustomer Is Nothing) And rows > 0 Set xmlcustomer = xmlcustomer.nextSibling rows = rows – 1 Loop End Function Quelltext 5: Überspringen von Datensätzen Im vorliegenden Beispiel wird die übergebene Anzahl von Knoten im XML übersprungen. Spaltennamen Auch die Funktion, in der die Spaltennamen berechnet werden, wird einmal aufgerufen und als Rückgabeparameter muss eine Liste von Spaltennamen definiert werden. Die Anzahl der Spalten muss mit der Anzahl der Werte übereinstimmen, die vom Skript später pro Datensatz gesetzt werden. 151 Beispiel: Berechnen der Spaltennamen Function GetHeader(columns) Redim columnNames(100) Redim columns(100) Dim columnCount, xmlnode columnCount = 0 Set xmlnode = xmlcustomer.firstChild Do While Not(xmlnode Is Nothing) columnNames(columnCount) = xmlnode.nodeName columns(columnCount) = xmlnode.nodeName Set xmlnode = xmlnode.nextSibling columnCount = columnCount + 1 Loop Redim Preserve columnNames(columnCount - 1) Redim Preserve columns(columnCount - 1) End Function Quelltext 6: Berechnen von Spaltennamen In diesem Beispiel werden die Namen der Subelemente des ersten XML-Knotens als Namen für die Spalten verwendet. Datensätze Während der Verarbeitung wird die Funktion GetRecord solange immer wieder aufgerufen, bis entweder die konfigurierte Anzahl der Datensätze erreicht ist, oder der Parameter IsValid dieser Funktion auf „False“ gesetzt wird. Im Parameter data wird ein String-Array übergeben, das im Skript mit den Werten eines Datensatzes befüllt werden muss. Ist das Ende der Daten erreicht, so muss der Parameter IsValid auf „False“ gesetzt werden, ansonsten auf „True“. 7. Konfiguration der Datenquellen 7.4 Skript-Datenquelle Beispiel: Berechnen eines Datensatzes Function GetRecord(data, isvalid) If xmlcustomer Is Nothing Then isvalid = False Else coort.Trace "" For i = LBound(columnNames) To UBound(columnNames) Set xmlnodelist = xmlcustomer.getElementsByTagName(columnNames(i)) If xmlnodelist.length > 0 Then data(i) = xmlnodelist.item(0).text coort.Trace columnNames(i) + ": " + data(i) End If Next Set xmlcustomer = xmlcustomer.nextSibling isvalid = True End If End Function Quelltext 7: Berechnen eines Datensatzes Hier werden die Werte der Subelemente der Knoten als Datenwerte der Spalten ausgelesen, die beim Aufruf von GetHeader berechnet wurden. Ende Die Funktion Terminate kann implementiert werden um nach dem Lesen der Datensätze einen Status zu setzen, temporäre Dateien zu löschen oder andere Aufräumtätigkeiten durchzuführen. Zu diesem Zeitpunkt muss aber die Verarbeitung der Datensätze noch nicht abgeschlossen sein. Es dürfen daher keine Aktionen angestoßen werden, die schon auf die Beendigung des Imports angewiesen sind. 153 Beispiel: Aufräumen des Imports Function Terminate() Set xmlcustomer = Nothing Set xmldoc = Nothing End Function Quelltext 8: Aufräumen des Imports Hier werden die XML-Ressourcen explizit freigegeben. 7.5 LDAP-Datenquelle LDAP (Lightweight Directory Access Protocol) ist ein standardisiertes Protokoll für den Zugriff auf Directories, wie zum Beispiel das Microsoft Active Directory oder OpenLDAP, die unter anderem für die Verwaltung von Benutzerdaten und Gruppen verwendet werden. Mit der Anbindung von LDAP-Verzeichnissen ist eine automatisierte Übernahme und Aktualisierung von Log-in-Informationen aus dem Directory in die Benutzerverwaltung der Fabasoft Components Domäne möglich. Beispiel: Objekt im Directory dn: cn=John Doe,dc=example,dc=com cn: John Doe givenName: John sn: Doe telephoneNumber: +1 888 555 6789 telephoneNumber: +1 888 555 1234 mail: [email protected] manager: cn=Barbara Doe,dc=example,dc=com objectClass: inetOrgPerson objectClass: organizationalPerson objectClass: person objectClass: top 7. Konfiguration der Datenquellen 7.4 Skript-Datenquelle 7.5 LDAP-Datenquelle Die Objekte in einem Directory sind hierarchisch strukturiert und jeder Eintrag darin besteht aus mehreren Eigenschaften. Der Distinguished Name (dn) ist der Primärschlüssel für jeden Eintrag im Directory. Die Eigenschaften sind in einem Schema definiert und können auch mehrere Werte haben. Die Namen der Eigenschaften sind gleichzeitig die Spaltennamen des Datenimports, mit der Ausnahme, dass die Spaltennamen um Formatierungsoptionen erweitert werden können, die zur Interpretation der Werte notwendig sein können. Folgende Parameter werden für die Definition der Verbindung zum LDAP-Server unterstützt: ° ° ° host: Hostname des Servers ° ° ° ° ° searchbase: Basisverzeichnis für die Datenauswahl port: TCP-Port für die Verbindung zum LDAP-Server username/password: Accountname und Passwort für die Verbindung zum LDAP-Verzeichnis. Werden diese Parameter unter Microsoft Windows nicht angegeben, wird „Integrated Authentication“ (über Kerberos) für die Anmeldung verwendet. timeout: Timeout für die Abfrage in Sekunden sizelimit: Größenlimit für die Anfrage pagesize: Maximale Anzahl der Werte pro Eigenschaft dirsyncflags/dirsyncsize: Flags und Abfragegröße für die Synchronisation von Verzeichnissen Beispiel: Verbindungsparameter LDAP-Datenquelle host=FSPCOLD; searchbase=OU=Test Users,DC=coldtest,DC=com; timeout=100; pagesize=300 Diese Konfiguration definiert die Übernahme von Datensätzen vom Host „FSPCOLD“. Es werden jene Objekte übernommen, die in der Hierarchie unterhalb von der Organizational Unit „OU=Test Users,DC=coldtest,DC=com“ liegen. 155 In der Tabelleneigenschaft werden die Einschränkungen bei der LDAP-Abfrage spezifiziert. Um Benutzerobjekte zu laden ist das zum Beispiel „(objectClass=user)“. In vielen Fällen müssen jedoch vor dem Laden der Daten Formatierungen durchgeführt oder Spalten berechnet werden. Dafür ist das Filterskript für Rohdaten die richtige Stelle. Im Fall des Microsoft Active Directory wird zum Beispiel aus dem Attribut userAccountControl das Bit für die Information ausgelesen, ob ein Benutzerkonto aktiv ist oder nicht. Dieses Skript finden Sie im Kapitel „Zusätzliche Beispiele“ auf Seite 159. 7.6 ADE DB Seit kurzem stehen auch jene Datenbankanbindungen direkt für den Datenimport zur Verfügung, die von den Fabasoft Components Services verwendet werden. Über diese ADE DB genannte Schnittstelle sind vor allem die Datenbanksysteme Oracle (über die OCI-Schnittstelle) und PostgreSQL über deren datenbankspezifisches Interface angebunden und können damit auch ohne Installation von ODBC- oder OLE DB-Treibern verwendet werden. Die Parameter sind genau dieselben, wie sie auch für die Verbindungseinstellungen der Fabasoft Components Backendservices zu den jeweiligen Datenbanken verwendet werden. Als Datenbank-Schnittstellen stehen damit neben OLE DB und ODBC, die ja auch direkt als Datenquelle verwendet werden können, die Anbindung von Oracle und PostgreSQL über deren eigenes Datenbank-API zur Verfügung. Somit kann jetzt mit derselben Datenbankkonfiguration des Backendservers auch der Datenimport durchgeführt werden. Ein Unterschied zu den anderen Datenbank-Datenquellentypen ergibt sich dadurch, dass die ADE DB keine allgemeine Schnittstelle bietet, mit der die Datentypen der Spalten ermittelt werden können. Wenn bei den Zuordnungen die Option „Dateiname“ gesetzt ist, dann wird der Wert in eine Datei gespeichert, wenn die Option „Inhalt als Wert“ gesetzt ist, dann wird eine Zeichenkette von 1 MB Größe verwendet, sonst werden generell Zeichenketten mit 256 Zeichen verwendet. Werte die über diese Grenzen hinausgehen werden abgeschnitten. 7. Konfiguration der Datenquellen 7.5 LDAP-Datenquelle 7.6 ADE DB 7.7 CSV 7.8 Roll-Forward Log-Reader 7.7 cSV Die Prinzipien der CSV-Dateien (Comma-Separated Values) wurden schon im Abschnitt über ODBC besprochen. Seit Fabasoft Components Version 7 kann man CSV-Dateien auch ohne ODBC durch die Verwendung der CSV-Datenquelle importieren. 7.8 Roll-Forward Log-Reader Nicht als Datenquelle auszuwählen, aber dennoch von der Funktion wie eine Datenquelle ist der Roll-Forward Log-Reader, der implizit verwendet wird, wenn der Menübefehl Roll-Forward zum Starten des Datenimports verwendet wird. Der Roll-Forward Log-Reader liest die Daten aus dem Roll-Forward-Log, das im Protokollobjekt hinterlegt ist und in dem die Daten jener Datensätze eingetragen sind, die beim letzten Datenimport nicht vollständig korrekt verarbeitet werden konnten. Damit können, wenn Verarbeitungsfehler aufgetreten sind, konkret jene Datensätze noch einmal importiert werden, bei deren Verarbeitung die Fehler aufgetreten sind. 157 8 159 8 Zusätzliche Beispiele Beim Importieren von Daten steht man bei jeder Migration immer vor ähnlichen Situationen, wodurch die Wiederverwendung und Anpassung von konkreten Datenimporten, Filterskripts oder Konfigurationen besonders wichtig ist. In diesem Kapitel finden Sie neben erklärenden Beispielen auch einige Skripts, die von konkreten Migrationen abgeleitet sind und die leicht an Anforderungen eines Datenimports angepasst werden können. 8.1 Datenquellenskript XML-Datenquelle In diesem Skript werden aus einer XML-Datei Datensätze importiert. Es wird dabei angenommen, dass in der XML-Datei unter dem Wurzelknoten eine Liste von XML-Elementen angeführt ist und für jeden Knoten ein Datensatz erzeugt werden soll. Die Werte der Spalten stammen aus den Subelementen der einzelnen Listenknoten, die Bezeichnungen aus den Elementnamen dieser Subelemente. ' LANGUAGE="VBScript" Dim xmldoc, xmlcustomer Dim columnNames Dim coort Set coort = CreateObject("Coo.Runtime") Set xmlcustomer = Nothing Function Init(table) Set xmldoc = CreateObject("MSXML2.DOMDocument.6.0") xmldoc.async = False If xmldoc.load(table) Then Set xmlcustomer = xmldoc.documentElement.firstChild End If End Function Function GetHeader(columns) Redim columnNames(100) Redim columns(100) Dim columnCount, xmlnode columnCount = 0 Set xmlnode = xmlcustomer.firstChild Do While Not(xmlnode Is Nothing) columnNames(columnCount) = xmlnode.nodeName columns(columnCount) = xmlnode.nodeName Set xmlnode = xmlnode.nextSibling columnCount = columnCount + 1 Loop Redim Preserve columnNames(columnCount - 1) Redim Preserve columns(columnCount - 1) End Function Function Skip(rows) Do While Not(xmlcustomer Is Nothing) And rows > 0 Set xmlcustomer = xmlcustomer.nextSibling rows = rows – 1 Loop End Function Function GetRecord(data, isvalid) If xmlcustomer Is Nothing Then isvalid = False Else coort.Trace "" For i = LBound(columnNames) To UBound(columnNames) Set xmlnodelist = xmlcustomer.getElementsByTagName(columnNames(i)) If xmlnodelist.length > 0 Then data(i) = xmlnodelist.item(0).text coort.Trace columnNames(i) + ": " + data(i) End If Next Set xmlcustomer = xmlcustomer.nextSibling isvalid = True End If End Function Quelltext 9: Datensätze aus XML-Datenquelle importieren 8.2 Filterskript für Rohdaten Berechnete Spalten Gleich im Anfangskapitel wurde der Datenimport aus der Tabelle Customers der Datenbank Northwind beschrieben. Darin trat das Problem auf, dass der ContactName sowohl Vor- als auch Nachname beinhaltet. Um diesen in zwei Teile zu zerlegen, wurde im ersten Ansatz eine SQL-View verwendet. Alternativ dazu kann die Berechnung der beiden Werte auch in einem Filterskript für Rohdaten erfolgen. 8. Zusätzliche Beispiele 8.1 Datenquellenskript 8.2 Filterskript für Rohdaten Dazu definiert man bei den Zuordnungen eine Zeile, in der nur die Spalte auf „CustomerName“ gesetzt ist aber keine Objektklasse und keine Eigenschaft. Dadurch wird die Spalte zwar von der Datenquelle angefordert und steht dem Filterskript zur Verfügung, es werden aber keine Zuordnungen von diesem Wert vorgenommen. Als Spaltennamen für die Zuordnung zum Vor- bzw. Nachnamen setzt man „@CustomerFirstname“ bzw. „@CustomerLastname“. Das „@“-Zeichen an der ersten Stelle des Namens bedeutet, dass die Spalte nicht aus der Datenquelle stammt, sondern vom Filterskript für Rohdaten berechnet wird. Nun aber zum Skript, das hier in zwei Versionen vorliegt, als VBScript und als JavaScript mit der gleichen Funktionalität. ' LANGUAGE="VBScript" ' Available global Variables: ' coort (Components Runtime Object) ' cootx (Components Transaction Object) ' coolog (XMLLogWriter Object, ' valid only if logging is not disabled) ' Define global variables holding the indexes ' of data columns Dim ContactNameIdx, ContactFirstnameIdx, ContactLastnameIdx ' mark indexes as invalid ContactNameIdx = -1 ContactFirstnameIdx = -1 ContactLastnameIdx = -1 ' this procedure is called by the reader thread for ' each data record Function Main(columns, data, changed, skip) ' Parameters: ' columns: [IN] Array of Strings: column names ' data: [INOUT] Array of Strings: column data ' changed: [OUT] Boolean: Set this flag to 'true' if ' 'data' has been changed ' skip: [OUT] Boolean: Set this flag to 'true' ' to skip the whole record Dim i, nameParts If ContactNameIdx = -1 Then For i = LBound(columns) To UBound(columns) If columns(i) = "ContactName" Then ' Index of ContactName column ContactNameIdx = i ElseIf columns(i) = "@ContactFirstname" Then ' Index of ContactFirstname column ContactFirstnameIdx = i 161 ElseIf columns(i) = "@ContactLastname" Then ' Index of ContactLastname column ContactLastnameIdx = i End If Next End If If ContactNameIdx <> -1 And ContactFirstnameIdx <> -1 \ And ContactLastnameIdx <> -1 Then nameParts = Split(data(ContactNameIdx), " ") Select Case UBound(nameParts) Case 1: ' ContactName consists of 2 words data(ContactFirstnameIdx) = nameParts(0) data(ContactLastnameIdx) = nameParts(1) Case 2: ' ContactName consists of 3 words If nameParts(1) = „de“ Then data(ContactFirstnameIdx) = nameParts(0) data(ContactLastnameIdx) = nameParts(1) + " " + \ nameParts(2) Else data(ContactFirstnameIdx) = nameParts(0) + " " +\ nameParts(1) data(ContactLastnameIdx) = nameParts(2) End If Case Else data(ContactLastnameIdx) = data(ContactNameIdx) End Select changed = True Else coolog.LogString "Index not found" skip = True End If End Function Quelltext 10: Filterskript für Rohdaten (VBScript) Alternativ dazu das folgende JavaScript: // LANGUAGE="JScript" /*----------------------------Available global Variables: coort (Components Runtime Object) cootx (Components Transaction Object) coolog (XMLLogWriter Object, valid only if logging is not disabled) -----------------------------*/ 8. Zusätzliche Beispiele 8.2 Filterskript für Rohdaten // this procedure is called by the reader thread // for each data record /*----------------------------function MainEx(params) Parameters: params: [INOUT] Dictionary params.values: Dictionary: Column values params.changed: bool: Set this flag to 'true' if 'params.values' has been changed params.skip: bool: Set this flag to 'true' to skip the whole record -----------------------------*/ function MainEx(params) { var nameParts = params.values.ContactName.split(" "); switch (nameParts.length) { case 2: params.values.SetEntryValue("@ContactFirstname", 0, nameParts[0]); params.values.SetEntryValue("@ContactLastname", 0, nameParts[1]); break; case 3: if (nameParts[1] == "de") { params.values.SetEntryValue("@ContactFirstname", 0, nameParts[0]); params.values.SetEntryValue("@ContactLastname", 0, nameParts[1] + " " + nameParts[2]); } else { params.values.SetEntryValue("@ContactFirstname", 0, nameParts[0] + " " + nameParts[1]); params.values.SetEntryValue("@ContactLastname", 0, nameParts[2]); } break; default: params.values.SetEntryValue("@ContactLastname", 0, params.values.ContactName); break; } params.changed = true; } Quelltext 11: Filterskript für Rohdaten (JavaScript) 163 Berechung der Werte aus der LDAP-Datenquelle In dem folgenden Filterskript für Rohdaten werden aus der LDAP-Eigenschaft userAccountControl die Information extrahiert, ob das User-Objekt ein Computeraccount ist oder ob das Objekt inaktiv ist. Je nachdem wird der Datensatz gänzlich ignoriert oder die Eigenschaft Aktiv auf „1“ oder „0“ gesetzt. Außerdem werden aus den Werten der LDAP-Eigenschaft proxyAdresses E-Mail-Adressen im korrekten Format für die Eigenschaften in Fabasoft Components generiert. ' LANGUAGE="VBScript" ' Available global Variables: ' coort (Components Runtime Object) ' cootx (Components Transaction Object) ' coolog (XMLLogWriter Object, valid only if logging is not disabled) ' global code and variables go here Dim userAccountControl, dn, sAMAccountName, proxyAddresses, emailAddress, emailKnownType userAccountControl = -1 dn = -1 sAMAccountName = -1 proxyAddresses = -1 emailAddress = -1 emailKnownType = -1 Function Main(columns, data, changed, skip) ' Parameters: ' columns: [IN] Array of Strings: column names ' data: [INOUT] Array of Strings: column data ' changed: [OUT] Boolean: Set this flag to 'true' if 'data' has been changed ' skip: [OUT] Boolean: Set this flag to 'true' to skip the whole record Dim cnpos, nextpos, esc Dim lastDn, lastUserStatus ' this procedure is called by the reader thread for each data record If userAccountControl=-1 Then For i = LBound(columns) To UBound(columns) If columns(i) = "userAccountControl" Then userAccountControl = i 8. Zusätzliche Beispiele 8.2 Filterskript für Rohdaten ElseIf columns(i) = "dn" Then dn = i ElseIf columns(i) = "sAMAccountName" Then sAMAccountName = i ElseIf columns(i) = "proxyAddresses" Then proxyAddresses = i ElseIf columns(i) = "@emailAddress" Then emailAddress = i ElseIf columns(i) = "@emailKnownType" Then emailKnownType = i End If Next End If If userAccountControl > -1 Then If Len(data(userAccountControl)) > 1 Then If (CLng(data(userAccountControl)) AND 512) = 0 Then skip = True ElseIf (CLng(data(userAccountControl)) AND 2) = 0 Then data(userAccountControl) = "1" changed = True Else data(userAccountControl) = "0" changed = True End If End If End If If skip = False AND dn > -1 AND sAMAccountName > -1 Then If Len(data(dn)) > 1 AND Len(data(sAMAccountName)) > 0 Then cnpos = InStr(data(dn), "DC=") domain = "" If cnpos > 0 Then esc = False cnpos = cnpos + 3 Do While cnpos < Len(data(dn)) If Not esc AND Mid(data(dn), cnpos, 1) = "," Then Exit Do Else domain = domain + Mid(data(dn), cnpos, 1) If Mid(data(dn), cnpos, 1) = "\" Then esc = Not esc Else esc = False End If End If cnpos = cnpos + 1 Loop End If 165 If Len(domain) > 0 Then data(sAMAccountName) = domain + "\" + data(sAMAccountName) changed = True End If End If End If If skip = False And proxyAddresses > -1 AND emailAddress > -1 AND emailKnownType > -1 Then If Len(data(proxyAddresses)) > 0 Then addrs = Split(data(proxyAddresses), ":") If UBound(addrs) = 1 Then data(emailAddress) = addrs(1) If LCase(addrs(0)) = "x400" Then data(emailKnownType) = "2" ElseIf LCase(addrs(0)) = "smtp" Then data(emailKnownType) = "1" End If changed = True End If End If End If End Function Quelltext 12: Filterskript zum Zugriff auf LDAP-Datenquelle 8.3 Filterskript für Objekte Versionierung Im folgenden Skript wird von jenen Objekten eine Version gezogen, die in der aktuellen Transaktion verändert wurden, was durch den Aufruf der Funktion IsChanged ermittelt wird. Da gerade erzeugte Objekte nicht versioniert werden können, wird zusätzlich mit der Funktion IsCreated geprüft, ob die Objekte in dieser Transaktion erzeugt wurden. 8. Zusätzliche Beispiele 8.2 Filterskript für Rohdaten 8.3 Filterskript für Objekte Es wird dabei das erste Objekt aus dem Parameter objects verwendet. Wenn mehrere Objekte an dem Datenimport beteiligt sind, müssen die beiden Parameter classes und ids herangezogen werden, um das richtige Objekt herauszufinden. Selbst wenn zum Zeitpunkt des Aufrufs des Skripts die Eigenschaften bereits geändert sind, so wird von der Versionierung der Originalzustand des Objekts – als jener vor der aktuellen Transaktion – in der Version abgelegt. Die Änderungen werden beim Commit in der aktuellen Version durchgeführt. // LANGUAGE="JScript" // Available global Variables: // coort (Components Runtime Object) // cootx (Components Transaction Object) // coolog (XMLLogWriter Object, valid only if logging is not disabled) // cootx is not the transaction that created the objects, // but the data main transaction of the data import // (equal to <cootx> in the filter for raw data)! // global code and variables go here function Main(classes, ids, objects, threadtx) { // Parameters: // classes: [IN] Array of Strings: object class references // ids: [IN] Array of Integer: object class id’s // objects: [IN] Array of Components Objects // threadtx: [IN] Components Transaction Object of the writer thread var obj = objects.toArray()[0]; if (obj) { if (!threadtx.IsCreated(obj) && threadtx.IsChanged(obj)) { obj.CallAction(threadtx, "ObjectFixVersion", false, "Datenimport"); } } } Quelltext 13: Skript zur Versionierung 167 8.4 Filterskript für Objekte nach dem Commit Archivierung Ähnlich wie bei der Versionierung der Objekte im Filterskript für Objekte vor dem Commit, können Objekte und Inhalte nach dem Commit auf ein Archiv gelegt werden. Hat man also vor, die importierten Daten sofort auf ein Archiv auszulagern, um zum Beispiel Platz auf den Festplatten-Volumes der Fabasoft Components MMCServices zu schaffen, kann man das gleich nach dem Anlegen des Objekts im Filterskript für Objekte nach dem Commit tun. 8. Zusätzliche Beispiele 8.4 Filterskript für Objekte nach dem Commit 169 9 171 9 Glossar Aggregat Seite 49 Eine Zusammenfassung von mehreren Eigenschaften zu einer großen Containereigenschaft, die auch als Liste dargestellt werden kann. Ein Aggregat, oder auch zusammengesetzte Eigenschaft genannt, ist ein Container für mehrere Formularblockelemente. Ein Aggregat entspricht einem Formularblock. Seite 79 Archivierung Unter Archivierung wird die Ablage und das Wiederbeschaffen von Informationen in IT-Systemen verstanden. Seite 48 Archivstore Um das Archivsystem verwenden zu können, muss in Fabasoft Components mindestens ein Objekt der Klasse Archiv-Store definiert werden, welches einen Verzeichnispfad zur Archivierung von Geschäftsobjekten angibt. AT-Service Seite 75 Am AT-Server laufen die Fabasoft Components AT-Services für die Durchführung automatischer Aufgaben(Automated Tasks) im Kontext eines Benutzers ohne Benutzerinteraktion ab. Get-Aktion Bezeichnet die bei einer Eigenschaft hinterlegte Aktion nach dem Lesen der Eigenschaft. Seite 44 Seite 67 Indizierung Das automatische Speichern von in Dateien im Dateisystem enthaltenen Texten und Metadaten in einem Index. Dieses Verfahren erlaubt eine schnelle Suche in den Inhalten von Dateien, da nicht in den Dateien selbst, sondern im Index nach dem Suchbegriff gesucht wird. Komponentenobjekt Seite 21 Objekt, das von einer bestimmten Objektklasse abgeleitet ist und Konfigurationsdaten enthält. Komponentenobjekte werden zu Softwarekomponenten zusammengefasst. Migrationszwischenformat Seite 96 Dieser Begriff wird im Zusammenhang mit der Ablösung von Altsystemen und Übernahme von Daten (Migrationen) verwendet. Beim Migrationszwischenformat handelt es sich um eine vorgegebene Datenstruktur in einer relationalen Datenbank. Mit Hilfe der Fabasoft Components/COLD-Technologie werden die Daten dann aus dem Migrationszwischenformat in eine Fabasoft Components Domäne geladen. Seite 61 Objektadresse Jedes Objekt in einer Fabasoft Components Domäne hat eine Objektadresse. Diese ist weltweit eindeutig. Seite 44 Set-Aktion Bezeichnet eine Aktion, die beim Setzen eines Eigenschaftswertes ausgeführt wird. Eine derartige Aktion kann direkt bei der Eigenschaft eingetragen werden. Transaktion Seite 28 Eine Abfolge von mehreren Operationen, die entweder alle oder gar nicht durchgeführt werden müssen. Erfolgt in einer Operation ein Fehler, müssen alle zuvor durchgeführten Änderungen wieder rückgängig gemacht werden. Werden alle Operationen ohne Fehler durchgeführt, so können alle Änderungen übernommen werden. Seite 83 TriStep Spezielle Ausprägung eines Objektkonstruktors. Ist der Konstruktor einer Objektklasse als TriStep implementiert, wird beim Erzeugen einer neuen Objektinstanz die Aktion [email protected]:PreGUI aufgerufen und anschließend das für diese Objektklasse definierte Konstruktorformular angezeigt. Nach dem Schließen des Konstruktorformulars durch den Benutzer wird die Aktion [email protected]:PostGUI ausgeführt. Webbrowser-Client Seite 88 Unter einem Webbrowser-Client wird in diesem Buch ein Arbeitsplatzrechner (PC-Arbeitsplatz) mit Betriebssystem (z.B. Microsoft Windows), Webbrowser (Mozilla Firefox oder Microsoft Internet Explorer) und entsprechender Anwendungssoftware (z.B. OpenOffice.org oder Microsoft Office) verstanden. 9. Operations Manager Log 173 Wrapper-Aktion Seite 116 Eine Aktion, die vor bzw. nach der Ausführung der eigentlich aufgerufenen Aktion ausgeführt wird. Pre-Wrapper werden vor der Ausführung der aufgerufenen Aktion ausgeführt, Post-Wrapper nach deren Ausführung. 10 175 10 Abbildungsverzeichnis Abbildung 1 Abbildung 2 Abbildung 3 Abbildung 4 Abbildung 5 Abbildung 6 Abbildung 7 Abbildung 8 Abbildung 9 Abbildung 10 Abbildung 11 Abbildung 12 Abbildung 13 Abbildung 14 Abbildung 15 Abbildung 16 Abbildung 17 Abbildung 18 Abbildung 19 Abbildung 20 Abbildung 21 Abbildung 22 Abbildung 23 Customers: Datenquelle und Zuordnungen Customers: Objektbeziehungen und Klasseneigenschaften Customers: Daten importieren Datenfluss bei der Verarbeitung eines Datenimports Zuordnungen mit Spaltenansicht Objektbeziehungen mit Spaltenansicht Kasseneigenschaften mit Spaltenansicht Einstellungen und Optionen des Datenimports Protokollobjekt Skripts des Datenimports Schlüssel und Änderungsmodus Definition mehrerer Listeneinträge pro Datensatz Methoden zur Vermeidung doppelter Objekte Auswahl der Suchmethoden Verarbeitung der Datensätze im Worker Thread Optimierungsoptionen des Datenimports Auswahl einer ODBC- bzw. OLE DB Datenquelle Auswahl der Tabelle und der Spalten Definition eines Datenbereichs in Microsoft Excel Definition einer OLE DB-Datenquelle Konfiguration der OLE DB-Quelldatenbank Konfiguration eines Imports mit OLE DB File Provider Konfiguration des Report-Umsetzers 22 23 24 27 32 33 34 35 36 38 43 53 58 65 78 98 133 134 136 140 141 146 147 11 177 11 Literaturverzeichnis [Gorh06] Gorham, Nick: „The unixODBC Project home page“. URL: http://www.unixodbc.org [Stand: 4.6.2007]. [MoNo03] Morle, James/Nørgaard, Mogens: „BAARF: Enough is enough“. URL: http://www.miracleas.com [Stand: 4.6.2007]. [MSDN04] Microsoft Developer Network: „Schema.ini File (Text File Driver)“. URL: http://msdn.microsoft.com/library/default.asp?url=/library/ en-us/odbc/htm/odbcjetschema_ini_file.asp [Stand: 4.6.2007]. [Orac07] Oracle Technology Network: „Instant Client Downloads for Linux x86“. URL: http://www.oracle.com/technology/software/tech/oci/ instantclient/htdocs/linuxsoft.html [Stand: 4.6.2007].