Zur Zeit wird gefiltert nach: sharepoint
Filter zurücksetzen

19.07.2012
17:03

Sharepoint - Verwaiste SiteCollection finden und aufräumen

Sharepoint, die einen lieben, die anderen hassen es und ich weiss immer noch nicht so recht was ich davon halten soll.

Mein bis jetzt gröbstes Projekt in Sharepoint ist die Aufbereitung und Transformation von Daten aus 3 alten Sharepoint-Lösungen (SP 2007), damit diese in eine neue Lösung importiert werden können. Der Metadaten-Ansatz hat sich dabei als sehr hilfreich heraus gestellt. Diese Metadatenschicht besteht aus knapp. 4 GB an Daten, was ca. 0.2% des Gesamtdatenvolumen der Migration entspricht.

In der Zukunft und Big Data wird das für Entwickler mit der Einstellung: "Die Datenhaltung ist mir scheiss egal" noch eine echte Knacknuss werden. Denn solche Datenmengen bestrafen diese Einstellung, ganz gleich ob SQL oder NoSQL.

Ein Problem in Sharepoint sind unter anderem verwaiste SiteCollections, die zudem doppelt vorhanden sein können. Solch eine Konstellation kann die Sharepoint Deployment Content API aus dem Tritt bringen und die Datenaufbereitung erschweren.

Mittlerweile habe ich gelernt, dass der Lösungsweg für dieses Problem eine Datenbank-Reparatur ist.

Der Befehl dafür lautet:

 


stsadm -o databaserepair -databasename <<Datenbank>> -url http://Site

bzw. ab SP 2010 mit Powershell (Merci an Thorsten Hans für den Tipp):


Add-PSSnapIn Microsoft.SharePoint.PowerShell 
$db = Get-SPDatabase "<<Datenbank>>"; 
$db.Repair($false); 
$db.Update();

In der Metadatenebene haben gerade die doppelten SiteCollections, von der jeweils eine verwaist war, den Logmechanismus aktiviert. :) Einfach ausgedrückt, diese haben es nicht aus der Staging-Area heraus geschafft, so dass die Integrität der Metadaten jederzeit gewährleistet ist. Im Log ist dann ersichtlich welche der vielen Datenbanken eine Datenbankreparatur erfordern, damit die Sharepoint Content Deployment API wieder fehlerfrei bei diesen SiteCollections arbeiten kann.

Weitere Informationen zum Thema:

Zurück

15.03.2012
22:54

Sharepoint Deployment API zwischen Version 2007 und 2010

Meine Migrationsstory geht weiter. Nachdem der Export vorlag, stand ich das erste Mal an, weil mich bei einem Testimport die Fehlermeldung:

The version of the package 12.0.10.0 is different from the current version this program supports, 14.0.0.0

begrüsste. Nach ein bisschen suchen fand ich einen Ansatz. Die Package Files, speziell die SystemData.xml muss angepasst werden, damit sich der Inhalt aus Sharepoint 2007 in Sharepoint 2010 importieren lässt.

In der XML-Datei muss dazu auf SchemaVersion-Ebene die Version, dass aktuelle Patchlevel (Build) und die Datenbankversion (DatabaseVersion) angegeben werden. Nachfolgendes XML zeigt einen Ausschnitt aus der SystemData.xml:


<?xml version="1.0" encoding="utf-8"?>
<SystemData xmlns="urn:deployment-systemdata-schema">
    <SchemaVersion Version="14.0.0.0" Build="14.0.6109.5002" DatabaseVersion="133739" SiteVersion="0" ObjectsProcessed="27" />
    <ManifestFiles>
        <ManifestFile Name="Manifest.xml" />
    </ManifestFiles>
    <SystemObjects>
        ...
    </SystemObjects>
    <RootWebOnlyLists />
</SystemData>

Müssten die Daten nicht noch bereinigt und transformiert werden, wäre ich bereits am Ende. Nun geht es daran, die XML-Dateien so aufzubereiten, damit die Daten in das Format der neuen Lösung des Zielsystems importiert werden können.

ETL geht auch einfacher, aber nicht unbedingt wenn Sharepoint im Spiel ist. ;-)

