Tja was soll ich sagen. Also erstmal: Nein, ich war nicht so lange krank ;). Es passierte nur einfach sehr viel in dem Jahr und doch gab es wenig Technisches zu Schreiben.

Derzeit sammle ich allerdings wieder Themen für Artikel. Möglicherweise mal etwas über Perfomance-Tuning in SAP (Das ESAT-Orakel) oder über IT-Zertifikate (davon gibt es gefühlt 3000). Oder irgendwas zum Thema Requirement Engineering im Scrum-Umfeld.

Mal sehen, vorerst wollte ich nur das ihr wisst, dass hier noch einer ist. 🙂 Ach ja, falls ihr Themenvorschläge habt, einfach einen Kommentar unten anfügen. 😉

Krank sein ist doof

So, damit könnte ich den Artikel beenden und mich wieder ins Bett legen :). Aber da ich heute sowieso schon 6h geschlafen habe, wird das eh nichts bringe, also erzähle ich euch mal etwas über kranke Programmierer.

Das schlurfende Gehirn

Das Gehirn arbeitet langsam, schleppend, man hat das Gefühl, es schlurft in der Gegend herum und man muss ständig warten, bis es endlich fertig ist. Man tippt und plötzlich hält man inne. Starrt auf den Bildschirm. Wartet bis es den Gedanken zueende gedacht hat und den nächsten Codefetzen ausspuckt. Das lässt einfach keinen schönen Programmierfluss aufkommen, keine Dynamik. Und es vergisst die einfachsten Regeln. Der Compiler meldet ein fehlendes }. 10 Minuten lang tut er das, während man alles Mögliche versucht, um den Fehler einzugrenzen.

Tunnelblick

Auch die Augen scheinen an ihrer Aufgabe nicht mehr interessiert zu sein. Immer leicht neben dem Fokuspunkt schlittern sie über den Code und man schwingt wie ein Eiskunstläufer mehrmals quer über den Code, um den Cursor wiederzufinden. Oder sie beginnen zu tränen, aber das macht ja nichts, man hat ja eine Jumbopackung Taschentücher neben sich. Also einmal Augen putzen und wieder den Cursor suchen.

Störende Körperfunktionen

Drei Zeilen tippen – Nase putzen – Drei Zeilen tippen – Husten – 5 Zeilen tippen – Husten. Und alle 30 Minuten rennt man aufs Klo, wo man dann 15 Minuten verbleibt. Immerhin hat man dort Zeit, über den Code zu reflektieren. Oder man hätte Zeit, wenn sich das Gehirn den dazu durchringen würde, darüber nachzudenken. Stattdessen versucht man konzentriert nicht einzuschlafen.

Schlafen – nicht

Wenn man schon so müde ist, dann kann man bestimmt gut schlafen. Dem Körper Zeit zur Regeneration geben und am nächsten Morgen wieder fit aufwachen. Aber wir alle wissen, was dann passiert: Mit offenen Augen liegt man ihm Bett, natürlich unterbrochen von Hustenanfällen und regelmässigen Besuchen auf dem Klo, vorzugsweise dann, wenn es grad einigermassen warm geworden ist.

Fazit

Wenn ihr einen kranken Programmierer findet: Nehmt ihm die Tastatur weg, drückt ihm ein Hörspiel und eine Tasse Tee in die Hand und bringt ihn ins Bett. Das kann er dann liegen und zuhören, während er nicht schläft. Lasst ihn auf keinen Fall Architekturentscheidungen treffen oder irgendwas Wichtiges fixen. Vorallem: Haltet ihn vom Releasebutton fern!

Und damit… geh auch ich ins Bett…

Da ich einen Vortrag über Agile Softwareentwicklung halten werde, dachte ich mir, ich könnte das Thema auch mal hier thematisieren. Also beginnen wir mit Artefakten, Plannings, Storypoints, der „Definition of Done“… oder wir reden über etwas ganz Anderes.

Seht, über all diese Dinge gibt es Hunderte von Artikeln. Die sind ausserdem mit hübschen Bildern ausgestattet. Oder Videos. Oder Katzen. Da kann ich nicht mithalten (speziell nicht bei den Katzen).

