Jedes Byte zählt. Tuning-Rezepte für APEX-Anwendungen in der Cloud Andreas Wismann WHEN OTHERS Beratung | Projektmanagement | Coaching rund um Oracle Application Express [email protected] WHEN OTHERS Beratung | Projektmanagement | Coaching rund um Oracle Application Express Andreas Wismann Dipl.-Informatiker (FH) http://when-others.com [email protected] +49 176 7800 3109 +49 2131 314 9966 @whenothers Wo kann man optimieren? • Webserver • Datenbank-Konfiguration • APEX-Konfiguration • Datenübertragung • Menge an HTML-Code • Menge an Daten • Geschwindigkeit der Skripte (SQL, PL/SQL, HTML, CSS, JavaScript) Wo kann man optimieren? Webserver Datenbank-Konfiguration APEX-Konfiguration Datenübertragung • Menge an HTML-Code • Menge an Daten • Geschwindigkeit der Skripte (SQL, PL/SQL, HTML, CSS, JavaScript) Cloud? • oft wenig/kein Einfluss auf die Parameter des Webservers, der Datenbank und der APEX-Instanz • Einfluss auf die Programmierung • "echte" Performance • "gefühlte Performance" Tuning-Rezepte… ALTER SESSION SET FAST=TRUE messen… • wie "groß" ist meine Seite? • wie "schnell" ist meine Seite? • Responsezeiten mit dem Kunden festlegen Welche Region ist langsam? • Session Debug View zeigt die Prozessdauer an #TIMING# Welche Region ist langsam? • Substitutionsvariable #TIMING# in jeden Region Footer einbauen 10 Sekunden tippen #ROWS_FETCHED# von #TOTAL_ROWS# Zeile(n) abgerufen in #TIMING# Sekunden SQL des Schreckens declare v_count number; begin select into from where count(*) v_count tabelle gueltig_bis < sysdate; if v_count > 0 then delete from tabelle where gueltig_bis < sysdate; end if; end; Langläufer-SQL • kritisch hinterfragen • optimieren • schon auf Seite 100 (während LOGIN) ausführen Regelmäßiger Code-Review Langsame Interaktive Reports • Report erscheint nicht sofort beim Öffnen der Seite • Browser reagiert nicht • subjektives Empfinden: »APEX ist schuld« • besser wäre: »der Report ist schuld« • Ziel: verhindern, dass Benutzer beim Aufruf einer Seite warten müssen Langsame Interaktive Reports • Hidden Item in die WHERE-Klausel einbauen: … where :P1000_SHOW_SQL > 0 and … • Default-Anfangswert: 0 oder NULL • Durch Interaktion auf 1 setzen 30 Minuten Programmieren &Testen Lang laufende Sub-Selects • Subselect: … where ID ) in ( SELECT ID FROM … where … IN (…, …) • Subselect ersetzen durch Wertemenge in APEX_COLLECTIONS: … where ID in ( select n001 from APEX_COLLECTIONS where collection_name = 'SUBSELECT_1' ) Lang laufende Sub-Selects • SQL-Zwischenergebnisse in APEX_COLLECTIONS parken APEX_COLLECTION.CREATE_COLLECTION_FROM_QUERY ( p_collection_name => 'Subselect 1', p_query => 'SELECT ID FROM …'); • Individuelle Zwischenergebnisse pro Benutzer verwenden • ins Thema einlesen • 15 Minuten Programmierung Viele / große Web-Dateien • JavaScript- und CSS-Dateien verzögern das Laden der ersten Anwendungsseite • Bilder erscheinen mit Verspätung • Seite flackert Viele / große Web-Dateien Viele / große Web-Dateien • Sämtliche Dateien auf der LOGIN-Seite vorausladen (Template anpassen) • CDN (Content Delivery Network) verwenden • CSS-Sprites oder Icon-Fonts einsetzen, wenn viele Icons geladen werden ("Font Awesome") • 5 Minuten Programmierung • 1 Klick "For Your Eyes Only" • zum Beispiel eine Region, die Table Statistics anzeigt, auf Page 0 (Global Page) anlegen • nur für Entwickler, per Authorization Scheme: RETURN apex_application.g_edit_cookie_session_id IS NOT NULL; • Report: 5 Minuten • Authorization Scheme: 2 Minuten Caching • Das "Dashboard" lädt langsam • ausgerechnet die Haupt-Anwendungsseite… • viele Benutzer unterwegs • große Datenmengen werden abgefragt Caching Caching Caching APEX_UTIL.CACHE_GET_DATE_OF_REGION_CACHE( p_application => :APP_ID, p_page => 1, p_region_name => 'MY TABLES' ), Caching Caching • Ziehen Sie das APEX Region Caching in Erwägung • Invalidieren Sie den Cache beizeiten: APEX_UTIL.PURGE_REGIONS_BY_NAME APEX_UTIL.PURGE_REGIONS_BY_PAGE • Insbesondere der Kurzzeit-Cache (10 Sek.) beschleunigt Seiten für "herumklickende" Benutzer 2 Klicks Viele Regionen oder Items mit gleichen Conditions • zwei oder mehr Regionen haben die ähnliche Conditions • Conditions sind komplex Viele Regionen oder Items mit gleichen Conditions Viele Regionen oder Items mit gleichen Conditions • Parent-Region mit dieser Condition erstellen (logischer Container) • andere Regionen und Items unterordnen • Parent-Region hat selbst kein "sichtbares" Template • DRY "Aufräumen": 15 Minuten pro Seite Bedingungen für Prozesse if ist_vertrag_gueltig (:P500_VERTRAGSNUMMER) = TRUE and :P500_KUNDENNUMER IS NOT NULL then -- viel Code, viele Aktionen … select … ; select … ; end if; Bedingungen für Prozesse if ist_vertrag_gueltig (:P500_VERTRAGSNUMMER) and :P500_KUNDENNUMER IS NOT NULL then -- viel Code, viele Aktionen … select … ; select … ; end if; Bedingungen für Prozesse if ist_vertrag_gueltig (:P500_VERTRAGSNUMMER) and :P500_KUNDENNUMER IS NOT NULL then -- viel Code, viele Aktionen … select … ; select … ; end if; Bedingungen für Prozesse if :P500_KUNDENNUMER IS NOT NULL and ist_vertrag_gueltig (:P500_VERTRAGSNUMMER) then -- viel Code, viele Aktionen … select … ; select … ; end if; Bedingungen für Prozesse if :P500_KUNDENNUMER IS NOT NULL and ist_vertrag_gueltig (:P500_VERTRAGSNUMMER) then -- viel Code, viele Aktionen … select … ; select … ; end if; Bedingungen für Prozesse if :P500_KUNDENNUMER IS NOT NULL and ist_vertrag_gueltig (:P500_VERTRAGSNUMMER) then -- viel Code, viele Aktionen … select … ; select … ; end if; Bedingungen für Prozesse if ist_gueltige_kundennummer(:P500_KUNDENNUMMER) then -- viel Code, viele Aktionen … select … ; select … ; end if; Bedingungen für Prozesse • Äußere Bedingungen? IMMER als Condition definieren • Erspart unnötiges Parsen des Programmcodes • APEX wertet Conditions sehr schnell aus • Shortcut-Bedingungen im Programmcode an die erste Stelle platzieren • zusätzlich: keine Testen auf Langsamkeit BEGIN DBMS_LOCK.SLEEP (10); -- 10 Sekunden END; -- üblicherweise nicht auf PUBLIC gegranted BEGIN APEX_UTIL.PAUSE (10); -- max. 120 Sekunden END; -- PUBLIC darf das ausführen! Schnelle HTML-Tabellen • SQL-Abfrage in einem Package ausführen • Alle Zeilen per PL/SQL-Loop durchlaufen • Jede Zeile als HTML formulieren • gesamtes Tabellen-HTML zum Browser senden Schnelle HTML-Tabellen htp.p ('<table>'); for c in (SELECT … ) loop htp.p ('<tr>' || '<td>' || c.spalte1 || '</td>' || '<td>' || c.spalte2 || '</td>' || '</tr>'); end loop; htp.p ('</table>'); Schnelle HTML-Tabellen • Reports, • die der Benutzer nicht konfigurieren muss • die nicht auf APEX Report-Feeatures angewiesen sind • … direkt per HTML aus der Datenbank ausgeben 30 Minuten pro Report Ladebalken • Einige Prozesse lassen sich nicht beschleunigen • Warten??? verunsichert die Benutzer • der Browser wird unbedienbar Ladebalken • Langlaufender Prozess gestartet mittels DBMS_SCHEDULER.CREATE_JOB • Status-Updates per DBMS_APPLICATION_INFO.SET_SESSION_LONGOPS schreiben • Status per V$SESSION_LONGOPS auslesen • geJOINT mit Details in benutzerdefinierter Tabelle ("PROCESSING_STEPS") • per AJAX Ladebalken und Statusreport aktualisieren Demo Menüvolumen verringern • Original-HTML eines Links: <li><a href="f?p=125:3:8629145276863:::::">zur Seite 3</a></a></li> (67 Zeichen) • gemeinsame Informationen für alle Seiten: Server, Session-ID f?p=,125,8629145276863 (22) • individuelle Informationen pro Seite: Seitennummer, Seitentitel 3,zur Seite 3 (14 inkl. Komma und Zeilenvorschub) APEX-Menüliste mit 100 Seiten: 67 kB Ersetzungsverfahren: 1400 Byte + ein jQuery-Skript Ersparnis: > 60 kB pro Seitenaufruf Menüvolumen verringern übertragen 1,zur Seite 1 2,zur Seite 2 3,zur Seite 3 4,zur Seite 4 5,zur Seite 5 6,zur Seite 6 7,zur Seite 7 8,zur Seite 8 9,zur Seite 9 10,zur Seite 10 11,zur Seite 11 12,zur Seite 12 13,zur Seite 13 14,zur Seite 14 15,zur Seite 15 16,zur Seite 16 17,zur Seite 17 18,zur Seite 18 19,zur Seite 19 20,zur Seite 20 per Skript berechnen <li><a <li><a <li><a <li><a <li><a <li><a <li><a <li><a <li><a <li><a <li><a <li><a <li><a <li><a <li><a <li><a <li><a <li><a <li><a <li><a href="f?p=125:1:8629145276863:::::">zur Seite 1</a></a></li> href="f?p=125:2:8629145276863:::::">zur Seite 2</a></a></li> href="f?p=125:3:8629145276863:::::">zur Seite 3</a></a></li> href="f?p=125:4:8629145276863:::::">zur Seite 4</a></a></li> href="f?p=125:5:8629145276863:::::">zur Seite 5</a></a></li> href="f?p=125:6:8629145276863:::::">zur Seite 6</a></a></li> href="f?p=125:7:8629145276863:::::">zur Seite 7</a></a></li> href="f?p=125:8:8629145276863:::::">zur Seite 8</a></a></li> href="f?p=125:9:8629145276863:::::">zur Seite 9</a></a></li> href="f?p=125:10:8629145276863:::::">zur Seite 10</a></a></li> href="f?p=125:11:8629145276863:::::">zur Seite 11</a></a></li> href="f?p=125:12:8629145276863:::::">zur Seite 12</a></a></li> href="f?p=125:13:8629145276863:::::">zur Seite 13</a></a></li> href="f?p=125:14:8629145276863:::::">zur Seite 14</a></a></li> href="f?p=125:15:8629145276863:::::">zur Seite 15</a></a></li> href="f?p=125:16:8629145276863:::::">zur Seite 16</a></a></li> href="f?p=125:17:8629145276863:::::">zur Seite 17</a></a></li> href="f?p=125:18:8629145276863:::::">zur Seite 18</a></a></li> href="f?p=125:19:8629145276863:::::">zur Seite 19</a></a></li> href="f?p=125:20:8629145276863:::::">zur Seite 20</a></a></li> Große Selectlisten <select name="f04" id="f04_0000"> <option <option <option <option <option value="1" value="2" value="3" value="4" value="5" >Dokument >Dokument >Dokument >Dokument >Dokument mit mit mit mit mit der der der der der … … … … … … </select> Demo 250 Einträge: 14 kb pro Selectliste 200 kB pro Seitenaufruf Nummer Nummer Nummer Nummer Nummer 1</option> 2</option> 3</option> 4</option> 5</option > Große Selectlisten function selectlistenErsetzen () { var selectListOriginal = $('#P4_SELECTLIST_ORIGINAL'); // finde die Selectlisten im Tabular Form $('td[headers="DOKUMENT_ID"] select') .each(function() { // … und ersetze die Dummys mit dem Original: $(this).html( selectListOriginal.html() ); // richtige Auswahl einstellen … }); } Prozesse • alles was "länger ist als drei Zeilen" in die Datenbank verlagern einzeilig im APEX-Prozessfenster aufrufen Pipelined Function • zur Aufspaltung in "einfachen" und "teuren" Teil eines Reports • zur Vereinfachung von überkomplexem SQL-Code • wenn Sie den Ausführungsplan nicht in den Griff bekommen Templates vereinfachen • APEX-Templates bieten viele Features • sind auf Funktionalität, Komfort und Optik optimiert … auf Kosten der HTML-Effizienz? … auf Kosten der Ladezeit? last but not least • gute Tools beschleunigen den Entwicklungszyklus (anderer Aspekt, aber nicht weniger wichtig) • Debugging abschalten • statische Conditions in Build Options umwandeln Buchempfehlung • Steve Souders • 2009 • 244 Seiten • Sehr viele Anregungen rund um die technische Architektur von Webseiten WHEN OTHERS Beratung | Projektmanagement | Coaching rund um Oracle Application Express Andreas Wismann Dipl.-Informatiker (FH) http://when-others.com [email protected] +49 176 7800 3109 +49 2131 314 9966 @whenothers