Caluga
all about java, coding and hacking

Caluga - The Java Blog

this blog will cover topics all around Java, opensource, Mongodb and alike. Especially Morphium - THE POJO Mapper for mongodb


found results: 70

<< 1 ... 2 ... 3 ... >>


category: Computer --> programming

Objective-C vs Java - Memory Management

2013-05-29 - Tags: java memorymanagement objective-c

no english version available yet

Bei meinen Entwicklungen in Java und Objective-c musste ich mich zwangsl√§ufig auch mit dem Memory Management auseinandersetzen. Das ist in Java meistens ja etwas, das insbesondere seit JDK 1.4 mehr oder minder im Hintergrund stattfindet. Das war eine der gro√üen Neuerungen von Java und viele andere Sprachen haben es √ľbernommen. Aber gerade das aufkommen der neuen mobilen Endger√§te mit doch mehr oder minder beschr√§nkten Resourcen machen das MemoryManagement der eigenen Applikationen wieder etwas spannender.

Warum √ľberhaupt Speichermanagement?

Fr√ľher war doch alles besser - im guten alten Assembler hat man einfach so im Speicher rumgeschrieben, ohne sich einen Dreck um irgendein Betriebssystem oder so zu k√ľmmern. Das ist heute nat√ľrlich nicht einfach so m√∂glich, denn das Betriebssystem benutzt ja auch RAM und man ist ja nicht das einzige Programm, das gerade l√§uft. So gesehen, war die gute alte 8-Bit √Ąra schon besser, weil einfacher! Heute muss man erst mal das System fragen, wenn man Speicher benutzen will, denn sonst wird das Programm schnell einfach beendet (Unter Unix ist das gerne mal ein ‚ÄěSegmentation Fault‚Äú) - schon aus Sicherheitsgr√ľnden - man k√∂nnte ja sonst wichtige Systemfunktionen oder ~daten √ľberschreiben. Speicher allokiert man z.B. in C/C++ mit der Funktion malloc. Ihr gibt man einfach eine Gr√∂√üenangabe mit und als Ergebnis erh√§lt man einen Zeiger auf den f√ľr uns zur Verf√ľgung gestellten Speicherbereich. Das Gegenst√ľck zu mallocist free- damit wird zuvor allokierter Speicher wieder frei gegeben. Alles eigentlich doch recht simpel. Naja‚Ķ da k√∂nnte man doch fragen, was denn daran so kompliziert ist, den Speicher, den man sich vorher ‚Äěgeholt‚Äú hat, dann auch wieder frei zu geben, wenn man ihn nicht mehr braucht. Im Grunde ist das richtig, vor allem f√ľr sehr einfache Programme kann man den Speicher so ‚Äěverwalten‚Äú. Aber heutige Anwendungen haben schon andere Anforderungen an das Speichermanagement. Das f√§ngt schon damit an, dass ich zum Erstellungszeitpunkt gar nicht genau wei√ü, wie viel Speicher ich eigentlich ben√∂tige. Person p=new Person(); p.setName(‚ÄěEin Name‚Äú); p.setAge(new Integer(44)); sieht simpel aus, oder? aber genaugenommen passiert folgendes: es wird Speicher f√ľr ein Objekt ‚ÄěPerson‚Äú allokiert. Die Person ist noch ‚Äěleer‚Äú. Jetzt wird ein Name hinzugef√ľgt. D.h. zum Zeitpunkt der Erstellung der Person wusste ich nicht, wie gro√ü das Objekt eigentlich werden sollte. Ich kann also gar nicht wissen, wie viel Speicher ich bereit stellen muss.

Retain Cycle

Gleiches gilt f√ľr die Freigabe. Wann soll ich den Speicher wieder frei geben? Wann wird die Person pnicht mehr ben√∂tigt? Das wird besonders dann komplex, wenn man auch komplexere Datenstrukturen ben√∂tigt. Ein einfaches Beispiel daf√ľr ist eine Baumstruktur:

 Class Parent extends Node{
      List children;
    }
    Class Child extends Node {
       Parent parentNode;
    }

solch eine Struktur ist normalerweise im Vorhinein nicht klar in der Größe festzulegen. Außerdem ergeben sich sog. Retain Zyklen. Hier hat jeder Knoten einen Zeiger auf seine Vaterknoten und dieser wiederum einen Zeiger auf alle Kindknoten. Das bedeutet, jeder dieser Knoten ist von irgendwoher erreichbar, es gibt ja eine Referenz darauf. Das Hauptprogramm:

root = new Parent();
//add children
// do something with root and the Data
Root = new Parent()

Das Hauptprogramm erzeugt hier einen Parent-Knoten mit alle seinen Kindknoten. Der der so belegte Speicher ist noch vom Hauptprogramm aus erreichbar, da wir ja die Referenz root haben. Wird diese allerdings ersetzt (letzte Zeile), ist der ganze Baum nicht mehr erreichbar und der Speicher könnte theoretisch freigegeben werden. Allerdings ergibt sich ein Retain Zyklus, da ja jeder knoten von einem anderen referenziert wird => Dilemma.

Wie funktioniert das in java - der Garbage Collector

Den Garbage Collector (oder kurz GC) kann man sich vorstellen, wie die Mutter eines Teenagers, die immer hinter ihm herl√§uft und auf- bzw. wegr√§umt. Genauso ist der Garbage Collector. Der GC erkennt die Objekte, die vom Programmthread (bzw von allen Programmthreads) aus erreichbar sind. Alle anderen werden gel√∂scht. Daf√ľr erstellt der GC einen sog. Erreichbarkeitsgraphen aller erstellten Objekte. Und der Speicher aller Objekte, die vom einem laufenden Thread aus nicht mehr erreichbar sind, wird freigegeben. Klingt im ersten Moment recht simpel, wird aber insbesondere im Hinblick auf Retain Cycle etwas undurchsichtig. Kurz gesagt, der GC kann auch komplexere Datenstrukturen einfach aus dem Speicher r√§umen, da er ja die Erreichbarkeit von den Programmthreads aus berechnet und nur die Objekte abr√§umt, die nicht mehr erreichbar sind. => auch Retain Cycle werden abger√§umt. Manchmal wird geschrieben, dass der GC Retain Cycle erkennt, da er ja alle darin befindlichen Objekte im Speicher h√§lt, wenn es auch nur eine Referenz auf das Objekt gibt.

Wie obiges Beispiel: Baumstruktur. Vaterknoten h√§lt Referenzen auf Kindknoten und umgekehrt. Wenn der Vater abger√§umt wird, weil er nicht mehr erreichbar ist, werden auch alle Knoten, die daran h√§ngen mit abger√§umt (es sei denn, das Hauptprogramm h√§lt noch eine referenz auf einen der Kind-Knoten. Dann w√ľrde zumindest dieser Baumteil nicht mit abger√§umt).

Die erste Instanz von Parent() mit all seinen abhängigen Kindknoten sind am Ende des Beispiels zwar noch im RAM, aber vom den Programmthreads aus nicht mehr erreichbar. D.h. Der GC wird sie beim nächsten Lauf abräumen.

