Lade...
 

Clustering und Collections

Clustering und Collections

Clustering von Objekten führt generell zu besserer Performance da Blockaden zwischen Clients nur dann auftreten, wenn zwei Clients das exakt gleiche Objekt beschreiben wollen, da zwei geclusterte Objekte nie auf der gleichen Page liegen.

Performanceprobleme in SET und BAG

Werden geclusterte Objekte jedoch in Collections vom Typ SET oder BAG eingefügt (transient & persistent), dann ist mit deutlichen Performanceeinbußen zu rechnen. ObjectStore implementiert beide Collectiontypen intern als Hashmaps und verwendet eine Hash-Funktion, die den Cluster nicht in die Hashberechnung einschließt. Dadurch kommt es zum ständigen Rehashing der Datenstruktur, da sich die Objekte nicht auf unterschiedliche Buckets verteilen. 

Das Rehashing ist sehr Zeitintensiv, da die Collection vergrößert wird und anschließend der Hashwert aller bisherigen Einträge neu berechnet wird und die Elemente in die neue Collection kopiert werden. Durch die Hashkollisionen landen jedoch alle Elemente in einem Bucket, wodurch das SET/BAG zu einer LIST degradieren. Dadurch haben Find und Contains auch keinen Performancevorteil mehr gegenüber einer normalen LIST-Collection.

Dieses Problem tritt auch beim Einfügen von Objekten in die Root-Entry-Point-Collection in CreatePersObject und RegisterObject auf.

Bei großen Collections (>10.000 Elemente) kann das Rehashing auch mehrere Sekunden dauern. Der ClassiX-Prozess bleibt dann an der aktuellen Insert-Anweisung für mehrere Sekunden stehen und im Task-Manager sieht man eine 100% CPU-Auslastung (für einen Kern).

Lösung

Für REP-Collections

Für geclusterte Masterobjekte sollte in der classix.odb (bzw. classix.ini) die REP-Collection als LIST definiert werden.

Storage(stockSpace, DB(1), "accountS", EP("stockSpaceL0"(LIST)), ...

Für bereits existierende REP-Collection kann der Typ nachträglich per CXS_REP_COLLECTION::CollType() geändert wrden. Hierzu wird die Collection intern neu aufgebaut. Vorher sollten alle Indexe auf diese Collection deaktiviert werden.

Für Collections im Code

Da das Problem auch mit transienten Collections im Code auftreten kann, gibt es ab Dll-Version 223726 einen speziellen Logger, mit dessen Hilfe man das Einfügen von geclusterten Objekten in Sets loggen kann. Der Code sollte dann soweit möglich umgeschrieben werden, dass Listen statt Sets verwendet werden. Hierbei muss beachtet werden, dass vor einem Insert per Contains geprüft werden sollte, ob das Element in der Liste enthalten ist, um das Verhalten eines Sets mit einer Liste zu immitieren.

Performanceprobleme im Code finden

223726

Um Performanceprobleme innerhalb der Anwendung zu finden, die durch das Einfügen von geclusterten Objekten in Sets verursacht werden, wurde ein Logger eingeführt, der in regelmäßigen Abständen beim Einfügen in ein Set prüft, ob dieses größtenteils aus geclusterten Objekten besteht und in dem Fall das Set, das eingefügte Objekt und den aktuellen Befehl loggt.

Um die Prüfung zu aktivieren, muss der Logger cx.omgr.set.clustering mindestens auf die Stufe WARN gestellt werden und die Umgebungsvariable CX_CHECK_CLUSTERED_SET_INSERTS auf einen Wert > 0.

Der Wert von CX_CHECK_CLUSTERED_SET_INSERTS bestimmt dabei das Intervall, wie oft die Prüfung durchgeführt wird. Bei einem Wert von 100 wird die Prüfung immer dann durchgeführt, dann die Anzahl der Elemente eines Sets ein ganzzahliges Vielfaches von 100 erreicht. Ein Wert von 1 prüft bei jedem Einfügen1 und kann die Anwendung erheblich ausbremsen. 

 

Bei kleinen Sets (< 32 Einträge) wird eine Warnung nur dann geloggt, falls alle Objekte des Sets geclustert sind.

Bei größeren Sets werden die Einträge (nach ihren Hashwerten) auf bis zu 64 Buckets verteilt und die Warnung wird geloggt, falls ein Bucket übermäßig mit Objekten befüllt ist. Auf diese Weise werden auch Sets gefunden die nicht ausschließlich aus geclusterten Objekten bestehen, aber zu einem ausreichend großen Teil, sodass die Performance darunter leiden kann.

Solange der Logger nur auf WARN steht, werden im zweiten Fall nur so viele Objekte geprüft, bis klar ist, dass ein Bucket übermäßig befüllt ist. Auf diese Weise kann der Check schneller durchgeführt werden und bremst einen Anwendungsfall weniger aus. Steht der Logger auf DEBUG, dann werden immer alle Elemente der Collection geprüft und die Anzahl der Elemente im vollsten Bucket wird ausgegeben.

Wird der Logger auf TRACE gestellt, dann wird zusätzlich zum aktuellen Befehl auch der gesamte Callstack geloggt, um besser nachvollziehen zu können, wie die Codestelle erreicht wurde.

Achtung: Dieser Logger ist nicht für den Livebetrieb gedacht. Auch wenn die Prüfung nur sehr selten durchgeführt wird, so werden dabei alle Elemente der Collection angefasst (und damit gelocked) und länger als nötig gelocked.

= Geprüft wird bei Insert, Link, +=, und CreatePersObject und nur beim Einfügen in Sets, nicht bei Bags.