Also informiert euch via Wikipedia oder Google (Ich werde ein paar weiterführende Links unten einfügen) über die Grundlagen und lasst uns gleich zu einem anderen Thema springen.

Warum geht es so oft schief?

Nur damit wir uns verstehen: Ich liebe die Agile Softwareentwicklung! Die Möglichkeit zusammen mit dem Business die Richtung in einem Projekt immer wieder ändern zu können, zu lernen, Fehler zu machen, ist einfach grossartig. Daneben erscheint mir die normale Projektarbeit mit ihren Vorstudien, Detailstudien, Kontrollinstanzen, etc. sehr starr, fast wie eine Fabrik, in der ich nur am Band stehe und Code im Akkord fertige.

Aber trotzdem gibt es Dinge, die nicht gut funktionieren und die müssen wir ansprechen, vielleicht können wir sie dann besser machen.

Agile Softwareentwicklung ist nur ein Aufkleber

In manchen Firmen ist Agile Softwareentwicklung mehr sowas wie die ISO 9001-Plaketten, die am Empfang an der Wand hängen und verstauben. Man hat irgendwann drei Entwickler in einen Scrum-Kurs geschickt, eine weisse Wand mit ein paar Strichen bemalt und seitdem erwähnt man in Offerten, dass die Entwicklung „agil“ sein wird.

Würde man den Verkäufer oder Marketingleiter dazu auffordern „Agile Softwareentwicklung“ zu definieren, gäbe es nur ein hilfloses Schulterzucken zu sehen. Das ist schliesslich etwas „Was unsere Techies tun“ und das nichts mit dem Management zu tun hat

Falsch.

Agilität muss durchgezogen werden bis oben, damit es sein volles Potenzial entfalten kann. Was bringt es uns, wenn zwar die Entwickler alle zwei Wochen in neue Richtungen entwickeln können, aber das Management nur einmal im Jahr eine Roadmap festlegt.

Lösungsvorschläge

Also was können wir tun, damit Agilität auch bis nach oben durchkommt? Hier einige typische Fragen des Business und mögliche Antworten darauf:
„Ein Projekt braucht einen Projektantrag und in dem müssen Start, Ende sowie möglichst schon alle Anforderungen eingetragen werden. Ich kann nicht einfach mal anfangen.“

Das verlangt auch keiner! Natürlich brauchen wir einen Startpunkt. Auch einen Endpunkt können wir definieren und wir werden zu dem Zeitpunkt ein lauffähiges Produkt liefern. Nur was in dem Produkt enthalten ist, darüber können wir im Augenblick noch wenig sagen.

Aber seid mal ehrlich: Wie viele Projekte wurden in der Vergangenheit zum definierten Endpunkt fertig und hatten alle im Projektantrag genannten Eigenschaften? Ich habe einige Zahlen darüber gelesen und stelle deshalb einfach mal 20% in den Raum. Höchstens.

Eigentlich muss sich nicht viel ändern. Definiert Startpunkt, Endpunkt sowie die Anforderungen wie gewohnt. Anschliessend legt ihr für die Anforderungen eine Rangliste fest. Die erste Anforderung werden wir zuerst umsetzten und können diese releasen, weit vor dem Projektende, wenn gewünscht. Anschliessend gehen wir in der Rangfolge weiter, bis das definierte Enddatum erreicht ist.

Möglicherweise werden wir nicht alle Anforderungen umsetzen, aber das dürften auch die Punkte sein, deren Einfluss klein ist (Schliesslich gab es einen Grund, weshalb diese in der Rangfolge später kommen).

„Ohne genauen Projektablauf weiss ich ja nicht, was das kostet. Ich muss aber Budget beantragen!“

Wenn das Team noch keine agilen Softwareprojekte gemacht hat, ist das in der Tat ein Problem. Nimm in so einem Fall die Tage zwischen Startdatum und Enddatum, rechne diese mal die Anzahl Entwickler mal deren Durchschnittslohn und du hast die maximalen Kosten. Stark vereinfacht natürlich.