Manchmal ist es auch wichtig, diese Cyclen vom Programmseite aus zu verhindern bzw. zu vermeiden. Daf√ľr gibt es in Java z.B. noch spezielle Referenzobjekte WeakReference und PhantomReference. Diese verhalten sich etwas anders beim berechnen des Erreichbarkeitsgraphen und beeinflussen so den GC. Eine Normale Variable ist in dem Zusammenhang immer als strong Referenz zu sehen.

Wie klappt das in Objective-C?

In Objective-C wird seit geraumer Zeit ein etwas anderer Ansatz gew√§hlt, das Reference Counting. Dabei h√§lt jedes Objekt einen Z√§hler, wie viele Zeiger darauf verweisen. An sich eine gute Idee, jedoch muss der Programmierer leider selbst daf√ľr sorgen, dass diese Z√§hler erh√∂ht oder verringert werden. Daf√ľr stellt Objective-C die beiden Methoden retain und release bereit, die von NSObject aus vererbt werden. Mit retain wird der Reference Counter (auch Retain Counter) erh√∂ht, mit release um eins reduziert. Ist der Reference Counter gleich 0, kann das Objekt gel√∂scht und der zugeh√∂rige Speicher freigegeben werden. Das war f√ľrchterlich m√ľhsam und auch fehleranf√§llig. Man musste h√∂llisch aufpassen, wann man retain und wann release aufruft. Wurde letzteres zu fr√ľh aufgerufen, hatte man irgendwo einen nil-Pointer. Das verursacht zwar nicht unbedingt eine Fehlermeldung in Objective-C (zugriff auf nil pointer tut meistens einfach nix), aber tut eben auch nicht das, was man erwartet. Wurde ein release jedoch vergessen, hatte man ein Memoryleak produziert - auch nicht gerade w√ľnschenswert.

ARC

In den neuen Versionen von Obective-C (OSX und IOS6) wurde das ‚ÄěAutomatic Reference Counting‚Äú eingef√ľhrt. Das einem diese ganze l√§stige Arbeit abnehmen sollte. Das funktioniert eigentlich wirklich super - wenn man ein wenig Hintergrundinfos hat. Im Endeffekt wird das Hauptprogramm nur in einem @autoreleasepool { } gestartet, was der Laufzeitumgebung sagt: ‚ÄěHey, alles in diesen Geschweiften klammern bitte z√§hlen‚Äú Das funktioniert weitgehend zur Compiletime, d.h. der Compiler f√ľgt Code ein, der die Referenzcounter erh√∂ht oder verringert! Zur Laufzeit findet nur noch eine Pr√ľfung auf reference_counter==0 statt. Der Ansatz ist also grundlegend anders als der des Garbage collectors. Auch birgt er so seine T√ľcken.

So kann mit dieser Methode ein Retain Cycle nicht erkannt werden bzw. die so belegten Speicherbereiche werden nicht freigegeben => Speicherloch! Um das zumindest von Programmiererseite verhindern zu können, kann man auch in Objective-C schwache weak referenzen verwenden. Nehmen wir die Baumstruktur noch mal als Beispiel, hier in Objective-C. Parent.h:

 @interface Parent: Node
 @property (strong, nonatomic) NSArray *children;
 @end

Child.h:

@interface Child: Node
@property (weak, nonatomic) Parent *parent;
@end

wichtig ist, dass der Child-Knoten nur eine weak-Referenz auf den Vaterknoten hat. Damit kann der ganze Baum abgeräumt werden, wenn der Vaterknoten nicht mehr erreichbar ist. im Programm:

Parent *p=[[Parent alloc]init];
//add children
//do something
p=[[Parent alloc]init];

Eine weak-Referenz erh√∂ht den Reatain-Counter n√§mlich nicht. Wenn also das Hauptprogramm keinen Zeiger mehr auf den urspr√ľnglich erzeugten Objektbaum hat, kann alles abger√§umt werden. Bei meinen Tests mit IOS7 ist mir aufgefallen, dass es sehr wohl sinnvoll sein kann, Objektpointer auf nil zu setzen. Wenn man das tut, wird der Speicher sofort wieder freigegeben. Und in einigen F√§llen ist es das, was man braucht. (zur Erinnerung, das meiste beim ARC wird als Code automagisch eingef√ľgt! Wenn ich irgendwas auf nil setze, wei√ü der Compiler auf jeden Fall, dass ich das Objekt nicht mehr brauche).

Aber das ist leider nicht alles. Das ARC wird von den Grenzen des Code-Blocks von @autoreleasepool bestimmt. Das bedeutet, erst wenn man den Block verlässt, wird sicher alles abgeräumt, was noch mit einem Retain-Counter von 0 im RAM rumidelt. So kann es nötig sein, dass man von Zeit zu Zeit mal einen neuen Autoreleasepool anlegt, damit die darin erzeugten Zwischenergebnisse vom Heap entfernt werden. Es ist schwer zu sagen, wann genau man das machen muss, aber Rekursionen und Schleifen sind heiße Kandidaten ;-)

Leider kommt man in Objective-C nicht immer nur mit den Objekten aus, die einem die Laufzeitumgebung zur Verf√ľgung stellt. Da Objective-C eine Obermenge von C ist, kann man auch auf die gesamte C Funktionalit√§t zur√ľckgreifen und das ist leider manchmal n√∂tig. Einige Systemaufrufe liefern als Ergebnis leider C-Structs und keine Objekte, die mit ARC wieder abger√§umt werden. Manchmal muss man solche Datenstrukturen auch anlegen, was dann nat√ľrlich am ARC ‚Äěvorbei‚Äú passiert. So kann man sich sehr einfach ein Speicherloch z√ľchten, wenn man nicht aufpasst. Im Zweifel am besten immer auf die Objekte der Laufzeitumgebung setzen.

Bewertung beider Mechanismen

GC

  • Nachteil: die Laufzeianalyse ist evtl aufw√§ndig, kann zu kurzzeitigem "einfrieren" f√ľhren insbesondere bei speicherintensiven Anwendungen, l√§uft irgendwann - der Zeitpunkt ist nicht klar
  • Vorteil: simpel f√ľr den Programmierer, Retain Cycle werden erkannt

ARC

  • Vorteil: keine aufw√§ndige Laufzeitanalyse, alles zur Compiletime (Set Nil deswegen manchmal sinnvoll)
  • Nachteil: es wird automatisch Code erzeugt, das kann zu unerwartetem Verhalten f√ľhren, keine Erkennung von cyclen Problem: Memory allocation an dem Arc vorbei!


category: Computer

MongoDB Problem

2013-04-22 - Tags:

sorry, no english version available


category: global

Who is Stephan Bösebeck?

2013-04-19 - Tags: about

originally posted on: https://boesebeck.name

Whose blog is this? Yes, who am I ... this question is amazingly difficult to answer, foolishly. My IT career can be read here on the blog under My IT History.

