diff --git a/chapters/thesis/appendix05.tex b/chapters/thesis/appendix05.tex index 08504df..9d590fe 100644 --- a/chapters/thesis/appendix05.tex +++ b/chapters/thesis/appendix05.tex @@ -5,23 +5,24 @@ %******************************************************* % If problems with the headers: get headings in appendix etc. right %\markboth{\spacedlowsmallcaps{Appendix}}{\spacedlowsmallcaps{Appendix}} -\chapter{JSF Performance Measure} -\label{ap:jsf_performance_measure} +\chapter{JSF Performance Statistics Servlet} +\label{ap:jsf_performance_statistics_servlet} -Für die Protokollierung der Abläufe im \ac{JSF} werden zwei Klassen benötigt. Die Factory \ref{lst:logger_factory}, -wird die Wrapper"=Klasse in die Bearbeitungsschicht eingeschleust. Diese Wrapper"=Klasse \ref{lst:logger} beinhaltet -dann die eigentliche Performance-Messung, inklusive der Ausgabe in die Log"=Datei des \textit{Glassfish}"=Servers. -Zusätzlich muss in der Konfiguration \textbf{faces-config.xml} noch angepasst werden, wie in -\ref{lst:logger_factory_activate}, um die Factory durch das System aufrufen zu lassen. +Um die Cache-Informationen über einen \ac{API}"=Aufruf bereitzustellen, wird ein Servlet \ref{lst:servlet} und ein +Provider \ref{lst:servlet_provider} benötigt. Für die Aufnahme in der Routing, wird noch ein zusätzlicher Eintrag in +der \textit{web.xml} benötigt \ref{lst:servlet_activate}. -\includecode[java]{chapters/thesis/appendix05_Logger.java}{lst:logger}{Vdi Logger} -\includecode[java]{chapters/thesis/appendix05_LoggerFactory.java}{lst:logger_factory}{Vdi Logger Factory} +\includecode[java]{chapters/thesis/appendix05_servlet.java}{lst:servlet}{Performance Statistics Servlet} +\includecode[java]{chapters/thesis/appendix05_provider.java}{lst:servlet_provider}{Performance Statistics Provider} -\begin{lstlisting}[language=xml,caption={Einbindung Factory},label=lst:logger_factory_activate] - - - de.wedekind.utils.VdlLoggerFactory - - +\begin{lstlisting}[language=xml,caption={Einbindung Servlet},label=lst:servlet_activate] + + PerformanceStatisticServlet + de.wedekind.servlets.PerfStatServlet + + + PerformanceStatisticServlet + /api/perfstat + \end{lstlisting} diff --git a/chapters/thesis/appendix05_Provider.java b/chapters/thesis/appendix05_Provider.java new file mode 100644 index 0000000..13ad855 --- /dev/null +++ b/chapters/thesis/appendix05_Provider.java @@ -0,0 +1,41 @@ +public interface PerfStatistics { + String getOpenJPAStatistics(); + String getEntityClasses(); +} + +@Stateless +public class PerfStatisticsImpl implements PerfStatistics { + @Inject + private EntityManagerJPAFactory entityManagerFactory; + private OpenJPAEntityManagerFactory openJPAEntityManagerFactory; + + @PostConstruct + public void init() { + openJPAEntityManagerFactory = OpenJPAPersistence.cast(entityManagerFactory.getEntityManager().getEntityManagerFactory()); + } + + @Override + public String getOpenJPAStatistics() { + StoreCache cache = openJPAEntityManagerFactory.getStoreCache(); + + CacheStatistics st = cache.getStatistics(); + return String.format("{ \"ReadCount\": %d, \"HitCount\": %d, \"WriteCount\": %d }", st.getReadCount(), st.getHitCount(), st.getWriteCount()); + } + + @Override + public String getEntityClasses() { + StringBuilder res = new StringBuilder("{ \"Entities\": ["); + + EntityManagerFactory emf = openJPAEntityManagerFactory; + EntityManager em = emf.createEntityManager(); + Cache cache = emf.getCache(); + for(EntityType entityType : em.getMetamodel().getEntities()) { + Class cls = entityType.getBindableJavaType(); + res.append(String.format("\"%s\", ", cls.getCanonicalName())); + } + res.append("\"\"], \"Cache\": \""); + res.append(cache.toString()); + res.append("\" }"); + return res.toString(); + } +} diff --git a/chapters/thesis/appendix05_servlet.java b/chapters/thesis/appendix05_servlet.java new file mode 100644 index 0000000..25baf85 --- /dev/null +++ b/chapters/thesis/appendix05_servlet.java @@ -0,0 +1,25 @@ +public class PerfStatServlet extends HttpServlet { + private final static Logger m_Logger = Logger.getLogger(PerfStatServlet.class.toString()); + + @Inject + private PerfStatistics perfStatistics; + + public PerfStatServlet() { + m_Logger.info("PerfStatServlet() Constructor"); + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String openJPAStatistics = perfStatistics.getOpenJPAStatistics(); + String entityClasses = perfStatistics.getEntityClasses(); + + resp.setStatus(HttpServletResponse.SC_OK); + resp.setContentType(MediaType.APPLICATION_JSON); + PrintWriter out = resp.getWriter(); + out.print("{ \"openJPAStatistics\": "); + out.print(openJPAStatistics); + out.print(", \"entityClasses\": "); + out.print(entityClasses); + out.print(" }"); + } +} diff --git a/chapters/thesis/appendix06.tex b/chapters/thesis/appendix06.tex new file mode 100644 index 0000000..32a8b0a --- /dev/null +++ b/chapters/thesis/appendix06.tex @@ -0,0 +1,27 @@ +% !TeX root = ../../thesis.tex + +%******************************************************************** +% Appendix +%******************************************************* +% If problems with the headers: get headings in appendix etc. right +%\markboth{\spacedlowsmallcaps{Appendix}}{\spacedlowsmallcaps{Appendix}} +\chapter{JSF Performance Measure} +\label{ap:jsf_performance_measure} + +Für die Protokollierung der Abläufe im \ac{JSF} werden zwei Klassen benötigt. Die Factory \ref{lst:logger_factory}, +wird die Wrapper"=Klasse in die Bearbeitungsschicht eingeschleust. Diese Wrapper"=Klasse \ref{lst:logger} beinhaltet +dann die eigentliche Performance-Messung, inklusive der Ausgabe in die Log"=Datei des \textit{Glassfish}"=Servers. +Zusätzlich muss in der Konfiguration \textbf{faces-config.xml} noch angepasst werden, wie in +\ref{lst:logger_factory_activate}, um die Factory durch das System aufrufen zu lassen. + +\includecode[java]{chapters/thesis/appendix06_Logger.java}{lst:logger}{Vdi Logger} + +\includecode[java]{chapters/thesis/appendix06_LoggerFactory.java}{lst:logger_factory}{Vdi Logger Factory} + +\begin{lstlisting}[language=xml,caption={Einbindung Factory},label=lst:logger_factory_activate] + + + de.wedekind.utils.VdlLoggerFactory + + +\end{lstlisting} diff --git a/chapters/thesis/appendix05_Logger.java b/chapters/thesis/appendix06_Logger.java similarity index 100% rename from chapters/thesis/appendix05_Logger.java rename to chapters/thesis/appendix06_Logger.java diff --git a/chapters/thesis/appendix05_LoggerFactory.java b/chapters/thesis/appendix06_LoggerFactory.java similarity index 100% rename from chapters/thesis/appendix05_LoggerFactory.java rename to chapters/thesis/appendix06_LoggerFactory.java diff --git a/chapters/thesis/chapter03.tex b/chapters/thesis/chapter03.tex index fa70541..b953696 100644 --- a/chapters/thesis/chapter03.tex +++ b/chapters/thesis/chapter03.tex @@ -14,23 +14,37 @@ auf mögliche Optimierungen untersucht und bewertet. Für die Untersuchung des Systems wird der direkte Zugang zum Server benötigt. Hierbei werden zuerst die im Kapitel \ref{sec:basics:services} beschriebenen Einstellungen überprüft. -Zuerst wird am PostgreSQL"=Server die Konfiguration der Speicher mit der Vorgabe für Produktivsystem abgeglichen. +Zuerst wird am PostgreSQL"=Server die Konfiguration der Speicher mit der Vorgabe für Produktivsysteme abgeglichen. Hierunter fallen die Einstellungen für die \textit{shared\_buffers}, der bei einem Arbeitsspeicher von mehr als 1 GB -circa 25\% des Arbeitsspeicher definiert sein soll \cite{PostgresC20.4:2024}. +circa 25\% des Arbeitsspeicher besitzen sollte \cite{PostgresC20.4:2024}. -\mytodos{die anderen Speicher abarbeiten?} +Bei der Einstellung \textit{temp\_buffers} geht es um den Zwischenspeicher für jede Verbindung, die bei der Verwendung +von temporären Tabellen verwendet wird. Dieser Wert sollte auf dem Standardwert von 8 MB belassen werden. Und nur bei +der Verwendung von großen temporären Tabellen verändert werden. + +Der Speicher, der für eine Abfrage verwendet werden darf, wird über die Konfiguration \textit{work\_mem} gesteuert. +Wenn der Speicher zu gering wird, werden die Zwischenergebnisse in temporäre Dateien ausgelagert. Der empfohlene Wert +berechnet sich aus \textit{shared\_buffers} dividiert durch \textit{max\_connections} \citep{ConfigTo12:online}. +Sollte der Berechnung außerhalb der Grenzwerte von 1 MB und 256 MB liegen, ist der jeweilige Grenzwert zu verwenden. +Um zu ermitteln, ob die Konfiguration richtig ist, muss im PostgreSQL die Einstellung \textit{log\_temp\_files} auf 0 +gesetzt werden. Mit dieser kann ermittelt, ob temporäre Dateien verwendet werden und deren Größe. Bei vielen kleineren +Dateien sollte der Grenzwert erhöht werden. Bei wenigen großen Dateien ist es ist sinnvoll den Wert so zu belassen. + +Für die Wartungsaufgaben wie VACUUM oder dem erstellen von Indexen wird die Begrenzung über die Einstellung +\textit{maintenance\_work\_mem} gesetzt. Dieser Wert sollte 5\% des verfügbaren Arbeitsspeicher entsprechen und größer +als \textit{work\_mem} sein. Dann wird mit dem Systemtools, wie den Konsolenanwendungen \textit{htop} und \textit{free}, die Auslastung des Servers -überprüft. Hierbei ist die CPU-Leistung, der aktuell genutzte Arbeitsspeicher, sowie die Zugriffe auf die Festplatte +überprüft. Hierbei ist die CPU"=Leistung, der aktuell genutzte Arbeitsspeicher, sowie die Zugriffe auf die Festplatte die wichtigen Faktoren zur Bewertung. -Die CPU-Leistung sollte im Schnitt nicht die 70\% überschreiten, für kurze Spitzen wäre dies zulässig. Da sonst der +Die CPU"=Leistung sollte im Schnitt nicht die 70\% überschreiten, für kurze Spitzen wäre dies zulässig. Da sonst der Server an seiner Leistungsgrenze arbeitet und dadurch es nicht mehr schafft die gestellten Anfragen schnell genug abzuarbeiten. -Da unter Linux der Arbeitsspeicher nicht mehr direkt freigegeben wird, ist hier die Page-Datei der wichtigere Indikator. +Da unter Linux der Arbeitsspeicher nicht mehr direkt freigegeben wird, ist hier die Page"=Datei der wichtigere Indikator. Wenn dieses in Verwendung ist, dann benötigen die aktuell laufenden Programme mehr Arbeitsspeicher als vorhanden ist, -wodurch der aktuell nicht verwendete in die Page-Datei ausgelagert wird. Hierdurch erhöhen sich die Zugriffszeiten auf +wodurch der aktuell nicht verwendete in die Page"=Datei ausgelagert wird. Hierdurch erhöhen sich die Zugriffszeiten auf diese Elemente drastisch. Die Zugriffsgeschwindigkeit, die Zugriffszeit sowie die Warteschlange an der Festplatte zeigt deren Belastungsgrenze auf. @@ -58,8 +72,8 @@ abgelaufen ist. Ebenso wird nach Standorten sortiert, um zu prüfen mit welchen \mytodos{Hier noch mehr Infos dazu, für was genau die Editoren diese tun} Da die Daten in der 3. Normalform in der Datenbank gespeichert werden, sind einige Relationen für die Abfragen -notwendig. Dies wird durch die generische Abfrage in \autoref{lst:documentlist} gezeigt. Zusätzlich wird für jedes -dargestellte Dokument eine zusätzliche Abfrage durchgeführt, die in \autoref{lst:documentlist_sub} zeigt, dass auch hier +notwendig. Dies wird durch die generische Abfrage in \ref{lst:documentlist} gezeigt. Zusätzlich wird für jedes +dargestellte Dokument eine zusätzliche Abfrage durchgeführt, die in \ref{lst:documentlist_sub} zeigt, dass auch hier weitere Relationen notwendig sind. \includecode[SQL]{chapters/thesis/chapter03_documentlist.sql}{lst:documentlist}{Generische Abfrage der Dokumentenliste} @@ -104,7 +118,7 @@ welchen Umfang die Umstellungen eine Veränderung im Verhalten der Webseite bewi Bei den \ac{JSF} wird eine Zeitmessung eingefügt. Hierfür wird eine \textit{Factory} eingebaut, die sich in die Verarbeitung der Seiten einhängt, und damit die Zeiten für das Ermitteln der Daten, das Zusammensetzen und das -Render der Sicht aufgenommen werden können. Die Zeiten werden in die Log°=Datei des \textit{Glassfish}!=Servers +Render der Sicht aufgenommen werden können. Die Zeiten werden in die Log"=Datei des \textit{Glassfish}"=Servers hinterlegt und durch das Skript ausgewertet. Somit ist es einfach aufzuzeigen, an welcher Stelle der größte Teil der Verzögerung auftritt. diff --git a/chapters/thesis/chapter04.tex b/chapters/thesis/chapter04.tex index b59243b..9f212f8 100644 --- a/chapters/thesis/chapter04.tex +++ b/chapters/thesis/chapter04.tex @@ -17,6 +17,23 @@ prüfen, die den Cache von OpenJPE auswerten} \section{Überprüfung des PostgreSQL und Servers} \label{sec:performance-checking:postgresql-checking} +Die einfachste Art die Einstellungen zu prüfen ist, die Abfrage in \ref{lst:postgresql-select-settings} am +Datenbankserver auszuführen. + +\begin{lstlisting}[language=SQL,caption={Ermitteln der PostgreSQL Einstellungen},label=lst:postgresql-select-settings] +SELECT name AS setting_name + , setting AS setting_value + , unit AS setting_unit +FROM pg_settings +WHERE name IN ( + 'shared_buffers' + , 'temp_buffers' + , 'work_mem' + , 'max_connections' + , 'maintenance_work_mem' + ) +\end{lstlisting} + \mytodos{Konfiguration vom Produktionsserver prüfen} \section{Einbau und Aktivieren von Performance-Messung} @@ -61,6 +78,9 @@ log_rotation_size = 100MB wichtigste Einstellung ist \textit{log\_min\_duration\_statement}, diese definiert ab welcher Laufzeit eine Abfrage protokolliert werden soll. Mit dem Wert 0 werden alle Abfragen protokolliert. Alle weitere Einstellungen sind so gesetzt, dass nicht unnötige Abfragen für die spätere Auswertung mit \textit{pgBadger} protokolliert werden. +Zusätzlich ist die Einstellung \textit{log\_temp\_files} auf 0 zu setzen. Dadurch werden alle erzeugten temporären +Dateien und ihre Größe ebenfalls protokolliert. Diese Dateien entstehen, wenn der temporäre Puffer für die Abfrage +nicht ausreicht und die Zwischenergebnisse ausgelagert werden müssen. \begin{lstlisting}[language=yaml,caption={PostgreSQL Ausgabekonfiguration},label=lst:postgresql_logconf] log_min_duration_statement = 0 @@ -74,6 +94,6 @@ log_error_verbosity = default log_hostname = on log_lock_waits = on log_statement = 'none' -log_temp_files = -1 +log_temp_files = 0 log_timezone = 'Europe/Berlin' \end{lstlisting} diff --git a/chapters/thesis/chapter05.tex b/chapters/thesis/chapter05.tex index 63399c9..0abf175 100644 --- a/chapters/thesis/chapter05.tex +++ b/chapters/thesis/chapter05.tex @@ -257,7 +257,7 @@ abfragt. \label{tbl:measure-cached-queries} \end{table} -\section{Caching im \ac{JPA}} +\section{Caching im JPA} \label{sec:performance-investigation-application:caching-jpa} %hier ein test vom acf: \acf{JPA}. aber ich seh ihn nicht @@ -301,7 +301,7 @@ abfragt. %Wie man an den Daten erkennen kann, wird der Cache vom \ac{JPA} für diese Abfrage nicht verwendet, sonst müssten die %Anzahl der Abfragen an die Datenbank drastisch reduziert werden. Selbst die Laufzeit ändert sich nur marginal. -\section{Caching in \ac{EJB}} +\section{Caching in EJB} \label{sec:performance-investigation-application:caching-ejb} Die Cache-Einstellungen des \ac{EJB} sind in der Admin-Oberfläche des Payara-Servers zu erreichen. Hier @@ -327,7 +327,7 @@ Die Cache-Einstellungen des \ac{EJB} sind in der Admin-Oberfläche des Payara-Se \label{tbl:measure-ejb-cache-active} \end{table} -\section{Abfragen \ac{JPQL}} +\section{Abfragen JPQL} \label{sec:performance-investigation-application:query-jpql} Für die \ac{JPQL} wird ein \ac{SQL} ähnlicher Syntax verwendet um die Abfragen an die Datenbank durchzuführen. Für die @@ -579,7 +579,8 @@ CREATE INDEX idx_searchdocument_startdate CREATE INDEX idx_searchdocument_addressees_first_entry ON searchdocument - (((addressees->0->>'surname')::text), ((addressees->0->>'firstname')::text)); + ( ((addressees->0->>'surname')::text) + , ((addressees->0->>'firstname')::text)); CREATE INDEX idx_searchdocument_city ON searchdocument (city); @@ -650,8 +651,6 @@ Nach dem Anpassungen haben sich dann die Werte aus \ref{tbl:measure-materialized \label{tbl:measure-materialized-view-ext} \end{table} -\mytodos{prüfen ob ms oder msec die richtige SI-Einheit ist} - Da bei der Materialized View das laden der Daten und das wandeln in die Java"=Objekte getrennt programmiert wurde, können hier eigene Zeitmessungen für die zwei Schritte eingebaut werden. Hierfür wird die Zeit vor dem \textit{map}"=Aufruf und der \textit{map}"=Aufruf gemessen. Für den ersten Aufruf, wurde ein \textit{SearchDocument} diff --git a/expose-ref.bib b/expose-ref.bib index ff3abd5..48be266 100644 --- a/expose-ref.bib +++ b/expose-ref.bib @@ -54,6 +54,15 @@ urldate = {2024-03-27} }, +@online{ConfigTo12:online, + author = {}, + title = {Config - Too small work\_mem · pganalyze}, + url = {https://pganalyze.com/docs/checks/settings/work_mem}, + month = {}, + year = {}, + urldate = {2024-09-12} +}, + @online{AspNetCore:2024:MVC, year = 2024, url = {https://learn.microsoft.com/de-de/aspnet/core/fundamentals/middleware/?view=aspnetcore-8.0}, diff --git a/thesis.pdf b/thesis.pdf index 322d277..89e3ade 100644 Binary files a/thesis.pdf and b/thesis.pdf differ diff --git a/thesis.tex b/thesis.tex index 9252397..0b785ef 100644 --- a/thesis.tex +++ b/thesis.tex @@ -102,7 +102,7 @@ \include{chapters/thesis/appendix03} \include{chapters/thesis/appendix04} \include{chapters/thesis/appendix05} -%\include{chapters/examples/appendix02} +\include{chapters/thesis/appendix06} %************************************************************************* % Other Stuff in the Back %*************************************************************************