Weitere Informationen zum Thema:

Zurück

Parallelisierung wenn eine relationale Datenbank im Spiel ist

Nachdem ich mir das erste Mal eine CLR-Funktion für eine Datenmigration gebaut habe, geht es nun daran Codeteile zu parallelisieren, damit die Migrationsdauer verkürzt werden kann. Ein paar Herausforderungen dabei:

  • Ich habe die Auflage das Ganze in .NET 3.5 zu entwickeln, sodass ich die TPL nicht nutzen kann.
  • Direkte Insert, Update und Delete-Anweisungen dürfen nicht ausgeführt werden. Es muss die Sharepoint Content Deployment API verwendet werden.

Im Blogpost fand ich einen interessanten Ansatz, wie in .NET 3.5 eine parallele Schleife realisiert werden kann.

Also den ersten Test erstellt. Eine Stub-Methode die 10 Sekunden wartet, geschrieben und 5-mal aufgerufen. Testkriterium: Ausführungsdauer weniger als 15 Sekunden bestehen, funktioniert.

Anschliessend den integrativen Test in Verbindung mit einer Datenbank. Testkriterium: Auditinformationen in Datenbank schreiben nicht erfüllt, tatsächlich:

Transaction (Process ID XX) was deadlocked on lock | communication buffer resources with another process and has been chosen as the deadlock victim. Rerun the transaction. at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, …

Ein Codestück, das die Verbindung zur Datenbank herstellt, hat folgenden Aufbau:


public static void ExecuteNonQuery(SqlCommand cmd)
{
    try
    {
        using (var conn = GetSqlConnection())
        {
            cmd.Connection = conn;
            cmd.ExecuteNonQuery();
         }
    }
    catch (Exception ex)
    {
       log.Error(ex.Message, ex);
        throw;
    }
}

Als ersten Ansatz stellte ich dieses Codestück um und machte einen asynchronen Aufruf daraus.

Es dauerte unwesentlich länger, aber auch dieser Code erzeugte Deadlocks auf der Datenbank. In diesem Moment kam ich zum Schluss: So, genug gebastelt jetzt wird analysiert wer bzw. was diese Deadlocks auf der Datenbank auslösen. Wenn ich mit dem SQL Server arbeite, nutze ich üblicherweise den SQL Server Profiler für diese Zwecke.

Dieser kann über das SQL Server Management Studio (SSMS) unter dem Menüpunkt Tools gestartet werden. Nach der Verbindung zur Datenbank kann auf der ersten Dialogmaske das Template TSQL_Locks ausgewählt werden.

Abbildung 1 Einstellungen für Deadlock-Tracing
Abbildung 1

Unter dem zweiten Tab können weitere Einstellungen vorgenommen werden, darauf gehe ich nicht näher ein. Mit klick auf die Schaltfläche "Run" wird die Ablaufverfolgung gestartet.

Nach dem Start des Integrationstests wurde alles aufgezeichnet und es dauerte nicht lange, bis die ersten Meldungen im Tracelog erschienen.

Abbildung 2 Informationen im Trace über Deadlocks
Abbildung 2
Abbildung 3 Grafische Übersicht, welcher Prozess zum Opfer erklärt wurde
Abbildung 3

Mit diesen Informationen lässt sich im ersten Augenblick nichts anfangen, der Deadlock Graph ist noch interessant, da hier ersichtlich wird, welcher Prozess durch den aktuellen zum Opfer erklärt wurde.

Nun stellt sich die Frage, welcher Befehl ausgeführt wurde. Dies lässt sich über die Object Id ermitteln. Im Trace steht die Object ID 693577509. Wer sich dahinter versteckt, lässt sich mit folgendem SQL-Befehl ermitteln:

Der Deadlock Graph zeigt zudem noch nützliche Informationen an, mit denen sich auch die Befehle ermitteln lassen. In meinem Beispiel trat der Deadlock während eines Inserts in die Log-Tabelle auf.

Abbildung 4 Angaben welcher Prozess zum Deadlock führte
Abbildung 4

SELECT
	name
	, type
	, type_desc
 FROM sys.objects
 WHERE object_id IN (693577509)