There is not much more to say. There will be very little private life here, so my professional career will be in a nutshell:

  • After the Abi (and a rather stupid episode at the Bundeswehr - but that is another story) I studied computer science at the University of Passau
  • Degree with diploma
  • Since I had little or no BaF√∂G and my parents were not able to contribute much, I started to work while I was studying
  • Apart from a few student jobs (for example at the computer discounter ESCOM - unfortunately broke meanwhile, but since I learned a lot) I was from 1995 as an IT consultant
  • I have also made some certifications during this time: Sun Certified Trainer, Certified Java Programmer, LPI Certification, Certified C ++ Programmer, Certified Macro4 Trainer, etc. - I could presumably cover entire rooms with the receipt
  • Customers of mine were among others: IBM, Sun, Dresdner Bank, Deutsche Bank, HP, Unilog Integrata, Ixos Training, Goethe Institute ...
  • I have specialized in the technologies Linux / Unix and Java. Therefore also the certifications in the areas
  • I did Java training, especially for SUN and Integrata, from Beginner to Advanced Java Programmer Certification Training
  • It looked similar with Linux training, u.A. For IBM. From beginner training courses to advanced clustering and firewalling topics
  • Of course there were also training courses in other areas around Unix: Perl, TCL / Tk and a few other exotics (Python was still really exotic: smirk :)
  • The knowledge for the Schlulungen I have me in many, unfortunately usually quite small projects appropriated. There was a lot of small and medium-sized companies, for which I developed software. Or worked on larger projects.
  • Since I often can not realize the projects very often, I was quickly pushed into the role of the project manager / manager. As contact for customers, as subcontractor, etc.
  • Since I as a freelancer alone had hardly a chance to lead a large project or realize I have decided to work hard. In this case, I started as a project manager at Softlab
  • There I was able to realize some major projects (well, at least bigger than that before). Among other things for MAN and BMW
  • I wanted to gain more management experience and was then "sued" by Acronis. I started there as "Manager Training & Consulting EMEA". And had to build the training department there. First only for Germany, later for EMEA, then worldwide.
  • The training management and the training management has then more or less moved to USA, which is why I became "Head of Engineering" at Holidayinsider.com. Unfortunately HRS has bought us and now there are Holidayinsider also no more
  • I was then little more than a year Head of Technology at Simplesystem GmbH & Co Kg - but that was unfortunately not quite the correct
  • the next job is already fixed, but I will only post here, when I started there: smile:


category: Computer

PostgreSQL => MongoDb => Morphia => Morphium

2013-04-18 - Tags: java mongodb morphium

no english version available yet

Das war ein langer Weg... Bei holidayinsider.com nutzen wir mittlerweile MongoDB (neben Lucene-SolR als primäre SearchEngine) als primäres BackEnd. Das brachte ein paar Probleme aber auch tolle Lösungen mit sich.

Zun√§chst mal, warum √ľberhaupt eine NoSQL-Db und warum dann gerade Mongo? Die Frage ist nat√ľrlich berechtigt und wurde bei uns auch hei√ü diskutiert. F√ľr das, was wir vor hatten, war PostgreSQL (so toll die Datenbank auch sonst ist) leider nicht ganz das richtige. Die Vorteile (Transaktionen, Berechtigungen und Relationen) w√§ren f√ľr unseren Ansatz nur hinderlich gewesen.

Was haben wir gemacht? Wir loggen nahezu alles, was auf der Plattform passiert und werten diese Daten dann später in einem Offline-Prozess aus (wobei auch eine Menge Daten live in Graphite mit verfolgt werden können). Das war so mit PostgreSQL nicht möglich - wir hatten zwischenzeitlich Collections mit mehr als 100 Millionen Datensätzen deren Verarbeitung mit PostgreSQL mehr als 24h gedauert hätte. Da diese Daten keinen Relationen zuzuordnen sind, brachte eine Relationale Datenbank mit Transaktionen etc keine besonderen Vorteile. Deswegen der Gedanke (auch, weil das Mapping den Java-Objekten recht nahe kommt) auf einen Dokumentenbasierten Storage und in dem Fall Mongo zu setzen. Und: wir sind ein hippes Startup Unternehmen, da muss man auf neue Technologien setzen!

Warum Mongo? Wir wollten auf ein m√∂glichst flottes und einfaches Modell setzen, Mongodb als prim√§re Datenbasis war das erkl√§rte Ziel, PostgreSQL "nur" noch im Hintergrund f√ľr einige nicht lastabh√§ngige Daten. Mongo schien uns als erste Wahl in dem Zusammenhang... da k√∂nnte man in der Retrospektive sicherlich noch mal dr√ľber diskutieren ;-)

holidayinsider.com ist eine 100% pure Java Anwendung ohne viel Overhead. Deswegen war es auch wichtig, eine gute Java Anbindung der NoSQL-Datenbank zu haben. F√ľr Mongo gab es 1. einen guten Java-Treiber, und 2. ein Mapping Framework namens Morphia.

Wir haben morphia einige Zeit lang produktiv in der Anwendung gehabt, aber insbesondere mit der Erweiterbarkeit hatten wir Probleme. Als dann die ersten Bugs in morphia zu Datenverlusten und DownTimes gef√ľhrt hatten, mussten wir uns was einfallen lassen. Insbesondere, weil Morphia seit bestimmt einem Jahr auf der Version 0.99-1 festh√§ngt und nicht mehr weiter entwickelt wird. Wir h√§tten bei dem Projekt ja sogar mitentwickelt, haben das auch angeboten, aber der einzige Entwickler von Morphia wollte nicht, dass irgendjemand mit macht.

Wir wollten das Projekt forken, haben es ausgecheckt und ich hab mir den Code mal genauer angesehen. Sagen wir es so - es war einfacher, es neu zu machen.

