Project

General

Profile

Performance

Added by Werner Hahn over 4 years ago

Ich habe jetzt eine Testumgebung mit Kivitendo 3.2 Git-Revision: f6121bf, 28.02.2015 17:13:53 +0100
auf einer Proxmox VM mit debian 7.8 8GB RAM 2CPUs
ca. 80.000 Kundendatensätzen (17 benutzerdefinierte Variablen)
ca. 850 Artikelstammdaten (12 benutzdefinierte Variablen) in 151 Artikelgruppen
Wenn ich jetzt eine Rechnung schreiben will dauert es bis zu 12sec. bis die Maske sich öffnet. top zeigt das postgres 75% cpu in anspruch nimmt
Beim eingeben der Position dauert es wieder bis zu 6 sec bis das From sich neu aufgebaut hat. postgresb wieder hoch in der zeit.
Das drucken bzw. drucken und buchen dauert wieder 6-7 sec.
Allgemein dauert vieles Lange was mit den Artikeln zu tun hat.
Bei den Kunden (Suche, öffnen, speichern) geht es ganz gut


Replies (19)

RE: Performance - Added by Sven Schöling over 4 years ago

Mach bitte mal in der Config unter debug folgendes an:

[debug]
global_level = REQUEST_TIMER TRACE

und gib mir für die jeweiligen Requests die lange dauern die Ausgabe in der Logdatei, dann schau ich mal woran das liegt. Leider sind immernoch viele Ecken im Programm nicht darauf ausgelegt, dass man sehr viele Kundendaten hat. Ein paar zigtausend Belege oder Waren sind ok, zuviele Kunden dagegen schlecht getestet, weil kaum jemand solche Mengen hat.

RE: Performance - Added by Werner Hahn over 4 years ago

Hallo
ich hab das debuglog als Datei angehängt.
hab zwei Positionen eingegeben.
Werner

Rechnung_erfassen.txt (121 KB) Rechnung_erfassen.txt Debuglog von Rechnung erfassen

RE: Performance - Added by G. Richardson over 4 years ago

Ich habe nur ganz kurz draufgeschaut, es scheint an _get_customers in SL/Form.pm zu liegen, da werden einmal alle Kunden aus der Datenbank ausgelesen, und es gibt dort auch noch eine Vertreter-Klausel im Query, wobei ich nicht weiß, ob das in dem Fall aktiv ist. Das lässt sich jedenfalls garantiert optimieren.

RE: Performance - Added by Sven Schöling over 4 years ago

Jo, das hab ich erwartet.

Falls Du das machen willst Geoffrey, ich hab das vor Jahren schonmal in der Hand gehabt. Das Problem war da, dass die multibox für Kunden erst beim template rendern entscheidet ob sie Dropdown oder Textinput anzeigt. Deshalb ist ein Limit zu dem Zeitpunkt noch nicht bekannt. Ich würde da wie folgt rangehen:

1. Rausfinden wo im Programm überall get_lists(customers => ...) benutzt wird, was ja untendrunter _get_customer aufruft. Vor allem welche actions das triggern, ich meine da gab es irgendeinen wirren case mit update().
2. Rausfinden wie man die so bauen kann, dass sie schon da wissen wieviele sie maximal brauchen (alle ist niemals sinnvoll)
3. Das dann sinnvoll beschränken.

Auch: 80000 Kundendaten? Wtf?

RE: Performance - Added by G. Richardson over 4 years ago

Die andere Alternative wäre auf Customerpicker umzusteigen, was langfristig eh das Ziel wäre, dann wäre diese Arbeit auch überflüssig. Aber ich vermute das in allen Masken anzupacken wäre derzeit noch mehr Arbeit als das get_lists zu verbessern.

RE: Performance - Added by Sven Schöling over 4 years ago

Customerpicker wäre toll, ist aber nicht einfach zu machen weil der update Mechanismus auf check_name basiert. Und der check_name Code ist genauso gut anzuschauen wie das Innere der Bundeslade.

RE: Performance - Added by Jan Büren over 4 years ago

Gibt es eine Möglichkeit die Performanzverbesserung schon vorher zu messen?