Wer das obige Codebeispiel nochmals betrachtet sieht, dass die Methode statisch ist. Hier ging ich von der Annahme aus, dass dieser Ansatz "Thread Safety" ist. Um das Problem zu beheben, habe ich die statische Methode wie folgt angepasst:


static readonly object locker = new object();

public static void ExecuteNonQuery(SqlCommand cmd)
{
    try
    {
        lock (locker)
        {
            using (var conn = GetSqlConnection())
            {
                cmd.Connection = conn;
                cmd.ExecuteNonQuery();
            }
        }
    }
    catch (Exception ex)
    {
        log.Error(ex.Message, ex);
        throw;
    }
}

Nach dieser Anpassung waren die Deadlocks verschwunden. Ganz parallel arbeitet dieser Code natürlich nicht mehr, es wird sich also nicht der komplette Code parallelisieren lassen.

Noch eine Anmerkung zum Schluss: Vergesst vor lauter Unit-Testing mit Mocks und Fakes die Integrationstests mit zufälligen und korrupten Daten nicht. ;-)

Weitere Informationen zum Thema:

Zurück

29.02.2012
22:03

Sharepoint 2010 Best Practices

Sharepoint gehört immer noch zu meinen Aufgabengebieten. Gute Anleitungen zu finden ist auch nicht einfach, gerade im Bezug was man tun oder lassen sollte.

Das Produktteam hat eine Übersicht mit Best Practices unter dem Link bereitgestellt.

Mal sehen, ob es brauchbar ist...

Weitere Informationen zum Thema:

Zurück

Sharepoint - Der Unterschied der Versionsbezeichnung zwischen Frontend und Datenhaltung

Bei einem grösseren Migrationsprojekt hatte ich eine interessante Aufgabenstellung im Bezug auf die Versionsbezeichnung von Dokumenten. Im Frontend und im Export-XML (Manifest.xml) werden diese in für Benutzer lesbarer Form als 0.1, 0.2 bzw. 1.0 angezeigt. Intern ist die Version dagegen als 1, 2 bzw. 512 abgelegt.

In einem ersten Schritt habe ich eine Metadatenschicht aufgebaut. Die Gründe hierfür sind:

  • Übersicht des zu erwarteten Aufwands bzw. Fortschritts
  • Intensive Tests
  • KPI's und Auswertungen für das Management
  • Analyse der Datenqualität

Die aufbereiteten Metadaten enthalten die sogenannte interne Version der Datenhaltung. Die Versionsbezeichnung der Sharepoint Content Deployment API enthält hingegen die Bezeichnung, wie sie der Benutzer sieht.

Tabellarisch gesehen sieht die Gegenüberstellung so aus:

FrontendversionInterne Version
0.11
0.22
1.0512
1.1513
2.01024

Der Aufbau dieser Versionen folgt einer Regel:

  • Hauptversionen steigen in 512-Schritten
  • Nebenversion fortlaufend

Mit dieser Information war ich in der Lage, das Ganze in einer Hilfsklasse zu packen, die in der Lage ist, die Konvertierung in die interne bzw. Frontendversion vorzunehmen.

Codebeispiel Konverter:


    public class SharepointVersionConverter
    {
        const int MainVersionIdentifier = 512;

        public static string GetSharepointVersionStringFromInternalVersion(int version)
        {
            return String.Format("{0}.{1}", version / MainVersionIdentifier, version % MainVersionIdentifier);
        }

        public static int GetSharepointInternalVersionFromString(string version)
        {
            if (String.IsNullOrEmpty(version))
                throw new ArgumentException("version is null or empty.", "version");

            var split = version.Split('.');

            if (split.Length != 2)
                throw new ArgumentException("Wrong version string");

            return Int32.Parse(split[0]) * MainVersionIdentifier + Int32.Parse(split[1]);
        }
    }

So habe ich jetzt die Möglichkeit, die interne Version aus den Metadaten mit der Frontendversion zu vergleichen, um so die Metadatenschicht aktualisieren zu können.

Weitere Informationen zum Thema:

Zurück

Translate this page

Kategorien