Das war die Geburtsstunde von Morphium (http://code.google.com/p/morphium) - der Name war an Morphia angelegt, ich wollte so auch die Vorarbeit von Scott Hernandes w√ľrdigen. Das Projekt wird rege weiter entwickelt und ist mittlerweile in der Version 2.0.6 verf√ľgbar. Entweder von der Projektseite, oder von SonatypeOSS / Maven.

Morphium ist ein ObjectMapper f√ľr Java mit einer Menge Features (die meiner Meinung nach in Morphia gefehlt haben). Hierbei bezeichnen Entities Objekte (POJOs), die in Mongo gespeichert werden sollen.:

  • sehr robustes, flexibles und performantes Mapping von Entities/POJOs zu mongo Objekten: So ist es in Morphium m√∂glich, seinen eigenen Mapper zu definieren. Das ist z.B. sinnvoll, wenn man das Mapping mocken muss f√ľr z.B. JUnit-Tests oder dergleichen.
  • Flexible API: Es ist m√∂glich, fast alle Kernkomponenten selbst zu implementieren bzw. die Default-Implementierungen abzuleiten und um eigene Funktionalit√§ten erweitern. so k√∂nnen ObjectMapper, Cache, Writer und Query durch eigene Implementierungen ersetzt werden (zur Laufzeit).
  • Unterst√ľtzung von Vererbung und Objekthierarchien: Morphium untersucht f√ľr das Mapping den gesamten Ableitungsbaum, wodurch es m√∂glich ist, Entities von anderen abzuleiten und entsprechend zu erweitern.
  • Deklarative definition von Indices: Mongo ist nur dann performant, wenn indices verwendet werden. Diese sollten gut zu den Suchanfragen passen. Ohne Index hit ist eine Query um leicht das 10-100 fache langsamer (query auf eine Collection mit ca. 500.000 Entries ohne Index hit dauerte ca. 45 Sek - mit < 1Sek). In Morphium k√∂nnen die Indices mit Hilfe von Annotations definiert werden.
  • Deklaratives mapping: Feld- und Collectionnamen k√∂nnen deklarativ festgelegt werden. Es ist auch m√∂glich, Aliase anzugeben, wordurch ein Feld evtl. unter mehreren Namen ansprechbar ist. Das ist insbesondere bei Datenmigration hilfreich. Es ist auch m√∂glich, daten von verschiedenen Typen in eine Collection zu speichern - insbesondere bei Vererbung sinnvoll, weshalb das Flag auch @polymorph hei√üt.
  • transparente Referenzen (werden von Morphium aufgel√∂st), incl. lazy loading: In Morphium kann man sehr leicht objekt-Referenzen definieren. Das funktioniert √ľber die Java-Annotation @Reference. Diese wird direkt auf dem entsprechenden Feld angewendet, wodurch nicht das ganze
  • deklaratives Caching: jedes entity bestimmt, ob es per default gecachted werden soll oder nicht. Das bedeutet, dass Ergebnisse von Suchanfragen an diese Entity im Ram gehalten werden. Dabei wird auch bestimmt, wie gro√ü dieser Cache sein soll, ob er bei Schreibzugriffen geleert wird, wie lange Cache-Eintr√§ge g√ľltig sind und was passieren soll, wenn der Cache "voll" ist.
  • asynchrones und gepuffertes Schreiben: gerade die Schreibzugriffe im Cluster (wenn man darauf wartet, dass die Knoten die einzelnen Schreiboperationen best√§tigen) k√∂nnen l√§nger dauern. Das kann zu Problemen mit der Performance f√ľhren. Eine Asynchrone API war da sehr sinnvoll. Gepuffertes Schreiben legt zun√§chst einen Puffer im RAM an und schreibt die Daten dann erst nach einer gewissen Zeit, oder wenn eine Mindestanzahl erreicht ist. Das ist insbesondere deswegen sinnvoll, weil Bulk-Inserts deutlich effizienter funktionieren als einzelne.
  • Validation: Validieren der Daten vor jedem Schreibzugriff mit javax.validation
  • Partial updates: gerade bei gro√üen Dokumenten sinnvoll - sendet √Ąnderungen an die Datenbank, nicht das gesamte Objekt
  • Threadsafety und Cluster awareness bzw. Clusterf√§higkeit: Wir nutzen Mongo im cluster von mehreren Knoten. Es ist wichtig, das Morphium Unterst√ľtzung daf√ľr hat. Einerseits um die Sicherheit zu gew√§hrleisten (warte darauf, dass alle verf√ľgbaren Knoten den Schreibzugriff best√§tigen) andererseits um z.B. richtig zu reagieren (wenn ein knoten ausf√§llt etc.). Das ganze wird innerhalb Morphiums auch √ľberwacht
  • Montoring und profiling: durch ein cleveres Listener-Konzept ist es m√∂glich, jeden Schreib- oder Lesezugriff zu messen und die Daten f√ľr sp√§tere Auswertung abzulegen (z.B. in Graphite).
  • Lifecycle Methods: Es ist m√∂glich, Callbacks innerhalb eines Entity zu definieren f√ľr "preStore" oder "postLoad". Des Weiteren ist es noch m√∂glich, globale Listener f√ľr diese Events zu definieren, d.h. bei jedem Schreibzugriff bzw. Lesezugriff unbabh√§ngig vom Typ informiert werden.
  • automatische Werte: wie z.B. lastChanged oder lastAccessed - Timestamps
  • einfaches Messaging: messaging √ľber MongoDB. Die Messages werden persistiert und in einer Transaktions-√§hnlichen Art und Weise verarbeitet.
  • Unterst√ľtzung f√ľr Geospacial-Suche: selbsterkl√§rend, oder?
  • Unterst√ľtzung von Map-Reduce bzw. dem Aggregation Framework: Das ist die Antwort von Mongo auf Joins und komplexe Anfragen in der "Relationalnen" Welt. Kurz gesagt: damit lassen sich auch schwierigere Auswertungen einigerma√üen performant l√∂sen. Das Konzept dahinter ist aber dennoch etwas schwer zu verstehen.
all diese Features sind zur Zeit in der aktuellen Version von Morphium verf√ľgbar und sind auch schon produktiv in einigen Projekten im Einsatz.

Weitere Doku ist auf der Projektseite oder unter www.caluga.de/morphium zu finden.

Aber zur√ľck zum Thema mongoDB. So einfach, wie ich es hier geschildert habe, war es auch nicht. Wir hatten auch ein paar (u.a. schmerzhafte) learnings.

Wir hatten 10Gen zu uns eingeladen, uns die Vorteile von Mongo mal zu erkl√§ren und uns das in ihren Augen korrekte Setup zu empfehlen. Wir nutzten zu diesem Zeitpunkt mongo schon f√ľr einige kleinere Logs, hatten noch keinen Cluster. Die Empfehlung ging auch von 10gen eindeutig in Richtung Sharding und jeden Shard in einem replicaSet zu Clustern. An sich ja eine gute Idee, allerdings nicht ganz seiteneffektfrei.

Wir sind ziemlich schnell drauf gekommen, dass Sharding auch overhead produziert - nicht zu knapp! So ist die Wahl des shard Key von essentieller Bedeutung beim Einsatz von Sharding. Der Einsatz von timestamps f√ľhrte bei uns leider nicht zum gew√ľnschten Effekt (das f√ľhrt n√§mlich auch dazu, dass quasi minutenweise der Shard gewechselt wird, da nicht timestamp modulo Anzahl Maschinen benutzt wird um den Knoten rauszufinden, sondern immer 64K-Bl√∂cke auf den selben Knoten landen). Au√üerdem war es so, dass requests auf eine geshardete Collection Extreme Last produziert hat. Teilweise wurden dadurch globale locks angelegt -> downtime!!!! Das war untragbar. Mal abgesehen davon, dass das setup extrem kompliziert ist (mongod f√ľr jeden Knoten, der daten h√§lt, mit entsprechender Konfiguration, mongos auf jedem Knoten, der auf den shard zugreifen will und dann noch mongo-config-Server, die auch erreichbar sein m√ľssen. Bei uns f√ľhrte das zu mehr Problemen, als die, die es l√∂sen sollte. So wurde immer mal wieder die Last auf den mongo Knoten extrem hoch nur durch irgendwelche Sharding- oder ReplicaSet-Sync-Operationen. Dummerweise haben die dazu gef√ľhrt, dass alle anderen requests auch lahm wurden, teilweise so langsam, dass sie in Timeouts gelaufen sind.

Zu allem Überfluss lief auch eine ganze zeit lang der Failover nicht - zumindest nicht in Java. Wenn ein mongo Knoten ausgefallen ist, oder zur Wartung runtergefahren wurde, versuchte der Java Treiber dennoch darauf zuzugreifen.... Untragbar! Wir haben der mongo dennoch eine Chance gegeben, eine Menge von den learnings in Morphium einfließen lassen. Im Moment sind wir wieder auf dem Stand: Simplicity wins! Ein Cluster von 4 mongo Knoten, im replicaSet konfiguriert, kein Sharding. Und momentan keine Probleme!

Dabei ist auch wichtig, die Knoten korrekt zu dimensionieren. Wichtig sind: flotte Festplatte und genug RAM. Da ist man schnell etwas verwirrt, da der MongoD selbst nicht wirklich viel RAM nutzt, aber er nutzt "Memory Mapped files", d.h. der RAM-Bedarf des Systems steigt deutlich an. Wichtig ist, dass die Indices ins RAM passen. Sobald das nicht mehr der Fall ist, sollte man √ľber Sharding noch mal nachdenken. Wir sind da aber noch weit von entfernt bei unseren Datenmengen.

En Problem mit den Datenmengen hatten wir dennoch: die eine der prim√§ren Datenbanken ist zwischenzeitlich auf eine Gr√∂√üe von mehr als 250GB im Filesystem angewachsen, obwohl reine Nutzdaten nur in etwa 100GB umfassten. Das liegt daran, wie mongo das Storage organisiert: einzelne Dateien werden angelegt, je nach dem, was gerade ben√∂tigt wird - aber nie gel√∂scht, selbst, wenn man die Collections entfernt. Der einmal belegt Platz wird wieder verwendet. Allerdings scheint es da zumindest bei unserem Setup ein Problem gegeben zuhaben, denn der Platzbedarf im Filesystem lag deutlich √ľber den wirklichen Daten, obwohl nicht so viel gel√∂scht wurde... Die einzige L√∂sung f√ľr dieses Problem ist ein repairDatabase auszuf√ľhren. Das f√ľhrt aber dazu, dass der entsprechende Knoten f√ľr die Dauer der Reparatur nicht erreichbar ist - Super, wenn auch der Failover nicht richtig funktioniert. Au√üerdem darf das Datenverzeichnis nur max. zu 50% belegt sein, will sagen: es muss noch genug Platz auf dem Filesystem sein, damit die Daten ein mal komplett umkopiert werden k√∂nnen.

Das konnten wir erst in letzter Zeit lösen, sind dann auch einen anderen Weg gegangen:

  • alle 2 Wochen wird ein Knoten im Cluster runtergefahren. Wenn es der Primary node war, wartet man zudem noch auf den Failover
  • auf diesem Knoten wird das Datenverzeichnis gel√∂scht
  • der Knoten wird im replicaset wieder hochgefahren
  • er synchronisiert alle Daten neu, minimaler platzbedarf auf der Platte.
Das funktioniert mittlerweile recht problemlos (der Failover funktioniert, und Morphium √ľberwacht ja den Cluster Status). So haben wir den Platzbedarf auf den Platten um ca. 50% senken k√∂nnen.

Also als Tip: Jeder, der plant mongoDB einzusetzen, sollte auch diese Downtime f√ľr Repairdatabase oder das Syncen im Replicaset einplanen! Ich w√ľrde aus diesem Grund Mongo immer in einem ReplicaSet einsetzen!

Ein anderes Problem mit mongo hat zu gr√∂sseren hickups gef√ľhrt, ein mal zu einem kompletten Datenbankcrash. Das muss man etwas ausf√ľhrlicher erkl√§ren:

In mongo ist es m√∂glich, komplett asynchron zu schreiben. D.h. Der schreibzugriff ist beendet, obwohl kein Knoten die Operation schon ausgef√ľhrt hat. Das ist nat√ľrlich etwas sub-optimal, wenn man diese Daten wieder einlesen m√∂chte (z.B. F√ľr Messaging oder Session Replikation). Deswegen ist es in mongo und in allen zugeh√∂rigen Treibern m√∂glich, anzugeben,

  • darauf zu warten, dass der Knoten die Operation bekommen hat
  • ob darauf gewartet wird, dass die Schreiboperation auf der Platte persistiert wurde (fsync)
  • und auf wie viele Knoten man denn da warten will. Da kann man mittlerweile auch so was wie majority angeben.
  • beim lesen vomier mongo kann Management, welche Knoten im replicaSet benutzt werden sollen. Ob prim√§rknoten, sekund√§rknoten oder irgendeiner.
Wir dachten nat√ľrlich, ok, bei wichtigen Daten warte auf alle Knoten. Das hat immer wird zu h√§ngenden schreibprozessen und timeout exceptions in der Anwendung gef√ľhrt. Auch nachkl√§nge Diskussion mit 10Gen konnte mir keiner erkl√§ren, was da los ist. Die hi verwendete Option ist bestimmt, wie viele schreibvorg√§nge auf verschiedenen Knoten erfolgreich sein m√ľssen. Wenn ich das auf die Anzahl der Knoten stelle, h√§ngt der request (auch in der mongo Shell) reproduzierbar. F√ľr alle Werte kleiner als die maximale Anzahl an Knoten funktioniert es.

in der Shell sieht dass dann so aus:

hi1:PRIMARY> db.testreplication.insert({_id:new Date()});printjson(db.runCommand({getLastError:1, w:4,  wtimeout:180000}));
{
        "n" : 0,
        "lastOp" : Timestamp(1347957897000, 17),
        "connectionId" : 431946,
        "wtimeout" : true,
        "waited" : 180000,
        "err" : "timeout",
        "ok" : 1
}

 

Das bedeutet, der Zugriff auf dieses Replicaset mit 4 Knoten (ohne Arbiter, eigentlich sind es 5) dauerte 180 Sekunden(!) in lief dann in einen Timeout. Sobald man irgend einen Wert kleiner 4 angibt, funktioniert es wunderbar. heute, 18.04.2013 ist dieser saublöde Fehler wieder aufgetreten, diesmal war allerdings nicht eingestellt, schreib auf alle Knoten, sondern nimm die Majority (also etwa die Hälfte). Sowas ist wirklich unnötig

Obwohl ich von 10Gen keine Best√§tigung bekommen habe, denke ich, es werden bei diesen Schreibvorg√§ngen, Hidden nodes (solche nodes, die nur die Daten bekommen, aber von denen nicht gelesen wird) dabei nicht mit gez√§hlt werden. Ist nur eine Theorie, funktioniert aber.... Morphium unterst√ľtzt eine deklarative Festlegung sowohl der ReadPreference, also welche Cluster Knoten f√ľr den Lesezugriff erlaubt sind, als auch auf wie viele Knoten gewartet werden sollte bei Schreibzugriffen. Da das mit dem Schreiben auf alle knoten nicht funktioniert hat und die Anwendung immer wieder Dirty Reds bekommen hat (ich musste mir anh√∂ren, dass es ja keine Dirty Reds geben kann, da es das Konzept auf mongo gar nicht gibt - klugsch... Dann eben stale Data!) haben wir momentan alles auf prim√§r umgestellt.. D.h. Wichtige Collections werden nur auf den prim√§ren Knoten geschrieben, und auch nur von dem gelesen... Widerspricht irgendwie dem lastverteilungsgedanken der replicasets.

Aber ok... Momentan haben wir noch keine Probleme mit der Last, sollte das kommen, muss sich 10Gen da was einfallen lassen.... Der Bug ist √ľbrigens auch in der aktuellen V2.4.1 von mongodb vorhanden... (wenn man von einem Bug reden kann, evtl. ist es nur ein Fehler in der Doku)

Fazit: Nichts desto Troz kann ich MongoDb weiterempfehlen, wenn auch nicht uneingeschr√§nkt. Ich habe Mongo jetzt mit Hilfe von Morphium auch in anderen Projekten eingesetzt (z.B. CalugaMed). Wenn man wei√ü, was man tut, kann mongo wirklich einen Gro√üen Vorteil bringen. Da man nicht so fix an Strukturen gebunden ist, wie in einer Relationalen Datenbank, hat man viel mehr M√∂glichkeiten. Als "Datenm√ľlleimer" ungeschlagen! Allerdings kann man auf Grund des fehlenden Zwangs zur Struktur auch mehr falsch machen! Datenmigrationen sind relativ leicht zu machen, die Anfragesprache (JavaScript) ist gut und m√§chtig (auch wenn immernoch ein brauchbares FrontEnd fehlt) und mit Morphium gibt es auch ein recht gutes Mapping-Framework (ich wei√ü, Eigenlob stinkt ;-) ).

Dennoch w√ľrde ich Mongo nicht f√ľr alles und immer benutzen. Man muss sich √ľberlegen, was man ben√∂tigt. Wenn man z.B. Joins ben√∂tigt bzw. die Daten eben nicht so document based ablegen kann, so sind diese Anfragen in Mongo eher schwierig bis unm√∂glich abzubilden. Abhilfe schaft da evtl. auch der "Mut zur Redundanz". D.h. evtl. daten mehrfach ablegen. Komplexe Anfragen funktionieren zwar, k√∂nnen aber zu echten Problemen und Downtimes f√ľhren, wenn man nicht aufpasst (Indizes!!! Indizes!!! Indizes!!!).

Alles in Allem ist Mongo eine echte Alternative zu SQL-Datenbanken und kann in bestimmten Fällen echte Performance und flexibilitätsvorteile bringen. Einen Blick ist es auf jeden Fall wert.

mongo gibt es hier: www.mongodb.org

Morphium Projektseite: code.google.com/p/morphium

Morphium deutsch: www.caluga.de/morphium.html


category: global

new Version of #Morphium (#M...

2012-06-17 - Tags: java morphium mongodb

new Version of Morphium and Morphium GUI: #Morphium (#MongoDb Object Mapper)

MorphiumGUI V0.6.8 - http://t.co/YRIVAUgL


category: Computer

Morphum Object Mapper f√ľr MongoDB (noSQL DB)

2012-04-03 - Tags:

sorry, no english version available


category: Tweet --> global

New Java Object-Mapper for #Mongodb

2012-04-02 - Tags: tweet

sorry, no english version available


category: global --> Tweet

Saying that Java is nice...

2011-09-21 - Tags: tweet java

originally posted on: https://boesebeck.name

‚ÄúSaying that Java is nice because it works on all OSes is like saying that anal sex is nice because it works on all genders.‚ÄĚ @NIxiePixel


category: Computer --> programming --> Java

Multithreaddingprobleme in Java

2011-08-10 - Tags: java-2 programming

no english version available yet

Da mir das jetzt schon so oft passiert ist, und ich immer wieder wieder solche Probleme lösen muss, möchte ich hier mal kurz ein paar Tipps zum Thema Multithreadding in Java und den damit verbundenen Problemen kommen.

Allgemeines

Worum geht es eigentlich und warum ¬†gibt‚Äôs da Probleme? Multithreadding ist eine Programmiertechnik, die es m√∂glich macht, dass in einem Programm mehrere Dinge (mindestens zwei) gleichzeitig passieren bzw. ausgef√ľhrt werden. Im Zuge der Desktopanwendungen nutzt jeder Anwendungen, die mehrere Threads nutzen, denn meistens ist die Oberfl√§che ein Thread, und die Anwendung "selbst" mindestens ein anderer. Das ist ja an sich kein Problem sondern eher w√ľnschenswert... Aber es bringt auch Probleme mit sich... Schauen wir uns das mal am Beispiel von Java etwas genauer an.

Das Problem ist, dass mehrere Threads quasi gleichzeitig auf einen gemeinsamen Speicherbereich bzw. eine gemeinsame Variable zugreifen. Das kann unproblematisch sein, wenn es sich um reinen Lesezugriff handelt. Sobald einer der beteiligten Threads jedoch schreibt, können seltsame Dinge passierten.

Klassenbibliothek und Externa

Java wird gerne in EJB oder allgemeiner J2EE-Umgebungen eingesetzt und dort muss das eigene Programm nicht selten mit vielen gleichzeitigen Zugriffen (concurrent usern) umgehen k√∂nnen. Dummerweise wird das bei der Programmierung oft einfach "vergessen" bzw. nicht beachtet. Der beliebteste Fehler ist vermutlich, dass Teile aus der Klassenbibliothek oder aus Bibliotheken von Drittanbietern verwendet werden, die nicht threadsicher (threadsafe) sind. Gerade mit JDK1.5 wurden einige neue und vermeintlich "bessere" Klassen weingef√ľhrt, wie zum Beispiel die der StringBuilder f√ľr den StringBuffer.

Nahezu der einzige Unterschied zwischen StringBuilder und StringBuffer ist, dass der StringBuilder nicht threadsafe¬†ist, der StringBuffer dagegen schon. Es ist also nicht ratsam, in J2EE-Umgebungen "blind" √ľberall den StringBuilder einzusetzen, weil Java sagt, der ist flotter. Ist er auch, allerdings kann das zu sehr seltsamen Effekten bei der Requestberarbeitung f√ľhren.

Weitere Beispiele, die evtl. noch aufgelistet werden sollten sind ArrayList¬†(nicht threadsafe) und Vector (threadsafe), HashMap (nicht threadsafe) und Hashtable (threadsafe). Klar... man sollte nun aber auch nicht blind die threadsafen Varianten verwenden, sondern sich einfach klar dar√ľber sein, was man tut und was man in welcher situation braucht.

Singletons und deren Probleme

Schlimmer wird es noch, wenn man z.B. in einem Container eingebettet ist wie z.B. einer Servletengine oder eben einem EJB-Application-Server. Dann werden die eigenen Klassen evtl. von mehreren Threads gleichzeitig aufgerufen, selbst wenn man sich selbst da keine Gehangen dar√ľber gemacht hat. Insbesondere, wenn man irgendwelche static-Variablen verwendet (auch schreibend) kann das sehr nach hinten los gehen. So wird eigentlich fast immer, das Singleton Pattern falsch¬†implementiert. Hier eine Implementierung, die man auch oft liest und die in Single-threadded Environments sicherlich ok ist:

[codesyntax lang="java" lines="fancy" lines_start="1"]
public class Singleton {
     private static instance;
     private Singleton() {
          //initialisierung
     }
     public static Singleton get() {
         if (instance==null) { instance=new Singleton(); }
         return instance;
     }
}

[/codesyntax]

Ich höre euch quasi schon sagen: "Was soll denn daran falsch sein, so mach ich es doch auch immer..."... Naja... wo fangen wir an.... (String genommen ist das ja auch eine Factory ;-))