Wenn es schon agile Projekte mit dem Entwicklerteam gab, lass dir die Velocity dieses Teams geben (Also die Anzahl an Storypoints, die es pro Sprint fertigstellen kann). Anschliessend erfasst du Storys zu allen deinen gewünschten Anforderungen, das Team schätzt deren Komplexität und voila, du hast die benötigte Anzahl Sprints. Das noch mal Entwickler und mal Durchschnittslohn, und du hast den ungefähren Betrag.

Am besten verteilst du aber die Kosten erst nach Projektabschluss und man sieht das Entwicklungsteam als Ressource, die Projekte nach Wichtigkeit verarbeitet.

Abschluss

So, dass waren erst zwei, aber der Artikel wird jetzt schon etwas lange. Ich werde nächstens weitere Vorschläge posten, wenn Fragen an mich gestellt werden oder mir selber was in den Sinn kommt.

Manchmal stösst man auf Dinge, bei denen man sich fragt „Sag mal, stört das nicht viel mehr Leute? Warum ändert man es nicht!“. Normalerweise bringt ein genaueres Nachbohren die Antwort zutage, warum es eben nicht so einfach ist. Doch wenn auch das keinen wirklichen Grund zu Tage bringt, dann muss man wohl darum herumarbeiten.

Heutiges Thema ist die Anpassung der Filesystemgrösse für die virtuelle Harddisk von virtuelle Maschinen unter Virtualbox. Wir wollen die Grösse anpassen, weil wir bei der Anlage der virtuellen Maschine mal wieder zu optimistisch gerechnet haben :).

Grundlagen

Einige wir uns erst einmal auf einige Begriffe:
Name der virtuellen Maschine: ubuntuserver
Name inkl. Pfad zum alten VMDK: /srv/VM/ubuntuserver.vmdk
Name inkl. Pfad zum alten VDI: /srv/VM/ubuntuserver.vdi
Gewünschte neue Grösse des Filesystems: 16384 (~16GB)

Problemstellung

Man sollte glauben, so ein einfacher Task sei mit einem Klick in der GUI erledigt. Doch weit gefehlt. Die Anleitung verweist uns auf den Consolenbefehl VBoxManage modifyhd. Das ist nun zwar etwas blöd, aber wir sind ja Experten – ein wenig auf der Console herumschrauben bereitet uns kein Problem.

Also mutig ein VBoxManage modifyhd ubuntuserver.vmdk –resize 16384 eingegeben und…

0%...
Progress state: VBOX_E_NOT_SUPPORTED
VBoxManage: error: Resize medium operation for this format is not implemented yet!

Ok das VMDK-Format wird nicht unterstützt. Man hätte jetzt noch schreiben können, welches Format den unterstützt wird. Aber na gut, eine kurze Suche ergibt: Virtuelle HDDs im VMDK-Format können nicht vergrössert werden (Genauso wenig wie HDDs mit aktiven Snapshots, aber das könnten wir ja beheben, indem wir die Snapshots verwerfen).

Lösungsansatz

Schritt 1

Wir klonen die HDD und erstellen dabei ein VDI anstatt ein VMDK

VBoxManage clonehd ubuntuserver.vmdk ubuntuserver.vdi --format VDI --variant Standard
Schritt 2

Das neue VDI lässt sich jetzt auch anstandslos vergrössern

VBoxManage modifyhd ubuntuserver.vdi --resize 16384

Soweit so gut, aber jetzt müssen wir ja auch noch das VMDK gegen das VDI austauschen. Man könnte jetzt die Idee kommen, einfach kurz in die VirtualBox.xml-Konfigurationsdatei zu gehen. Was soll schon schief gehen, wir ändern ja nur VMDK in VDI ab oder? Falsch gedacht: Zum einen hat jede virtuelle HDD eine eigene UUID (85602eb5-b05b-26b9-b835-f87a2710d7da beispielsweise) und zum Anderen legt Virtualbox Kopien seiner Konfigurationsdateien an und überschreibt solche Änderungen gleich wieder.

Vielleicht mache ich mal einen Artikel darüber, wie man Virtualbox doch Änderungen unterschieben kann… aber das ist eine Geschichte für ein anderes Mal, heute bleiben wir brav :).

Schritt 3