Z.B.:
Schritt 1: Einen Kunden fest auswählen.
Schritt 2: Position hinzufügen
Schritt 3: sub check_name übergehen (direkt ein return 1 am Anfang setzen)

Mich würde in diesem Schritt einfach die maximal erreichbare Einsparung interessieren.

Hilft es ansonsten die multibox für diesen Fall hart mit dem Wert "Freitextfeld" zu verbinden? Sodass diese Abfrage überflüssig wäre?

Andere Idee, rein zur Analyse: Was passiert wenn man in is.pl in sub form_header die customers erst gar nicht abholt. Also so:

$form->get_lists("taxzones" => ($form->{id} ? "ALL_TAXZONES" : "ALL_ACTIVE_TAXZONES"),
"currencies" => "ALL_CURRENCIES",
# "customers" => "ALL_CUSTOMERS",
"departments" => "all_departments",
"price_factors" => "ALL_PRICE_FACTORS");

Hier wäre es auch schon intelligenter die Liste nicht bei jedem Aufruf der Form neu zu generieren (jaja, altes konzeptionelles Problem).

RE: Performance - Added by Sven Schöling over 4 years ago

Jan: Was dann passiert ist, dass es schnell und kaputt ist. Die falsche Antwort schnell zu kriegen ist aber nicht sonderlich schwer.

Das korrekt und schnell zu machen, ist die Herausforderung die ich oben beschrieben habe.

RE: Performance - Added by Jan Büren over 4 years ago

Ja, das ist richtig. Mir geht es nur um eine Einschätzung inwiefern man hier näherungsweise "rankommt".

RE: Performance - Added by Sven Schöling over 4 years ago

Nein. Nicht näherungsweise.

RE: Performance - Added by Jan Büren over 4 years ago

Das war gestern eher so eine Ansammlung losen Ideen.

Hier eine andere Variante (check_name nur am Anfang der Positionseingabe ausführen):