Gehen wir mal davon aus, dass dieser Singleton in einer multithreadded Umgebung verwendet wird. Wenn nun mehrere Thread gleichzeitig auf get() zugreifen, kann es vorkommen, dass der Konstruktor 2x aufgerufen wird, was evtl. ja fatal ist. (ich wei√ü, die meisten nutzen Singleton, nicht weil das Objekt nur einmal instanziiert werden darf, sondern um einfach darauf zugreifen zu k√∂nnen.) Wenn im Konstruktor nun Dinge passieren, die nur ein Mal vorkommen d√ľrfen (Files √∂ffenen, Sockets √∂ffnen etc), dann knallst... es ist nicht gesagt, ob der Singleton dann richtig instanziiert ist.

Gleiches gilt, wenn in dem Constructor eine Exception geworfen wird, dann ist gar nicht klar, was passieren wird und ob/welche Instanz man dann bekommt, wenn man get() aufruft.

eine "Verbesserung" wäre die Instanz gleich zu initialisieren:

 

[codesyntax lang="java" lines="fancy" lines_start="1"]

public class Singleton {
       private static instance=new Singleton();

      private Singleton() {

           //initialisierung

      }

     public static Singleton get() {

           return instance;

     }

}

[/codesyntax]

 

Das ist zwar schon besser, aber nicht wirklich gut. Denn: der Construktor des Singleton wird vom ClassLoader aufgerufen, was zun√§chst mal gut klingt, jedoch gleich wieder relativiert wird, wenn man bedenkt, dass es durchaus mehr als einen ClassLoader in einer Application (insbesondere in J2EE-Containern oder √§hnlichem) geben kann. Dann kann es wiederum vorkommen, dass mehrere Instanzen des Singleton erstellt werden.¬†Siehe dazu auch: Programming Singletons (Sun). Evtl. erinnert man sich dann ja an das Schl√ľsselw√∂rtchen synchronized:

 