Das neue VDI als primäres Laufwerk unserer virtuellen Maschine hinzufügen

VBoxManage storageattach ubuntuserver --storagectl "SATA Controller" --device 0 --port 0 --type hdd --medium /srv/VM/ubuntuserver.vdi

Öffnet man jetzt die Virtualbox GUI, sollte die neue HDD dort eingebunden sein und die virtuelle Maschine sollte sich starten lassen. Leider scheint aber das virtuelle Linus so garnichts von dieser Vergrösserung mitbekommen zu haben. Als Nächstes müssen wir also Partition und darauf liegendes Dateisystem vergrössern.

Schritt 4

Partition vergrössern und Dateisystem vergrössern (Normales Vorgehen)

Ubuntu-LiveCD bei der virtuellen Maschine einfügen, von CD starten, LiveSystem aufrufen und mittels gparted bzw. resize2fs die Partition vergrössern.

Aber das ist etwas langweilig oder? Viel cooler wäre es doch, wir würden die Partition im laufenden System vergrössern? 🙂

Partition vergrössern und Dateisystem vergrössern (Verrücktes Vorgehen)

Erstmal: Credits an Sergey, der diesen Vorschlag auf askubuntu gepostet hat: Sergeys Antwort auf Askubuntu

Und hier kommen die Schritte:

Hinweis: Prüft welche Platte ihr im virtuellen System vergrössen müsst, ich gehe hier von /dev/sda1 aus.

Schritt 1

Virtuelle HDD kopieren, damit man sie wiederherstellen kann, sollte was schief gehen (Macht das, eure Katze wird bestimmt genau dann übers Kabel stolpern).

Schritt 2

Virtuelles System booten und als root anmelden (Oder sich mit sudo Rootrechte verschaffen – ihr wisst was ich meine).

Schritt 3

Dann fdisk starten mittels

fdisk /dev/sda
Schritt 4

So, p zeigt uns die Liste der Partition an und jetzt notieren wir uns (Wichtig!) die start-cylinder von /dev/sda1 und ob das Bootflag (*) gesetzt ist.
Gerät        Boot    Start      Ende             Sektoren      Größe      Id      Typ
/dev/sda1  *            2048      117229567     117227520       55.9G         83      Linux

Schritt 5

Jetzt löschen wir mit d die Partition /dev/sda1. Ich weiss, dass ist ein komisches Gefühl, aber traut euch (gesichert ist ja, nicht wahr?).

Schritt 6

Mittels n erstellen wir eine neue Partition. Hierbei setzen wir die start-cylinder wieder genau gleich wie bei der gelöschten Partition (Also 2048 in unserem Beispiel). Als end-cylinder belassen wir es beim Default, dann wird nämlich der ganze verfügbare Platz avisiert.

Schritt 7

Falls die Platte ein Bootflag hatte (Sternchen), setzen wir das mit a auch wieder.

Schritt 8

Wir kontrollieren nochmals die Werte, welche wir eingegeben haben. Mittels w schreiben wir die Änderungen dann effektiv auf die Disk.

Schritt 9

Virtuelle Maschine neustarten und sich auf das super Gefühl freuen, wenn sie wieder hochkommt :).

Schritt 10

Nun haben wir die Partition vergrössert, aber das Dateisystem innerhalb der Partition ist immernoch dasselbe – das müssen wir als nächstes mit diesem kleinen Befehl korrigieren:

resize2fs /dev/sda1

So, das hat mich jetzt 2h gekostet, nur weil ich nicht genau gelesen habe, was der Support schreibt:

Die Mailadresse des Kunden ist markus.van.holter.@veryimportant.com und das System kann einfach keine Mail dorthin senden!

Tatsächlich verweigert das System das Senden an diese Mailadresse. Bloss warum?

Manche Dinge erscheinen auf den ersten Blick simpel. Nehmen wir einmal eine Mailadresse, ihr wisst schon, dieses Relikt aus der Zeit vor WhatsUp :). Wie prüfe ich, ob mein Benutzer auch wirklich eine Mailadresse eingegeben hat?

Der simple Weg