sub update {
$main::lxdebug->enter_sub();

my $form     = $main::form;
my %myconfig = %main::myconfig;
$main::auth->assert('invoice_edit');
my ($recursive_call) = @_;
$form->{print_and_post} = 0         if $form->{second_run};
my $taxincluded = $form->{taxincluded} ? "checked" : '';
$form->{update} = 1;

+ &check_name("customer") if $form->{rowcount} > 2;.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Prinzipiell ist das Verhalten schlecht und da muss was getan werden.

Ich will jetzt keinen CustomerPicker implementieren, wenn ich nicht vorher weiß, ob nicht noch weitere Fallen lauern.

RE: Performance - Added by Sven Schöling over 4 years ago

Du brauchst keinen CustomerPicker implementieren, da gibt es schon einen.

Was Du machen musst, ist schauen was check_name alles tut (Spoiler: Es frisst Babys), und musst zusehen, dass die Version mit dem CustomerPicker all den Cruft der nötig ist damit check_name funktioniert entweder auch tut (pseudo name--id Strings bauen die dann wieder bei Kunden mit '-' im Namen kaputtgehen, old_customer_id setzen etc), oder musst check_name rausschmeissen und zusehen, dass alle Funktionalität die check_name tut anderweitig getan wird. Ich habe nicht mehr genau im Kopf was das alles tut (ich hab es in der Datei aber nach einer schmerzlichen Debuggingsession mal dokumentiert), aber da waren so lustige Sachen dabei wie:

- was tut das Programm wenn noch kein Kunde angelegt ist, und jemand auf erneuern drückt
- was passiert, wenn der eingegebene Name nicht existiert
- Wie erkennt das Programm, dass der Kunde geändert wurde, weil dann potentiell die Steuerzonen, Ansprechpartner und Kundenrabatte neu gesetzt werden müssen.

RE: Performance - Added by Jan Büren over 4 years ago

Danke Sven. Im Perldoc ist das sehr gut beschrieben:

 Don't use anyting in this file without extreme care, and even then be prepared for massive headaches.

Aber andersrum gedacht, check_name nur bei der ersten Position auszuführen und danach das Feld 'Kunde' nicht mehr änderbar machen, sieht, insbesondere nach lesen Deiner Doku als machbar aus.

RE: Performance - Added by G. Richardson over 4 years ago

Wenn man das angeht sollte das direkt sauber gemacht werden, und dann am Besten auch mit dem Customer/Vendorpicker, der irgendwann genau dafür sowieso verwendet werden soll.

Das betrifft ja nicht nur Verkaufsrechnungen sondern das gleiche Feld gibt es auch bei Einkaufsrechnungen, Debitoren- und Kreditorenbuchungen, im Filter für deren Berichte, bei Zahlungseingang und -ausgang und bestimmt noch ein paar mehr.

Den Kunden sollte man jederzeit ändern können, ansonsten muß man da bei für "als neu verwenden" wieder Sonderregeln einbauen.
Ob sich der Kunde ändert läßt sich doch über einen entsprechenden Trigger, den man ins Template einbaut, feststellen.

Da warten in dem Bereich sicherlich viele Fallstricke, aber bitte keine Pfuschlösung, zumindest nicht für den Standard.

RE: Performance - Added by Werner Hahn over 4 years ago

Hallo
voerst hab ich eine Lösung gefunden, die auch so denke ich halbsweg sauber ist. Trotzdem diese ganze Verkaufs-, Einkaufs- und Fibukrams ist ein ganz unschönes kuddelmuddel.
Jan sein Vorschlag mit den &check_name hatte nichts gebracht, dies überprüft nur ob sich der Name überhaupt geändert hat.
Das Problem liegt an der Auswahlliste des Kundenfeld und der sub form_header (is.pl ca zeile 300) hier wird u.a. _get_customer (alle Kunden) der Form.pm aufgerufen.
Hab zuerst die Beschränkung von Auswahllisten auf 0 gesetzt. (Noch keine Besserung)
und dann die entsprechende Zeile in der is.pl form_header auskommentiert, sodass diese Liste nicht mehr generiert wird. Kundenwechsel funktioniert weiterhin.
Aufrufen einer neuen Rechnung bzw. neue positionzeile 1-2 sec und je mehr Positionen es werden desto länger ------ debuglog: time: 0.824895 .
Ist zwar immer noch relativ lange, aber um hier erstmal ne Testumgebung mit reelen Daten aufzubauen ausreichend.

RE: Performance - Added by Werner Hahn over 4 years ago

Das wiederholt sich natürlich auch teilweise vorher
Angebot ok
Auftrag ok
Lieferschein nicht ok

RE: Performance - Added by Werner Hahn over 4 years ago

Habs jetzt nochmal geändert und so behandelt wie in den Angeboten und Aufträgen
@my $vc = $form->{vc} eq "customer" ? "customers" : "vendors";

$form->get_lists("taxzones"      => ($form->{id} ? "ALL_TAXZONES" : "ALL_ACTIVE_TAXZONES"),
"payments" => "ALL_PAYMENTS",
"currencies" => "ALL_CURRENCIES",
"departments" => "ALL_DEPARTMENTS",
$vc => { key => "ALL_" . uc($vc),
limit => $myconfig{vclimit} + 1 },

"price_factors" => "ALL_PRICE_FACTORS");@

RE: Performance - Added by Jan Büren about 4 years ago

Der aktuelle Stand unser Diskussion ist, dass wir die Auftragsmaske komplett neu implementieren und diese mit den minimalsten Features, die notwendig sind, parallel schalten.
Das wäre zusätzlich ein Beginn für: http://redmine.kivitendo-premium.de/issues/33.

Im weiteren Verlauf des Workflows, würden wir die Auswahl für Kunden erstmal sperren (Kunden wechsel nicht möglich), vielleicht wäre dies auch konfigurierbar für den Standard; vielleicht aber erstmal als Kundenprojekterweiterung.

RE: Performance - Added by G. Richardson about 4 years ago

In der neuen Maske würde man doch direkt mit dem Customerpicker statt dem alten Dropdown arbeiten, von daher wäre das Sperren gar nicht nötig.

    (1-19/19)