[codesyntax lang="java" lines="fancy" lines_start="1"]

public class Singleton {
      private static instance=null;

      private Singleton() {

           //initialisierung

      }

     public synchronized static Singleton get() {

if (instance==null) {

instance=new Singleton();

}

           return instance;

     }

}

[/codesyntax]

 

An sich h√§tte man damit alle Probleme umgangen, jedoch ist es nicht wirklich w√ľnschenswert, Singletons so zu implementieren. Denn das synchronized sorgt daf√ľr, dass immer nur ein Thread auf get()¬†zugreifen kann. Damit hat man sich einen ziemlichen Flaschenhals gebaut, der die Performance stark negativ beeinflussen kann.

Eine wesentlich bessere Lösung ist folgende:

[codesyntax lang="java" lines="fancy" lines_start="1"]

public class Singleton {
      private static instance=null;

      private Singleton() {

          try {
              //initialisierung
          } catch (Throwable e) {} //geeignete Fehlerbehandlung!!!!!!!
      }

      public static Singleton get() {
          if (instance==null) {
              synchronized(Singleton.class) {
                   if (instance==null) {
                        instance=new Singleton();
                   }
              }
          }

          return instance;
     }
}

[/codesyntax]

Damit ist sichergestellt, dass man in den Synchronized-Block nur dann kommt, wenn wirklich eine neue Instanz erstellt werden muss. Damit leidet die Performance nicht so erheblich wie im Beispiel vorher. NUR so hat man eine Singleton-Implementierung, die wirklich threadsafe ist. (und ich bin mir selbst da nicht zu 10000% sicher...;-)

