PostgreSQL Background Writer Statistics

The pg_stat_bgwriter view provides some interesting information about the background writer. This view provides only one row. If you want to analyze these data, these results has to be stored somewhere with a timestamp. The following shall show that it allows  an insight into the changes performed in shared buffer cache and also hints for tuning several server parameters.

If a backend process (a client) requests data it is either found in a block in shared buffer cache or the block has to be allocated (read from disk). The latter is counted in buffers_alloc column. The usage count of this block is increased. The backend process modifies data in this requested page, so this page becomes dirty. With the commit the transaction is written to WAL file. At some moment the background writer will synchronize the dirty page with the disk and mark this page as clean.

This latter asynchronous process can be triggered by the following three events:

  • regular checkpoint
  • backend request
  • background clean process.

The regular checkpoint is triggered either when the number of available checkpoint_segments are exhausted or when the checkpoint_timeout is reached. The first are counted in checkpoints_req column, the latter in checkpoints_timed column. How many blocks during checkpoint are written is counted in buffers_checkpoint column.

It may happen that a dirty page is requested by a backend process. In this case the page is synched to disk before the page is returned to the client. The number of those pages are incremented in buffers_backend column.

It may happen that a new page has to be allocated and for this an allocated page has to be cleared. PostgreSQL may clear pages with a low usage count in advance. The process scans for dirty pages with a low usage count so that they could be cleared if necessay. Buffers written by this process increment the buffers_clean column.

Writing dirty buffers on backend request may cause a delay of the response to the client request. Using the clean writer process may mitigate this effect. Writing least recently used pages (which have a low usage count) is enabled if bgwriter_lru_maxpages has a value bigger than 0. The number of pages to be written is calculated via the number of recently allocated buffers (you can see it in buffers_alloc) multiplied by bgwriter_lru_multiplier (which defaults to 2).

So a higher allocation rate may lead to a higher amount of buffers clean operations. These cause a higher I/O between checkpoints caused by background writer. On the other hand the higher allocation rate results in a lower cache hit ratio. This may a have serious impact on client applications e.g. it could cause canceling of querys with a very short query timeout. The shared buffer cache should be increased.

On a healthy system there should >90% of all checkpoints triggered by time. Based on a history of entries of pg_stat_bgwriter view this can easily verified by comparing the calculated checkpoint interval with current setting. Furthermore buffers written on checkpoint should be higher than 80% of the this total number of blocks written. The total number of blocks written is the sum of

  • buffer written on checkpoint
  • buffers written on clean
  • buffer written on backend request

So the ratio of cleaned buffers should be small while the ratio of buffers written by a regular checkpoint should be higher.

The statistics view pg_stat_bgwriter provides you the current state of statistics.
You can reset the statistic:

select pg_stat_reset_shared('bgwriter‘);

If you want to make analysis on background writer statistics you either have to reset the statistics or collect data into into a log table. From these values important indicators of database state can be calculated and can give hints which setting of the PostgreSQL cluster has to be adjusted.

Interesting further reading:

 

Posted in Allgemein | Kommentare deaktiviert

Wörter zählen

Am Donnerstag war Soundcloud wieder einmal so freundlich, seine Räume für das Treffen des FunClubs zur Verfügung zu stellen. Zu diesem Treffen sollten die Teilnehmer eine Aufgabe lösen: Wörter in einem Text zählen und die 10 häufigsten Wörter anzeigen.

Die Präsentationen waren sehr interessant. Es waren Lösungen in

  • Bash/Awk
  • Erlang
  • Haskell
  • Scala
  • Lua
  • Clojure
  • Common Lisp
  • R
  • C

zu sehen. Sehr interessant war, dass es doch trotz des sehr einfachen Problems sehr viele unterschiedliche Wege zu Lösung gibt.

Auch die Vergleich der Performance waren sehr interessant. In dieser Hinsicht war der Beitrag in C das Highlight des Abends (und zurecht auch am Schluss). Während bei den Hochsprachen Lua bei der Verarbeitung des 500MB Korpus mit ca. 20 Sekunden die Führung einnahm, so wurde diese Zeit durch die Implementation in C um Größen (2,8 Sekunden) geschlagen. Besonders interessant dabei war, wie viele Annahmen über das Problembereich gemacht wurden und welche Vereinfachungen der Implementation sich daraus ergeben – z.B. wenn man von einem belletristischen Text in englischer Sprache ausgeht kann man einen besonders einfachen und schnellen Algorithmus zur Berechnung des Hashwertes nehmen, da vermutlich kaum mehr als 100.000 Wörter vorkommen werden. Dafür war es in Zeilen gerechnet auch das längste Programm.

Hinsichtlich der optischen Reize war die Präsentation mit Keynote die Ansprechendste.

Leider ist wegen der großen Beteiligung und der vielen Beiträge die Diskussion des Problems etwas zu kurz gekommen. Es war aber trotzdem ein anregender Abend.

Posted in Programmierung | Kommentare deaktiviert

Umbenennen von Postgres Typen

In PL/pgSQL, eine der Programmiersprachen für die serverseitige Programmierung in PostgreSQL, werden das Interface der Prozedur und Body unterschiedlich behandelt. Der Body wird beim ersten Aufruf in einer Session kompiliert, für SQL werden Prepared Statements und ggfs. auch Ausführungspläne erstellt und diese im Cache bis zum Ende der Session gespeichert. Innerhalb eines Bedingungsbaums werden so nur die Statements vorbereitet, die auch benötigt werden (siehe Dokumentation).

Typen können in PostgreSQL erstellt und als Parameter oder Variablen referenziert werden. Typen in Parameterdeklarationen werden beim Erstellen der Funktion in die OID aufgelöst, Typen in Variablen Deklarationen zu Beginn der Session, wenn der Body compiliert wird.

Wenn ein Typ mit ALTER TYPE geändert wird, dann bleibt seine OID gleich. Wurde diese OID in einer Funktion als Interface definiert, so wird damit die gespeicherte Interface Definition geändert. Die im Cache befindlichen Funktionen kennen diese Änderungen nicht, weil das vorbereitete Statement durch ein ALTER TYPE nicht invalidiert wird. Sind im Cache nun aber Aufrufe des Interfaces enthalten, so verweisen diese auf den alten Typ. Wird eine neue Session erstellt, so wird der Body neu kompiliert und erst danach wird der neue – geänderte – Typ referenziert.

Wenn man nun durch ALTER TYPE den Namen des Typs ändert, dann funktionieren die aktuell im Cache enthaltenen vorbereiteten Statements weiterhin.Im Quellcode der Funktion sind jedoch noch den alten Namen des Typs enthalten. Hier könnte man auf die Idee verfallen, einen neuen Typen anzulegen, welcher den Namen des alten Typs vor der Änderungen erhält.

Wenn man jedoch daneben einen neuen Typ anlegt, der den gleichen Namen hat wie der geänderte Typ, so werden die neu kompilierten Prozeduren beim Aufruf (zur Ausführungszeit) immer noch fehlschlagen, weil es keine Funktion mit der neuen OID existiert! Die Funktion wird gesucht mit dem passenden Typ im Namen. Der Typname wird aufgelöst nach der OID und die passende Funktion mit gesucht mit der neuen OID gesucht. Eine solche Funktion gibt es aber nicht, da das Interface nicht neu compiliert wurde.

In diesem Fall, wenn Typen im Interface von Funktion geändert werden, müssen diese Funktionen auch neu erstellt werden. Das führt dazu, dass die bestehenden vorbereiteten Statements im Cache invalide werden und die Funktionen bei einem neuen Aufruf erneut kompiliert werden.

create type mutableType as (myval int);
--
create or replace function useMutableType(mut mutableType)
returns int stable language plpgsql as $body$
begin
return mut.myval;
end;
$body$

create or replace function invokeMutableTypeFunction()
returns int language plpgsql as $body$
declare
 val mutableType;
begin
 val.myval := 1;
 return useMutableType(val);
end;
$body$

local_db=# select * from invokeMutableTypeFunction() ;
invokemutabletypefunction
---------------------------
1
(1 row)

In einer anderen Session kann man den Type ändern.

alter type mutableType rename to mutatedType;

In der ersten Session kann das Statement weiterhin ausgeführt werden, führt man das Select jedoch in der zweiten Session aus, führt das zu folgenden Fehler

local_db=# \c local_db
You are now connected to database "local_db".
local_db=# select * from invokeMutableTypeFunction() ;
ERROR:  type "mutabletype" does not exist

 

Posted in Allgemein, Programmierung | Tagged | Kommentare deaktiviert

Objekte im Jasperserver aktualisieren

Sollen die Objekte im Jasperserver Repository aktualisiert werden (von einer Testumgebung auf eine Produktionsumgebung), muß man beachten, daß man die Datenquelle nicht überschreibt. Die Datenquelle wird referenziert und wird mit in den Exportordner oder das Export-Zipfile geschrieben. Löschen der Datenquelle aus dem Exportfolder vor dem Import ist auch keine Lösung, denn ohne Datenquelle können die Objekte jedoch nicht aktualisiert werden. Dann erscheint die Exception:

08:00:44,530 ERROR FileSystemInput:73 - java.io.FileNotFoundException:

(Interessanterweise kann eine Ressource ohne Datasource im Dateisystem importiert (hinzugefügt) werden, wenn die Ressource nicht vorhanden ist – ohne --update Option – und wenn die Datasource im Repository vorhanden ist. )

Das im folgenden beschriebene Verfahren kann auch verwendet werden für das Kopieren der Testumgebung in eine Produktionsumgebung.

Export aus dem Quellsystem

 > cd <installdir/jasperserver-3.5.0>\scripts
 > js-export.bat --output-dir x:\path\jasper --uris /FolderName/AnalysisView

Die Dateien der des Repositories einschließlich der index.xml Datei muß auf das Zielsystem transferiert werden. Diese enthält auch die Datasource des Quellsystems. Im Repository Verzeichnis muß diese Datasource ergänzt bzw. ausgetauscht werden. Im scripts Ordner des Zielsystems dazu die Datasource exportieren:

 > js-export.bat --output-dir c:\Temp\jasperexport --uris /datasources/targetdatasources

Wenn man die entstandene Datei ins entsprechende Verzeichnis des Repository kopiert hat, kann man dieses dann importieren, ggfs auch Objekte aktualisieren:

 > js-import.bat --update --input-dir c:\Temp\jasper

Danach muß man für Änderungen am Mondrian den Jasperserver über die Managementoberfläche des Tomcat neu starten.

Posted in Business Intelligence, Programmierung | Tagged | Kommentare deaktiviert

Funclub about fexprs

Gestern war ich auf einem Meetup des FunclubTim Felgentreff sprach über Fexprs und erläuterte das Konzept und auch die Nachteile. Interessant war, was mit einem solch einfachen Konzept möglich wird. Bei Fexprs werden die Operanden nicht evaluiert, bevor sie an die Funktion übergeben werden. Die Funktion erhält darüber hinaus die Umgebung des Aufrufers und kann darüber entscheiden, ob die Operanden evaluiert werden.

Interessant war die Demonstration, mit wie wenig Kernfunktionen eine Implementation auskommt, die Fexprs evaluiert. Dies ist die Stärke des Konzepts. Leider – und das ist der Nachteil – ist die Ausführung unglaublich langsam. Die statische Analyse kann nicht feststellen, ob ein Operator eine normale Funktion oder eine Fexprs darstellt.

Tim verschärfte das Problem in der Diskussion. Dadurch, dass die Umgebung (die Menge der Symbole und der gebundenen Werte) der Fexpr zugänglich ist, kann diese auch geändert werden kann. Alle späteren Implementationen von Lisp (nach 1980) haben meist auf die Fexprs zugunsten der Macros verzichtet.

Posted in Programmierung | Tagged | Kommentare deaktiviert

Abhängigkeiten in Programmen

Einige wenige Tage Beschäftigung mit Clojure bringt die gewohnten Bahnen ganz schön durcheinander. Einerseits bin ich noch immer wieder erstaunt, wie kurz funktionale Programme werden. Zum anderen stelle ich fest, dass ich beim Review von Code in imperativen Sprachen die Ausdruckskraft funktionaler Sprachen vermisse.

Gegeben sei Prozess, welcher einen langen Liste von Werten aufbaut. Da die Regeln umfangreich sind, wird die Verarbeitung aufgeteilt in verschiedene Methoden/Prozeduren, damit die Prozeduren übersichtlich bleiben.

process1( liste )
process2( liste )

meinRecord ist ein Objekt, was von beiden Prozeduren verändert wird. Es ist aber nicht ersichtlich, ob prozess2 vom Ergebnis von prozess1 abhängig ist. Es kann sein, dass in prozess2 gelesen werden, die in prozess1 gesetzt wurden. Es kann aber auch nicht sein. Dann würde

process2( liste)
process1( liste)

zum gleichen Ergebnis führen. Das macht es einem Entwickler schwer, diesen Ablauf zu ändern oder zu refaktorieren. Der Entwickler, muß manuell die ganzen Abhängigkeiten auflösen, ob process2 von einer Zustandsänderung in process1 abhängt.
Besser wäre es, wenn der Quellcode die Abhängigkeit explizit ausdrückt. Das ist eigentlich das prinzipielle Dilemma von imperativen Sprachen. Besser wäre die Form

B := process1 ( A )
C := process2 ( B )

Hier wird die Liste A nicht (in-place) verändert, sondern ist nicht veränderlich. Damit ist ersichtlich, dass process2 vom Ergebnis von process1 abhängt. Besteht keine Abhängigkeit, dann sollte auch dies erkennbar sein.

A := process1 ( X )
B := process2 ( Y )
C := combine( A, B )

Damit ist auch ersichtlich, dass die Ausführungsreihenfolge von process1 und process2 nicht von Bedeutung ist. Wäre es sicher, dass sie keine Nebenwirkungen haben – wie in rein funktionalen Sprachen – könnten diese Prozesse auch parallel ausgeführt werden. Leider ist nie alles Gute beisammen. Wenn eine Funktion immer das gleiche Ergebnis liefert, wenn man sie mit den gleichen Parametern aufruft, ist das zum Testen ganz schön, aber nicht alle Probleme sind so einfach. Ich bin gespannt, wie man in funktionalen Sprachen zustandsbehaftete Probleme löst.

 

 

Posted in Programmierung | Kommentare deaktiviert

Quellcode Analyse mit JaMoPP

Heute war ich mal wieder auf einer Veranstaltung der JUG-BB. Diesmal ging es um ein Tool zur Quellcode Analyse und Manipulation -JaMoPP.

Basis ist ein auf EMF basierendes Modell des Java-Quellcodes. Der Quellcode kann in das Modell umgewandelt werden und auch wieder geschrieben werden. Das schöne daran ist, dass alle Formatierungen erhalten bleiben – auch Whitespaces sind Elemente des Modells.

Diese Modell ist nun der Programmierung über die API zugänglich. Damit ist nun eine ganz andere Art von Validierung des Quellcodes möglich, als sie beispielsweise Findbugs bietet. Diese Validierungen können insbesondere in größeren Projektteams dazu dienen, die Einhaltung von z.B. Architekturrichtlinien oder Namenskonventionen zu wahren.

Da das Modell auch wieder in die Dateien geschrieben werden kann, ist dieses Tool auch in der Lage, den Quellcode automatisiert zu ändern. Damit können z.B. Klassen durch andere Implementationen ersetzt werden, Objekterzeugungen können durch einen Aufruf einer Factorymethode ersetzt werden etc.

Für JaMoPP gibt es auch ein Eclipse Plugin, das jedoch sehr viel Hauptspeicher erfordert, die Entwickler empfehlen daher den Einsatz über Unittest im CI Server.

Posted in Allgemein | Kommentare deaktiviert

Abenteuer Clojure

Im Laufe der Zeit habe ich schon einige Programmiersprachen kennengelernt. Meiner ersten Schritte habe ich mit Basic auf einem Commode C64 gemacht, dann in dBase die Daten meiner Diplomarbeit ausgewertet und damit das imperative und prozedurale Programmieren gelernt. Dann habe ich das objektorientierte Programmieren mit Powerbuilder, Delphi und Java entdeckt und es im Laufe der Zeit immer wieder neu gelernt. Die wirkliche Idee habe ich vielleicht erst über die Beschäftigung mit Smalltalk verstanden (das Schöne an Smalltalk zeigt z.B. der Artikel Reading Smalltalk).

Das alles war spannend, jedoch war der Schritt vom prozeduralen zum objektorientierten Programmieren nicht besonders groß, da auch die genannten Sprachen imperative Sprachen sind. Er ist nicht vergleichbar mit dem Schritt vom objektorientiert-imperativen zum funktionalen Programmieren mit einer Sprache aus der Lisp Familie. Dank der regen Clojure Community gibt es gute Webseiten, auf denen man diese Sprache erlernen kann. Das Aufregendste daran für mich ist es, ohne Variablen zu programmieren. Das stellt mein gewohntes Denken vor wirklich neue Herausforderungen. Man kann über Algorithmen nicht in der gewohnten Weise denken. Werte sind im Prinzip erst einmal unveränderlich. Das ist ein Abenteuer. Es fühlt sich an, als ob man  das Laufen neu lernte, oder als ob ein Klarinettist auf einmal Geige spielen lernte.

Posted in Programmierung | Kommentare deaktiviert

Verwirrende Meldung

Oracle beantwortet die Abfrage

select (count(*) > 0) from dual

mit der Fehlermeldung

ORA-00907: Rechte Klammer fehlt

Interessanterweise in PL/SQL wird eine äquivalente Anweisung sogar fehlerfrei compiliert, führt aber zu einem Laufzeitfehler mit der gleichen Meldung.
Was auf den ersten Blick verwirrt, ist im nachhinein einfach. Der Ausdruck ist ein Boolean und dieser Datentyp gehört leider nicht zu den SQL Datentypen, sondern nur zu den PL/SQL Datentypen von Oracle.

Eine sprechendere Fehlermeldung hätte Oracle schon spendieren können.

Posted in Allgemein | Tagged | Kommentare deaktiviert

Einschränkungen für DimensionUsage

Wiederverwendung von Dimension ist schön, hat jedoch auch Grenzen. Schema Dimensionen, die über das DimensionUsage Element eingebunden werden, haben bei Mondrian folgende Einschränkung: Diese Dimensionen können nicht für degenerierte Dimensionen (degenerate dimension) verwendet werden. Erklärung findet sich im Pentaho Forum:

We obviously want the shared dimensions to have the same set of members, regardless of which cube they are used in, and your ‘table-less’ shared dimensions wouldn’t behave like that.

Das bedeutet, dass es genau dann zu einem Fehler mit Shared degenerate dimensions kommt, wenn diese Dimensionen in unterschiedlichen Cubes verwendet werden, welche auf unterschiedlichen Fakt Tabellen basiert. In diesem Falle bekommt man folgendenen Fehler:

Mondrian Error:In usage of hierarchy '[hierarchyName]' 
in cube 'NameOfCube', you must specify a foreign key,
because the hierarchy table is different from the fact table.
Posted in Business Intelligence | Tagged | Kommentare deaktiviert