db4o Developer Community

db4o open source object database, native to Java and .NET
Welcome to db4o Developer Community Sign in | Join
in Search
More Search Options

Datenbankintegrität im C/S Modus und Reparatur ?

Last post 08-02-2008, 04:15 PM by Arne411. 11 replies.
Sort Posts: Previous Next
  •  07-11-2008, 11:30 AM 50089

    Datenbankintegrität im C/S Modus und Reparatur ?

    Hallo

    ich habe jetzt bereits mehrfach (leider nicht reproduzierbare) Probleme bei Kunden mit der Integrität der Datenbank. Bei uns ist dieser Fehler zum Glück (oder leider) noch nicht aufgetreten. Insgesamt gab es drei Vorfälle bei der aktuellen 6.4.48.10991 (stable) bzw. der Vorgängerversion 6.4.38.10595 (stable) im client-server-Modus.

    Über die Ursache kann ich derzeit nur mutmaßen. Die Datenbank enthält relativ komplexe Objektverknüpfungen.

    Ich habe die betroffenen Datenbanken analysiert. In der Regel war (durch welche Aktion auch immer) irgendein Objekt in der Weise beschädigt, dass eine Query welche dieses Objekt als result liefert, zu einer Ausnahme auf Seiten des Servers führt. Es sind leider völlig unterschiedliche Objekte (Klassen) betroffen und zwar teilweise auch solche, die normalierweise überhaupt nicht verändert sondern mit der Datenbank ausgeliefert werden (z.B: Gerichte). Allerdings werden bei einer Schemaänderung alle Objekte einmal angefasst und gespeichert. 

    Die Nachricht des Servers für den Fall einer solchen Ausnahme lautet dann z.B.

    Exception in thread "db4o server message dispatcher System" java.lang.ArrayIndexOutOfBoundsException
        at java.lang.System.arraycopy(Native Method)
        at com.db4o.io.CachedIoAdapter$Page.read(Unknown Source)
        at com.db4o.io.CachedIoAdapter.read(Unknown Source)
        at com.db4o.internal.IoAdaptedObjectContainer.readBytes(Unknown Source)
        at com.db4o.internal.IoAdaptedObjectContainer.readBytes(Unknown Source)
        at com.db4o.internal.Buffer.readEncrypt(Unknown Source)
        at com.db4o.internal.LocalObjectContainer.readReaderOrWriterByID(Unknown Source)
        at com.db4o.internal.LocalObjectContainer.readReaderByID(Unknown Source)
        at com.db4o.internal.query.processor.QCandidate.read(Unknown Source)
        at com.db4o.internal.query.processor.QCandidate.readYapClass(Unknown Source)
        at com.db4o.internal.query.processor.QCandidate.classReflector(Unknown Source)
        at com.db4o.internal.query.processor.QConClass.evaluate(Unknown Source)
        at com.db4o.internal.query.processor.QConObject.visit(Unknown Source)
        at com.db4o.foundation.Tree.traverse(Unknown Source)
        at com.db4o.internal.query.processor.QCandidates.filter(Unknown Source)
        at com.db4o.internal.query.processor.QConClass.evaluateSelf(Unknown Source)
        at com.db4o.internal.query.processor.QCandidates.evaluate(Unknown Source)
        at com.db4o.internal.query.processor.QCandidates$4.map(Unknown Source)
        at com.db4o.foundation.MappingIterator.moveNext(Unknown Source)
        at com.db4o.foundation.CompositeIterator4.moveNext(Unknown Source)
        at com.db4o.foundation.IntIterator4Adaptor.moveNext(Unknown Source)
        at com.db4o.internal.StatefulBuffer.writeIDs(Unknown Source)
        at com.db4o.internal.cs.messages.MsgQuery.writeQueryResult(Unknown Source)
        at com.db4o.internal.cs.messages.MQueryExecute.processAtServer(Unknown Source)
        at com.db4o.internal.cs.ServerMessageDispatcherImpl.messageProcessor(Unknown Source)
        at com.db4o.internal.cs.ServerMessageDispatcherImpl.messageLoop(Unknown Source)
        at com.db4o.internal.cs.ServerMessageDispatcherImpl.run(Unknown Source)

    anschließend blockieren sich Client und Server gegenseitig, d.h. die (Client) Anwendung steht, bis der Server beendet wird (was dann allerdings auch nichts mehr bringt). Alternativ kann auch die (Client) Anwendung beendet werden, der Server läuft dann wie gehabt weiter und kann (mit Ausnahme der query gegen defekte Objekte) weiter verwendet werden. Ein Neustart des Server ändert nichts am Verhalten gegenüber den  'defekten Objekten', d.h. die Ausnahme tritt immer wieder an dieser Stelle auf. 

    Auch mit dem ObjectManager kann man zwar (sowohl client/server als auch local Modus) die Datenbank öffnen, bei einer Query gegen die betroffenen Objekte (z.B. Akte oder Gerichte oder was auch immer) steht dann der ObjectManager und die Nachricht auf der (mitgestarteten) Konsole ist wie oben. 

    Leider kann ich die defekten Objekte nicht isolieren, weil die Ausnahme (auf Serverseite) noch vor dem Aufruf von Activate bei Aufruf von iterator.hasNext() (auf der Clientseite) erfolgt

    Query irgendeine_query ...
    ObjectSet result_set = irgendeine_query.execute();
    Iterator<Object> object_it = result_set.iterator();
    while (iterator_it.hasNext() {     <--- hier
       ...
    }

    ich kann also nicht einmal die uuid des betroffenen Objekts herausbekommen sondern nur die uuid des Vorgängerobjets im result_set.

    Die Beschädigung eines Objekts macht somit u.U. die Verwendung der gesamten Datenbank unmöglich, weil eine Query gegen alle Objekte eines bestimmten Typs nicht mehr möglich ist. Mittlerweile gibt es ein zwangsweises tägliches automatisches Backup (das auch nicht ganz trivial abzuschaltenden ist.). Das nutzt aber dann nichts, wenn der Fehler nicht sofort bemerkt wird (es ist ja nur ein Objekt betroffen) und sozusagen laufend mitgesichert wird oder weil die Anwender zunächst versuchen sich selbst zu behelfen.

    Ich hatte zwischenzeitlich mal die (in einem anderen Thread laufenden) EventCallbacks in Verdacht. Vorsorglich schalte diese bei umfangreicheren Transaktionen mittlerweile ab.

    Dass Objekte (ob nun durch einen Fehler unserer Anwendung oder wodurch auch immer) beschädigt werden ist die eine Sache. Aber es sollte eine Möglichkeit geben diese Objekte entweder zu reparieren oder alternativ defekte Objekte zu erkennen und zu entfernen (oder sonst unschädlich zu machen).

    Man kann ja händisch nur über den ObjectManager in die Datenbank und da bringt in diesen Fällen (siehe oben) nichts mehr. Ich weiss ja nicht, in welcher Struktur die Objekte in der Datenbank gesichert sind, aber ein Hilfstool zum Reparieren defekter Einträge wäre sehr hilfreich.
     

    Jetzt meine Fragen:

    Gibt es ein solches Tool oder ist so etwas geplant ?  
     
    Gibt es irgendeinen Hinweis auf die Fehlerursache ?  

     

    Gruß Arne

     

    PS.:

     

    1. Client und Server verwenden die gleiche Konfigurationseinstellung (zugriff nur über singleton). Neben den Indexeinstellungen sind das folgende:

    Db4o.configure().detectSchemaChanges(true);
    Db4o.configure().allowVersionUpdates(true);
    Db4o.configure().automaticShutDown(false); <--- wir verwenden einen eigenen Shutdownhook
    Db4o.configure().clientServer().singleThreadedClient(false);
    Db4o.configure().freespace().useBTreeSystem();
    Db4o.configure().clientServer().batchMessages(false); <-- true führte bei simultanem Anlegen von mehreren
                                                                                      1.000 Objekten manchmal zu Ausnahmen

     

    2. Sowohl beim Server aus auch beim Client liegen immer die gleichen Klassen im Classpath (die Applikation ist in Java entwickelt)

    3.  Solange der Anwender angemeldet ist, bleiben alle Container geöffnet.

    4.  Ich verwende mehrere Container für die gleiche Datenbank. Einige nicht zeitkritische Querys werden in einem anderen Thread gegen einen gesonderten Container ausgeführt. Als Ergebnis werden nur die uuid in einer Liste gesammelt und diese uuid-liste dem Hauptthread übergeben. Im Hauptthread werden dann die Objekte über die uuid aus dem Hauptcontainer nachgeladen.

    5. Auf einigen Rechnern wird zusammen mit dem Bildschirmschoner nach einer Weile die Netzwerkverbindung abgeschaltet, die Verbindung mit der Datenbank geht dann verloren. Kann es u.U. daran liegen ?

    6. Wir verwenden ausschließlich SODA Querys.
     

     

     


    Arne Stocker
  •  07-11-2008, 11:35 AM 50090 in reply to 50089

    Re: Datenbankintegrität im C/S Modus und Reparatur ?

    PS: in einem weiteren Fall sah die Logdatei folgendermaßen aus:

    Exception in thread "db4o server message dispatcher System" java.lang.OutOfMemoryError: Java heap space
        at com.db4o.internal.Buffer.<init>(Unknown Source)
        at com.db4o.internal.LocalObjectContainer.readReaderOrWriterByID(Unknown Source)
        at com.db4o.internal.LocalObjectContainer.readReaderByID(Unknown Source)
        at com.db4o.internal.query.processor.QCandidate.read(Unknown Source)
        at com.db4o.internal.query.processor.QCandidate.readYapClass(Unknown Source)
        at com.db4o.internal.query.processor.QCandidate.classReflector(Unknown Source)
        at com.db4o.internal.query.processor.QConClass.evaluate(Unknown Source)
        at com.db4o.internal.query.processor.QConObject.visit(Unknown Source)
        at com.db4o.foundation.Tree.traverse(Unknown Source)
        at com.db4o.foundation.Tree.traverse(Unknown Source)
        at com.db4o.foundation.Tree.traverse(Unknown Source)
        at com.db4o.foundation.Tree.traverse(Unknown Source)
        at com.db4o.foundation.Tree.traverse(Unknown Source)
        at com.db4o.foundation.Tree.traverse(Unknown Source)
        at com.db4o.foundation.Tree.traverse(Unknown Source)
        at com.db4o.foundation.Tree.traverse(Unknown Source)
        at com.db4o.foundation.Tree.traverse(Unknown Source)
        at com.db4o.foundation.Tree.traverse(Unknown Source)
        at com.db4o.internal.query.processor.QCandidates.filter(Unknown Source)
        at com.db4o.internal.query.processor.QConClass.evaluateSelf(Unknown Source)
        at com.db4o.internal.query.processor.QCandidates.evaluate(Unknown Source)
        at com.db4o.internal.query.processor.QCandidates.execute(Unknown Source)
        at com.db4o.internal.query.processor.QQueryBase.executeLocal(Unknown Source)
        at com.db4o.internal.query.result.IdListQueryResult.loadFromQuery(Unknown Source)
        at com.db4o.internal.query.result.HybridQueryResult.loadFromQuery(Unknown Source)
        at com.db4o.internal.cs.messages.MQueryExecute.executeFully(Unknown Source)
        at com.db4o.internal.cs.messages.MQueryExecute.execute(Unknown Source)
        at com.db4o.internal.cs.messages.MQueryExecute.processAtServer(Unknown Source)
        at com.db4o.internal.cs.ServerMessageDispatcherImpl.messageProcessor(Unknown Source)
        at com.db4o.internal.cs.ServerMessageDispatcherImpl.messageLoop(Unknown Source)
        at com.db4o.internal.cs.ServerMessageDispatcherImpl.run(Unknown Source)


    Arne Stocker
  •  07-18-2008, 04:05 PM 50215 in reply to 50089

    Re: Datenbankintegrität im C/S Modus und Reparatur ? [gelöst]

    ... der Thread hier scheint ja nicht gerade viele Leser vom Hocker zu hauen ... :-)

     Ich habe jetzt den 'Fehler' gefunden

     
    1. Ich konnte unter Last im c/s Modus regelmäßig die Ausnahme

    Exception in thread "db4o server message dispatcher System" java.lang.ArrayIndexOutOfBoundsException
    oder auch
    Exception in thread "db4o server message dispatcher System" java.lang.NegativeArrySizeException

    auf dem Server produzieren. Der Grund war mit nicht ganz klar. Die Ausnahme wurde immer in der Methode

    StatefullBuffer.java 

    public StatefulBuffer(Transaction a_trans, int a_initialBufferSize) {
        i_trans = a_trans;
        i_length = a_initialBufferSize;
        _buffer = new byte[i_length];
    }

    geworfen.

     

    2. Nachdem ich aus lauter Verzweiflung (ich wollte doch eigentlich nur Objekte speichern :-) ) den Source selbst kompiliert  hatte (das Projekt db4ojdk1.2 habe ich dabei nicht zu hinzugefügt) gab es eine Menge Fehlermeldungen wegen der Iteration über das ObjectSet. Mein Source lautete

    ObjectSet result_set = query.execute();
    if (result_set.isEmpty() == false){
        Object object = result_set.get(0);
    }

             bzw.

    ObjectSet result_set = query.execute();
    iterator<Object> object_it = result_set.iterator();
    while(object_it.hasNext()) {
        Object object = object_it.next();
    }

    Ab der Version 6.0 (oder so) kann der Zugriff auf ein Element mit index nur noch über den ExtObjectSet erfolgen,
    also z.B

    ObjectSet result_set = query.execute();
    if (result_set.size() > 0){
        Object object = result_set.ext()get(0);
    }.

    und die Iteration über

    ObjectSet result_set = query.execute();
    while(result_set .hasNext()) {
        Object object = result_set .next();
    }


    Damit sind die serverseitigen Exceptions die ich unter Last hatte verschwunden.


    Das Interface im Sourcecode 1.5 implementiert List<Item> während das Interface im Sourcecode 1.2 nur List implementiert.

    Wenn ich nämlich anstelle des Quellcodes wieder die *.jar Dateien (i.E: ant.jar, bloat-1.0.jar, db4o-6.4..48.10991-java5.jar, db4o-6.4..48.10991-db4ounit.jar und db4o-6.4..48.10991-nqopt.jar) importiere, gibt es auch wieder die Methoden .get() und .iterator(). Enhält die db4o-6.4..48.10991-java5.jar auch noch die Implementierung von 1.2 ???

    Ich muss natürlich zu meiner Schande gestehen, dass in der Dokumentation immer nach der zweiten Variante implementiert wurde.

     
    Gruß Arne

     


    Arne Stocker
  •  07-20-2008, 06:02 PM 50239 in reply to 50089

    Testcaste

    Hallo

    ich hatte mich etwas zu früh gefreut und konnte nach einigen Änderungen am Code immer noch eine Ausnahme in meinem (testweise modifizierten) Programm erzeugen. Möglicherweise hat sich nur das Laufzeitverhalten geändert.

    Mittlerweile glaube ich, dass die Ursache mit den Querymode SNAPSHOT bzw. LAZY zusammenhängt. Bei Verwendung von IMMEDIATE passiert nichts.

    Zwei typische Ausnahmen des serverseitigen DispatcherThreads sind

        Exception in thread "db4o server message dispatcher user_1" java.lang.ArrayIndexOutOfBoundsException
        at java.lang.System.arraycopy(Native Method)
        at com.db4o.io.CachedIoAdapter$Page.read(Unknown Source)
        at com.db4o.io.CachedIoAdapter.read(Unknown Source)
        ...
        
         Exception in thread "db4o server message dispatcher user_1" com.db4o.IncompatibleFileFormatException
        at com.db4o.internal.IoAdaptedObjectContainer.checkReadCount(Unknown Source)
        at com.db4o.internal.IoAdaptedObjectContainer.readBytes(Unknown Source)
        at com.db4o.internal.IoAdaptedObjectContainer.readBytes(Unknown Source)
        at com.db4o.internal.Buffer.readEncrypt(Unknown Source)

    Ich habe mal ein Testcase gebaut, bei dem nach kurzer Zeit die Ausnahme geworfen wird. In der ersten Variante hatte ich in einem weiteren Thread permanent Cars hinzugefügt. In der zweiten Variante (=aktuelles Testcase) werden die Cars nur am Anfang erstellt und während der laufenden Query (über die Cars) nicht mehr modifiziert oder hinzugefügt. Gleichwohl kommt es zur Ausnahme. Wie gesagt nur im QueryMode SNAPSHOT (und LAZY), nicht im QueryMode IMMEDIATE.


    Testcase download:  http://www.advolux.de/diverses/download/SnapshotTestCase.zip

    Das Testcase macht folgendes :

    1.  Einen Serverthread starten
    2.  1.000 Objekte vom Typ Car einfügen und committen
    3.  Zwei parallele Threads starten die sich jeweils als Client bei dem Server anmelden und
         im Sekundentakt (zeitversetzt um 500 msek)  jeweils 1 Piloten erzeugen.
    4.  Jedem Pilot wird eine Liste von Cars eines bestimmten Herstellers zugewiesen,
         die er durch Query aus dem ClientContainer seines Threads ausliest. Alle 5 Piloten wird ein Commit ausgelöst.
         
     nach kurzer Zeit wird auf dem Server eine Exception ausgelöst.
     
     Gruß Arne
     
    Arne Stocker
  •  07-21-2008, 11:13 AM 50246 in reply to 50239

    Re: Testcaste

    Responding to Arne411:

    > http://www.advolux.de/diverses/download/SnapshotTestCase.zip

    Gerade mal ausprobiert - reproduzierbar unter 6.4, keine Exception gegen
    den aktuellen svn trunk. Mir kommt's auf den ersten Blick nicht bekannt
    vor, ich forsche mal nach, im Zuge welchen Jira-Tasks das gefixt wurde,
    und warum es da offensichtlich (noch?) keinen Backport nach 6.4 gibt.

    Viele Gruesse,
    Patrick
    Patrick Roemer&nbsp;»&nbsp;db4objects
  •  07-22-2008, 01:54 PM 50264 in reply to 50246

    Re: Testcaste

    Vielen Dank

    Ich habe das Testcase gerade mal gegen die aktuelle 7.4.49.11005 (Development Version) laufen lassen und da trat der Fehler immer noch auf (Vista, DualCore 2.2 Ghz), das gleiche bei einem Run gegen die 7.4.111168 (Continuous build)

     
    Gibt es noch eine aktuellere Version ?

     
    Gruß Arne
     

     


    Arne Stocker
  •  07-22-2008, 10:12 PM 50272 in reply to 50264

    Re: Testcaste

    Responding to Arne411:

    > Ich habe das Testcase gerade mal gegen die aktuelle 7.4.49.11005
    > (Development Version) laufen lassen und da trat der Fehler immer noch
    > auf (Vista, DualCore 2.2 Ghz), das gleiche bei einem Run gegen die
    > 7.4.111168 (Continuous build)
    >
    > Gibt es noch eine aktuellere Version ?

    Hmpf, nein, sorry, inzwischen kann ich es hier auch mit trunk
    reproduzieren. :/ Ich arbeite gerade dran, es fuer einen Testfall weiter
    einzudampfen. Vielen Dank fuer den Beispielcode! Ich melde mich, wenn
    ich es (hoffentlich) weiter lokalisiert habe.

    Viele Gruesse,
    Patrick
    Patrick Roemer&nbsp;»&nbsp;db4objects
  •  07-23-2008, 12:00 PM 50283 in reply to 50272

    Re: Testcaste

    ich war zwischenzeitlich schon vom Glauben abgefallen, weil es mal funktionierte und mal wieder nicht und offensichtlich von weiteren Laufzeiteinstellungen abhängt. Insbesondere hatte ich auch die Startzeiten zwischen den Threads und die Pausezeite hin und her verändert und an der Configuration geschraubt. Ich hatte auch mal alle 100 Piloten eine Pause von 30 sek, eingelegt, auch das half nicht viel weiter, die Gesamtzahl der erstellten Piloten bis zum ersten Crash war immer ziemlich gleich.
     

    Ich habe mittlerweile den Testcase so abwandeln können, dass er keine Ausnahmen mehr wirft und zwar

    1. Alle Querys mit query.descends("_member").orderAscending()

    sortieren das erhöht die Zeit bis zum Ausfall erheblich (so auf einige hundert bis tausend)

    und dann vor jeder Query

    2. container.ext().purge()

    aufrufen. Mit der letzten Änderungen läuft das Testcase stabil und zwar auch dann, wenn die Personen alle 1msek erstellt werden und in einem weiteren QueryThread im SNAPSHOT-Modus abwechselnd für alle Namen die Anzahl der Piloten ermittelt wird. Da es nur die beiden Änderungen betrifft habe ich jetzt nicht das gesamte Testcase geändert.

    Gruß Arne
     






    Arne Stocker
  •  07-23-2008, 12:07 PM 50284 in reply to 50283

    Re: Testcaste

    bzw. nach jedem commit() !

    Gruß Arne 


    Arne Stocker
  •  07-23-2008, 03:43 PM 50287 in reply to 50264

    Re: Testcaste

    Responding to Arne411:

    > Ich habe das Testcase gerade mal gegen die aktuelle 7.4.49.11005
    > (Development Version) laufen lassen und da trat der Fehler immer noch
    > auf (Vista, DualCore 2.2 Ghz), das gleiche bei einem Run gegen die
    > 7.4.111168 (Continuous build)

    Ich habe es hier eingestellt:

    http://tracker.db4o.com/browse/COR-1346

    Viele Gruesse,
    Patrick
    Patrick Roemer&nbsp;»&nbsp;db4objects
  •  08-02-2008, 01:28 PM 50482 in reply to 50287

    Re: Testcaste

    Responding to myself:

    > http://tracker.db4o.com/browse/COR-1346

    Wir haben neue Builds online gestellt, die dieses Problem hoffentlich
    fixen. Dein Beispielcode laeuft bei mir jetzt ueberall gruen, auch wenn
    man die #sleep() rausnimmt und mehr Clients drauf loslaesst. Kannst Du
    bitte ausprobieren, ob Du das verifizieren kannst?

    Viele Gruesse,
    Patrick
    Patrick Roemer&nbsp;»&nbsp;db4objects
  •  08-02-2008, 04:15 PM 50485 in reply to 50482

    Re: Testcaste

    Hallo Patrick
     

    Ich habe jetzt den Aufbau nochmal verschärft, permanent neue Cars hinzugefügt, ohne Sleep gearbeitet und in einem zusätzlichen Thread gegen die laufend neu angelegten Personen nach Namen gesucht -> das Testcase läuft hier seit über einer Stunde ohne Probleme - perfekt !!!

    Vielen Dank für die Mühe und ein schönes Wochenende

    Gruß Arne 


    Arne Stocker
View as RSS news feed in XML