Static im Allgmeinen

Grunds√§tzlich sollte man es sich verkneifen in einer multithreadded Umgebung irgendwo das W√∂rtchen "static" zu verwenden, es sei denn, man ist sich seiner Sache sehr sicher. Die falsche Verwendung von Singletons ist nur ein Problem. H√§ufig werden auch Datenstrukturen statisch abgelegt um "einfacher" darauf zugreifen zu k√∂nnen. Das birgt nat√ľrlich Gefahren. Beispiel:

[codesyntax lang="java" lines="fancy" lines_start="1"]

public class Servlet extends HttpServlet {
private static MyLogger l=new MyLogger();

       .....

       public void get(HttpServletRequest req, HttpServletResponse resp) {
             l.setSessionId(req.getSession(true).getId());
             //do something
             l.setSessionId(null); //request finished!
      }

}

[/codesyntax]

 

Aus falschem Ehrgeiz wurde hier wohl versucht, den Speicherbedarf zu reduzieren. Die Idee war es, die SessionIDs mitzuprotokollieren um bei den Logausgaben die T√§tigkeiten eines Users/Browsers verfolgen zu k√∂nnen. Das ging aber geh√∂rig schief, denn: Ein Servlet wird (normalerweise) nur ein Mal instanziiert aber von mehreren Thread (je nach Konfiguration des Containers) verwendet. Das f√ľhrte dazu, dass man die Requests in keiner Weise verfolgen konnte, da sehr h√§ufig mitten in den Log ausgaben (null) ausgegeben wurde... Weil Thread 1 schon in der Bearbeitung recht weit fortgeschritten ist, und Thread 2 gerade beginnt doGet() aufzurufen.

Problem mit Member-Variablen

Wenn man sich die Ausf√ľhrungen vom letzten Beispiel noch mal ansieht, dann sollte klar sein, dass der selbe Fehler auch auftritt, selbst wenn de Variable nicht static ist! Der Grund: eine Instanz der Klasse "Servlet" wird von verschiedenen Threads verwendet. Somit existiert auch die Variable l (MyLogger) nur ein Mal, selbst wenn sie nicht static ist! Das kann man nur umgehen, indem man lokale Variablen erstellt, (sofern das nicht zu anderen Problemen f√ľhrt - Performance z.B.).

Dummerweise erkennt man nicht wirklich oft, ob eine Klasse  bzw. deren Instanz von "Außen" von mehreren Thread verwendet wird, oder nicht. Da hilft nur, RTFM!

Aber sowas kann man sich auch selbst zaubern, insbesondere wenn man seine Elemente in Datenstrukturen packt, die evtl. von verschiedenen Klassen/Threads verwendet werden wollen. Die Beispiele daf√ľr kann man sich wohl sehr einfach vorstellen, jedoch ist es h√§ufig nicht so einfach zu erkennen, selbst im eigene Code nicht. Es bleibt wichtig, sich immer klar dar√ľber zu sein, was die threads in der eignen Anwendung tun und wer, wie und mit¬†wie vielen Threads auf meine Klassen zugreift.

"Synchronized ist doch die L√∂sung f√ľr alle Multithread-Probleme"

Naja, das wäre ein wenig zu einfach. Mit synchronized kann man sicherlich einige Probleme lösen, allerdings könnte das sehr gut sein, dass man sich damit mehr Probleme einhandelt, als einem lieb ist. Die Performance leidet stark unter der Verwendung von synchronized, da diese Blöcke nur von einem Thread verwendet werden können. Besonders schlimm wird es, wenn man nicht das richtige Objekt zur Synchronisierung verwendet:

[codesyntax lang="java" lines="fancy" lines_start="1"]

public void doSomething() {
      synchronized(Object.class) {
           //do Something weird
       }
}
[/codesyntax]

Hier synchronisiert man gegen das Class Objekt von der Klasse Object, weiter oben in der Hierarchie geht gar nicht... wenn man das an verschiedenen Stellen der Anwendung macht, kann jeweils nur ein Thread auf einen¬†dieser Bl√∂cke zugreifen, selbst wenn sie auf v√∂llig unterschiedliche Daten zugreifen und gerne auch parallel laufen d√ľrften. Es ist besser, auf das "kritische" Objekt zu synchronisieren, dann w√ľrden sogar verschiedene Instanzen dieser Klasse parallel laufen k√∂nnen:

[codesyntax lang="java" lines="fancy" lines_start="1"]

public class MyClass {
    private Vector myData;
    ......
    public void doSomething() {
          synchronized(myData) {
             myData.add(....);
          }
    }
}