Nichts leichter als das oder? Ich meine wir alle wissen, wie eine Mailadresse aussieht!

thomas.vonwinkel@mail.de

Also erst irgendwas, dann ein @, gefolgt von irgendwas, einem Punkt und am Schluss 1-3 Buchstaben oder so richtig? Interessanterweise gilt: In den allermeisten Fällen ja! Und in der Tat gibt es bestimmt viele Webseiten und Programme, die eine Mailadresse etwa so prüfen:

code   
  1. DATA: lv_email TYPE STRING,
  2. lv_local TYPE STRING,
  3. lv_host TYPE STRING,
  4. lv_tld TYPE STRING.
  5.  
  6. lv_email = 'thomas.zumwinkel@mail.de'.
  7.  
  8. "Zerlege die Mailadresse in ihre Bestandteile
  9. FIND REGEX '(.*)@(.*)\.(.*)' IN lv_email SUBMATCHES lv_local lv_host lv_tld.
  10.  
  11. "Prüfe ob eines der Felder leer ist
  12. IF lv_local IS INITIAL OR lv_host IS INITIAL OR lv_tld IS INITIAL.
  13. WRITE: / 'Mailadresse ungültig!'.
  14. WRITE: / 'Mailadresse ok'.

Wir zerlegen mittels eines regulären Ausdrucks (Regex -> Ein sehr mächtiges Werkzeug. Wer das noch nicht kennt, sollte sich unbedingt einlesen!) die Mailadresse in ihre drei Bestandteile und prüfen dann, ob jeder Teil vorhanden ist. Das ist ein guter Anfang und ich schätze damit erwischen wir 95% aller Mailadressen.

ABER, eben nur 95%. Beispielsweise sind das hier auch gültige Mailadressen nach RFC 5321:

  • „meinname“@domain.ch
    • Wobei die Anführungszeichen nicht zur Mailadresse gehören
  • blue1922@[IPv6:2001:e18::1]
    • Klar, ich geb gerne die IP meiner Domain an
  • hans.xn--mller-kva@bla.com
    • Eigentlich hans.müller@bla.com. Ja Umlaute 🙂
  • „hans ich liebe leerzeichen“@bla.com
    • Jep Leerzeichen (und noch ne Menge sonstiger Müll -.-)
  • info(Hier bekommen Sie Infos)@bla.com
    • Das Zeug zwischen den Klammern ist ein Kommentar

OK, also Umlaute vorne und hinten (Danke für Unicode-Domains an der Stelle… nicht). Dazu Kommentare, IPs statt Domains sowie Anführungszeichen. Ausserdem gibt es noch einige komische Zusatzregeln (Und eine davon betrifft unsere schöne Kundenadresse von oben):

  • Kein Punkt am Ende oder am Anfang des Lokalteil
    • hans.meier.@bla.com ist also eine inkorrekte Adresse
  • Die TLD muss mindestens zweistellig sein
    • Also example.ch ist ok, example.c nicht

Fazit

Es gibt in praktisch jeder Programmiersprache Bibliotheken zur Prüfung von Mailadressen. Nutzt die. Punkt. Wenn ihr wirklich eine Prüfung machen müsst und keine Bibliothek dafür einbinden könnt, dann benutzt diesen Regex (Offiziell aus RFC 5322):