[/codesyntax]

hier werden nur Threads synchronisiert, die auf das selbe Objekt zugreifen. Falls es mehrere Instanzen von MyClass gibt, d√ľrfen auch mehrere Threads gleichzeitig die Methoden in den jeweils anderen Instanzen ausf√ľhren. Sowas passiert z.B. gerne bei der Verwendung von EJBs.

Es ist aber auch durchaus möglich, einen Deadlock mit dem Synchronized  statement zu erzeugen.

 

[codesyntax lang="java" lines="fancy" lines_start="1"]

public void a() {

        synchronized(this) {

              b();

              //do something

        }

}

public void b() {

        synchronized (this) {

              //do something!

        }

}

[/codesyntax]

Das sieht doch auf den ersten Blick recht gut aus, jedoch bei genauerem hinsehen, kann das nicht funktionieren: Wenn in Methode a() die Methode b() aufgerufen werden soll, ist man schon innerhalb eines synchronized Blocks. in Methode B befindet sich wiederum  ein Sychronized Block (auf das selbe Object) und der kann erst dann betreten werden, wenn der erste Block verlassen wird, was ja nicht passiert, solange b() nicht fertig wird... Klassischer Deadlock!

Oft ist das nat√ľrlich nicht so plakativ wie hier der Fall, sondern √ľber 3 oder Mehr "Ecken" (Klassen bzw. Objekte)... Das ist zwar nicht unbedingt ein Multithreadding-Only-Thema, denn Deadlocks k√∂nnen auch singlethreadded auftauchen (s.o.), aber bei mehreren Threads sind sie um so schwieriger zu entdecken. Manchmal gibt es auch keine echten Deadlocks sondern nur Verz√∂gerungen, wo ein Thread einfach mal 1-2 Minuten "Pause" macht, biss alle anderen irhre Synchronized-Bl√∂cke verlassen haben.

Static f√ľr Threads: Threadlocal

Wenn man allerdings doch etwas statisches einem Thread zur Verf√ľgung stellen will (bzw. allen Objekten, die in diesem Thread instanziiert werden), dann kann man sich mit einer neuen Klasse behelfen: Threadlocal (auch wenn das nicht wirklich die Idee f√ľr diese Klasse war):

[codesyntax lang="java" lines="fancy" lines_start="1"]

public class Test {
     private static ThreadLocal instance=null;
     private Test() {
        //do something
     }

     //remember to add synchronized blocks for Singleton!!!
     public static Test get() {
          if (instance==null || instance.get()==null) {
               instance=new ThreadLocal();
               instance.set(new Test());
          }
          return instance.get();
     }
}

[/codesyntax]

Bitte nicht vergessen, hier noch die passenden synchronized-Bl√∂cke einzuf√ľgen - der √úbersichtlichkeit wegen habe ich die hier mal weggelassen.

Beans, Servlets und so weiter

Diese Dinger sind ja momentan f√ľrchterlich in und werden √ľberall verwendet, meistens ohne sich wirklich Gedanken dazu zu machen. Mal abgesehen, dass man in den Meisten f√§llen mit EJB sich nur Nachteile einkauft und die Vorteile nicht wirklich nutzt. Aber das ist ein anderes Thema...

Was die meisten nicht bedenken (wieso auch, ist ja so einfach einzusetzen), dass Beans und Servlets einigen Regeln unterliegen, die man - insbesondere im Hinblick auf Multithreadding - besser kennen sollte.

Bei den Servlets verursacht es immer wieder Schwierigkeiten das wenige Instanzen des Servlet von vielen Threads verwendet wird. D.h. in diesem Zusammenhang können auch Member-Variablen zu Problemen werden!

Bei Beans ist es ähnlich, insbesondere Stateless beans... dort ist vielen nicht bekannt, dass, selbst wenn man eine Referenz auf eine Bean hat und selbst wenn es sich um eine Lokal vorhandene Bean handelt, es nicht gesagt ist, dass man bei jedem Methodenaufruf die selbe Bean erhält - oft ist das eben nicht so. Und meistens wird dieses eigentliche Default Verhalten eben erst unter Last gezeigt.

Man kann das nat√ľrlich umgehen, in dem man einfach Stateful Session Beans verwendet, die "kleben" quasi am Client. Da darf man dann auch gerne was zwischen Methodenaufrufen speichern.

Aber auch hier ergeben sich Probleme, denn unter hoher last, werden auch ein Haufen solcher Beans erzeugt. Das kann zu Speicher- oder allgemeiner Resourcenproblemen f√ľhren. Klar, man kann die Anzahl der Beans auf ein Maximum begrenzen... Dann warten die zus√§tzlichen Anfragen eben h√§ngen, bis eine Bean freigegeben wurde.

Und ach ja: bitte vergesst nicht, irgendwann der Bean zu sagen, dass sie nicht mehr ben√∂tigt wird... Eine Destroy-Methode oder so ist f√ľr Stateful Beans wirklich n√∂tig! (Siehe Auch: EJB-Lifecycle).

Abschließende Tipps

Ganz ehrlich, ich hab schon viel zu viel Zeit damit zugebracht, lustige Fehler in Java-Programmen zu suchen, die nur auftraten, wenn Last auf dem System ist. Diese Fehler sind in nahezu 100% der F√§lle auf Probleme der Trennung zwischen den Threads zur√ľckzuf√ľhren.

So etwas ist leider nicht so einfach zu debuggen, denn es tritt ja nur unter Last auf. Manche dieser Fehler kann man mit JMeter finden und somit dann auch beheben, aber da JMeter naturgemäß nur immer wieder das selbe tut, bzw. die gleiche Anfrage an den Server stellt, kann man damit auch nicht 100% alle Fehler dieser Art finden.

Eine nahezu unverzichtbare Hilfe beim l√∂sen dieser Probleme bieten Profiling-Tools wie z.B. JProfiler. Aber die wirklich n√ľtzlichen Tools sind leider kostenpflichtig, auch wenn sich die Anschaffung sicherlich auszahlen wird.


category: global

Procmail - mark messages as read

2008-05-24 - Tags: linux email

originally posted on: https://boesebeck.name

Das funktioniert allerdings nur, wenn man die Emails serverseitig in einem Maildir ablegt. Der Trick ist, procmail dazu zu bewegen, die Emaildatei mit dem Suffix :2,S zu versehen.

hier ein Auszug aus meiner Procmailrc:

:0
* From:.*Add your rule here.*
{
foldername=${DEFAULT}/.DEFAULTFOLDER_HERE

:0c
$foldername/ # stores in .$foldername/new/

:0
* LASTFOLDER ?? /\/[^/]+$
{ tail=$MATCH }
TRAP=‚ÄĚmv $foldername/new/$tail* $foldername/cur/$tail:2,Sa‚ÄĚ

:0
/dev/null
}

Ihr solltet nat√ľrlich die regel oben und den Defaultfolder anpassen. die Variable ${DEFAULT} zeigt bei mir direkt auf die Inbox. Das ganze mit dem zus√§tzlichen ‚Äúa‚ÄĚ an dem Dateinamen liegt an Dovecot (denke ich). die Letzte Regel sorgt daf√ľr, dass es zu keiner Mailverdoppelung kommt.

found results: 70

<< 1 ... 2 ... 3 ... >>