(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_
`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\
x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0
-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0
-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3
}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0
-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\
[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])

Mit dem funktionieren bestimmt 99%. Ja du hast richtig gelesen: Der Regex im offiziellen RFC zu Mailadressen validiert nicht zu 100% korrekt! Also bleibt schlussendlich nur noch eine Möglichkeit: Sendet dem Kunden eine Aktivierungsmail um zu sehen, ob seine Mailadresse korrekt ist (Ist sowieso usus das zu tun…)

Wie merkt ein Programmierer, dass er zuviel im Code rumwuselt? Daran das er mitten in der Nacht aufsteht um einen Performance-Bug zu korrigieren :). Und dabei war der Bug noch nicht einmal in meinem Code. Aber ich sollte vielleicht von vorne beginnen:

Die betroffene Funktion dient dazu, die Erscheinungsdaten einer Publikation auszulesen, damit das Frontend dem Kunden eine Auswahl an möglichen Bestelldaten anbieten kann. Bei einer Sonntagszeitung würde man also eine Liste von Sonntagen zurück bekommen. Nehmen wir das als Beispiel und sagen wir weiterhin, wir wollen ab dem 08.07.2016 starten und 20 Tage in die Zukunft schauen. Das System würde dann diese Liste zurückliefern:

  • 10.07.2016
  • 17.07.2016
  • 24.07.2016

Und hier kommt der Code dazu:

code   
"Erscheinungsdaten für Publikation und Zeitspanne ausgeben
lr_publication->get_publication_calendar_range(
EXPORTING
    ip_start_date           = lv_startdate
    ip_end_date             = lv_enddate
    ip_drerz                = lv_drerz
    ip_pva                  = lv_pva
RECEIVING
    rt_publication_dates    = lt_publication_dates ).
 
"Sortiere die Liste aufsteigend
SORT lt_publication_dates ASCENDING.
 
"Weise die fertige Liste unserer Exportstruktur zu
export_json-possible_dates = lt_publication_dates.

Soweit so einfach oder? Die Methode get_publication_calendar_range ruft die SAP Funktion ISP_SUBSCRIPTED_VAS_GET auf und diese macht nicht viel mehr, als im Tabellenview JDVVA diese Daten auszulesen. Der Zugriff ist dank Datenbankindex simpel und sehr schnell ausgeführt.

Jetzt kam aber als neue Anforderung hinzu, dass auch der Annahmeschluss beachtet werden sollte.  Und wir Entwickler wissen: Neue Anforderungen machen es nie einfacher…

Annahme…was?

Ich muss vielleicht für alle nicht SAP M/SD-Entwickler erklären, was ein Annahmeschluss im aktuellen Kontext ist: Die Zeitschrift oder Zeitung muss von irgendjemandem in die Briefkästen verteilt werden. Davor müssen vom Druckzentrum aus die Zeitungen an Lagerstellen verteilt werden (von wo die Verträger die Zeitungen dann abholen) und vorher sollte die Zeitung ja auch noch gedruckt werden :). Das alles braucht Zeit, weshalb es einen Zeitpunkt gibt (normalerweise ist das irgendwann nachmittags) an dem das „Bestellfenster“ für die morgige Ausgabe geschlossen wird. Nach diesem Zeitpunkt kann der Kunde zwar bestellen, aber eben nicht mehr die Ausgabe von morgen.

Natürlich gilt das Ganze in erster Linie für physische Produkte. Aber von denen hat ja jedes Medienhaus, trotz Digitalstrategie, noch so einige im Angebot :). So und damit zurück zur neuen Anforderung.

Die neue Anforderung

Kunden sollen kein Datum auswählen können, für das der Annahmeschluss schon gesetzt ist

Tja, wie macht man das in SAP? Mit einem Funktionsbaustein natürlich! Könnt ihr euch merken, dass ist die Standardantwort auf beinahe jedes Problem in SAP. Meistens ist dann das Finden des korrekten Bausteins die Herausforderung :). Aber ich schweife ab, hier ist der korrekte Funktionsbaustein um den Annahmeschluss eines M/SD-Produktes auszulesen: ISP_PACKAGING_START_DATE_GET.

Also einfach oder? Wir erweitern einfach unseren Aufruf etwas:

code   
"Erscheinungsdaten für Publikation und Zeitspanne ausgeben
lr_publication->get_publication_calendar_range(
EXPORTING
    ip_start_date           = lv_startdate
    ip_end_date             = lv_enddate
    ip_drerz                = lv_drerz
    ip_pva                  = lv_pva
RECEIVING
    rt_publication_dates    = lt_publication_dates ).
 
"Iteriere und prüfe, ob der Annahmeschluss gesetzt ist
LOOP AT lt_publication_dates INTO ls_publication_date.
 
    lr_publication->check_deadline
    EXPORTING
        ip_date                 = ls_publication_date
    IMPORTING
        has_deadline            = lx_deadline.
 
    IF lx_deadline IS INITIAL. "'false' für alle nicht SAPler :)
        APPEND ls_publication_date TO export_json-possible_dates.
    ENDIF.
 
ENDLOOP.
 
"Sortiere die Liste aufsteigend
SORT export_json-possible_dates ASCENDING.

Funktioniert doch?

„Das sieht nach funktionierendem Code aus“, werden jetzt einige SAPler sagen. Und so ist es auch, der Code funktioniert, er ist nur ein extremer Bremsklotz. Der Grund dafür ist zum einen der Funktionsbaustein ISP_PACKAGING_START_DATE_GET, der einige sehr „teure“ Datenbankabfragen vornimmt. Zum anderen prüfen wir hier viel zu viel!

Und genau der Gedanke war es, der mir nachts um zwei plötzlich in den Kopf schoss: Der Annahmeschluss definiert, dass die Ausgabe vom 16.02.2016 und alle noch älteren nicht mehr bestellt werden können. Er definiert aber auch, dass alle Daten grösser dem Annahmeschluss nicht mehr geprüft werden müssen, weil es nur einen Annahmeschluss zur gegeben Zeit geben kann (Er wird quasi jeden Tag nach vorne verschoben).

Die Lösung

Mit dieser Erkenntnis im Hinterkopf habe ich die Funktion umgeschrieben:

code   
"Erscheinungsdaten für Publikation und Zeitspanne ausgeben
lr_publication->get_publication_calendar_range(
EXPORTING
    ip_start_date             = lv_startdate
    ip_end_date               = lv_enddate
    ip_drerz                  = lv_drerz
    ip_pva                    = lv_pva
RECEIVING
    rt_publication_dates      = lt_publication_dates ).
 
"Sortiere die Liste aufsteigend
SORT lt_publication_dates ASCENDING.
 
"Fülle die Exportstruktur mit allen Publikationsdaten
export_json-possible_dates = lt_publication_dates
 
"Iteriere und prüfe, ob der Annahmeschluss gesetzt ist
LOOP AT lt_publication_dates INTO ls_publication_date.
 
    lr_publication->check_deadline
    EXPORTING
        ip_date                   = ls_publication_date
    IMPORTING
        has_deadline              = lx_deadline.
 
    IF lx_deadline IS INITIAL. "'false' für alle nicht SAPler :)
        DELETE ls_publication_date FROM export_json-possible_dates.
    ELSE.
        EXIT. "Beendet die Schleife sofort
    ENDIF.
 
ENDLOOP.

Ich fülle die Exportstruktur zu Beginn mit allen Publikationsdaten und lösche jeweils das aktuelle Datum heraus, wenn der Annahmeschluss für dieses Datum gesetzt ist. Da die Liste der Publikationsdaten aufsteigend sortiert ist, kann ich aufhören zu prüfen, sobald das erste Datum keinen Annahmeschluss mehr gesetzt hat.

Ergebnis

Ich habe bereits in der Nacht einige Messungen durchgeführt und bin dabei auf eine Geschwindigkeitssteigerung von 65% gekommen. Das Frontend reagiert entsprechend flotter und ich bin zuversichtlich, dass wir damit die durchschnittliche Antwortzeit noch weiter drücken können :). Werde dazu nochmals posten, wenn wir das auf Produktion haben und ich die echten Logdaten habe.

Fazit

Wenn du in eine bestehende Funktion neuen Code einfügst, der potenziell teure Datenbankoperationen durchführt, schau dir den umschliessenden Code an. Das Problem hier war grundsätzlich weder dir erste Umsetzung, noch die neue Funktion, sondern die unglückliche Verbindung von Beiden.


Update

Ich bin noch Angaben zur Perfomance auf dem produktiven System schuldig: Es gab eine Steigerung von etwas über 40% bei Aufrufen dieses Bausteins. Der Grund für den tieferen Wert ist mir nicht ganz klar. Ich vermute, dass durch die vielen produktiven Aufrufe dieser Funktion Teile der Daten vom SAP Applikationsserver gecached werden, was die Messergebnisse verfälscht. Dabei wäre die Messung vor dem Update schon besser gewesen, was die Performancesteigerung vermindert.

Allerdings war es gut genug, so dass wir es in der Form belassen haben. 😉