Autor Thema: JavaAgent bringt eine Instanz vom AgentManager zum Absturz, Out of Memory  (Gelesen 920 mal)

Offline mapbug

  • Frischling
  • *
  • Beiträge: 7
Guten Tag,

Ich habe ein Problem mit meinem JavaAgent auf dem Domino.
Zuerst versorge ich euch mit ein paar Infos:

- ursprüngliches Problem warum der Agent entstanden ist:
Ich habe eine XPage erstellt mit klassischen Portaldaten pro Benutzer und Datum (Aufträge, Angebote, Verkaufsaktivitäten, Umsätze, ....)
Diese XPage läuft nicht nur im Browser sondern auch im Notes Client selbst. Dazu waren wegen des veralteten Java Script Standards
viele manuelle Anpassungen notwendig. Dort verwende ich auch JavaScript Frameworks wie charts.js und Jquery DataTables.
Die Daten holte ich via Domino RESTServices (JavaScript).

Ausszug XPage
https://ibb.co/VVhX79J


Das Problem dabei war, dass bei manchen Benutzern, die eben mehr Daten durch ihre Berechtigung sehen durften, diese Seite länger als eine Minute brauchte dass alle Daten
geladen waren. Dieses Problem habe ich monatelang in Verbindung mit der HCL und kompetenten Partnerfirmen analysiert.
Ich habe viele, viele Ideen und Einstellungen probiert und wir sind zum Schluss gekommen, dass es wohl besser wäre diese Daten nächtlich zu berechnen und abzuspeichern
da die Performance (vorallem das Durchschleifen der Dokumente) echt mies ist.
In der XPage hole ich dann je nach Auswahl des Benutzers und Datums das korrekte Dokument und die Seite ist in weniger als 4 Sekunden komplett geladen.
(Wenn jemand mehr dazu wissen will -> PN)


- Was macht der Agent?
Der Agent läuft viele Ansichten von mehreren Domino Dbs durch und holt sich daraus Informationen.
Diese Informationen werden als JSON in einem Dokument abgespeichert.

Codeauszug
   
Code
// Dokument erstellen
	portalxsp = dbPortal.createDocument();
	....
	
	JSONObject angebote = getAngebote(benutzer, datum, waehrung);
	
	// JSON Daten in Richtextfeld speichern
	if (portalxsp.hasItem("angebote")) {
		portalxsp.removeItem("angebote");
	}
	rtItem = (RichTextItem) portalxsp.createRichTextItem("angebote");
	rtItem.appendText(angebote.toString());
	rtItem.update();
	rtItem.recycle();
	
	... (weitere Methoden) ...
	
	portalxsp.save(true, false);
	portalxsp.recycle();
	
	

private JSONObject getAngebote(Document docbenutzer, Calendar datum, String waehrung) {
		List<JSONObject> angebote = new ArrayList<JSONObject>();
		try {
			Date date = datum.getTime();

			// mit Berechtigungen von ausgewählten Benutzer filtern
			StringBuilder suchString = new StringBuilder();
			suchString.append(berechtigungsMap.get(docbenutzer.getItemValueString("Benutzer")));

			String kd = "KD";
			suchString.append(" AND Field Angebotsdatum = " + dfddMyyyy.format(date));
			// keine Kundendienstaufträge anzeigen
			suchString.append(" AND NOT (Field ProduktGruppe = \"" + kd + "\")");

			viewAngeboteXSP.FTSearch(suchString.toString());

			ViewEntryCollection col = viewAngeboteXSP.getAllEntries();
			ViewEntry ve = col.getFirstEntry();

			while (ve != null) {
				JSONObject angebot = new JSONObject();
				Vector colVals = ve.getColumnValues();

				angebot.put("Kundenname", colVals.get(11).toString().trim());
				angebot.put("Angebot", colVals.get(3));
				angebot.put("DoklinkAngebot", colVals.get(8));
				angebot.put("Produktgruppe", colVals.get(10).toString().trim());
				BigDecimal betrag = BigDecimal.ZERO;
				Object betragObj = colVals.get(7);
				if (betragObj instanceof Double) {
					betrag = new BigDecimal((Double) betragObj).setScale(2, BigDecimal.ROUND_HALF_UP);
				} else {
					betrag = BigDecimal.ZERO;
				}
				angebot.put("Wert", umwandelnBetrag("EUR", waehrung, betrag, date));

				// aktuelle Daten zu Array hinzufügen
				angebote.add(angebot);

				// nächsten Eintrag lesen
				ViewEntry tmpve = ve;
				ve = col.getNextEntry();
				tmpve.recycle();
			}
			col.recycle();

			int len = angebote.size();
			if (len > 1) {
				// Sortieren nach KdNr, Angebot
				angebote.sort(new ComparatorAngebot());
			}

			JSONObject jsonResult = new JSONObject();
			jsonResult.put("data", angebote);

			return jsonResult;

		} catch (Exception e) {
			try {
				System.err.println("Fehler in getAngebote bei Benutzer " + docbenutzer.getItemValueString("Benutzer"));
			} catch (NotesException e1) {
				e1.printStackTrace();
			}
			e.printStackTrace();
		}
		return null;
	}

Ich werde hier nicht den kompletten Code posten da das Programm über 4000 Zeilen Code hat und die jeweiligen Methoden immer gleich aufgebaut sind.


- Wie habe ich den JavaAgenten am Domino erstellt?
Neuer Java-Agent. Innerhalb der NotesMain() Methode, meine gewünschte Methode aufgerufen.
Die .jar Dateien binde ich als Archivdateien beim JavaAgenten ein.
Code
public void NotesMain() {
....
      try {
          // Portale Erstellen
          String[] args = null;
          Portal.main(args);
		  
         // Dump Thread Group
          Portal.dumptg(null);
		  
      } catch(Exception e) {
          e.printStackTrace();
       }
   }
Ich habe gelesen dass man die .jar Files besser in ..jvm\lib\ext einbinden soll.
Das habe ich zwecks Sicherheitsbedenken nicht gemacht da diese .jars dort alle Rechte haben.
Laut Bug SPR # BHUY8PRMKK werden die .jar Dateien bei jedem Start des Agenten in den Speicher geladen und bleiben dort.
Als Alternative habe ich eine Lösung von eknori gefunden -> EnableJavaAgentCache=2 in der notes.ini.
Mit diesem Parameter werden die .jars wiederverwendet und nicht immer neu in den Speicher geladen
https://www.eknori.de/2021-03-20/java-agents-with-imported-jar-files/
-> Der Agent wird jede Nacht ausgeführt.



- Probleme?
1) Der Agent kann nach dem Durchlauf ab und zu nicht alle Threads bereinigen und liefert: Java Agent "Error cleaning up agent threads"
Die Threads lasse ich laut https://support.hcltechsw.com/csm?id=kb_article&sysparm_article=KB0032652 ausgeben.

 
Code
 Agent Manager: Agent printing: ThreadGroup UTG: JavaAgent, objid = 1751964594
  Agent Manager: Agent printing:   subgroups  = 0
  Agent Manager: Agent printing:   total threads = 2
  Agent Manager: Agent printing:  Thread Name, ThreadID, ThreadGroup
  Agent Manager: Agent printing: AS400 Read Daemon [system:ATARTW01;job:515458/QUSER/QZDASOINIT], -663952502, UTG: JavaAgent
  Agent Manager: Agent printing: AgentThread: JavaAgent, 1991637412, UTG: JavaAgent
 
Da sehe ich nur einen Java Agenten und einen Verbindungsjob der Daten aus unserer IBM i abgreift.
Ich verstehe nicht warum er diese Threads nicht komplett bereinigen kann nachdem der Agent durchgelaufen ist. 


2) Der Agent bricht ab und zu mit der Fehlermeldung: LSXBE: Out Of Backend Memory ab.
Dies lässt eine Instanz vom Agent Manager abstürzen.
Code
    LSXBE: ************************************
    LSXBE: ****** Out of Backend Memory *******
    LSXBE: ************************************
 Agent Manager: Agent  error: Fehler in getVorschlaegeTelefonateA bei Benutzer CN=XXXXXX XXXXX/O=Artweger/C=AT
 Agent Manager: Agent  error: NotesException: Out of Memory
 Agent Manager: Agent  error:  at lotus.domino.local.DocumentCollection.NgetNextDocument(Native Method)
 Agent Manager: Agent  error:  at lotus.domino.local.DocumentCollection.getNextDocument(Unknown Source)
 Agent Manager: Agent  error:  at artweger.programs.crm.portal.Portal.getVorschlaegeTelefonateA(Portal.java:2789)
 Agent Manager: Agent  error:  at artweger.programs.crm.portal.Portal.erstelleXSPPortalDokument(Portal.java:562)
 Agent Manager: Agent  error:  at artweger.programs.crm.portal.Portal.erstellePortalSeiten(Portal.java:427)
 Agent Manager: Agent  error:  at artweger.programs.crm.portal.Portal.main(Portal.java:83)
 Agent Manager: Agent  error:  at JavaAgent.NotesMain(Unknown Source)
 Agent Manager: Agent  error:  at lotus.domino.AgentBase.runNotes(Unknown Source)
 Agent Manager: Agent  error:  at lotus.domino.NotesThread.run(Unknown Source)

 PANIC: CheckTheProcesses - Prozess C:\Program Files\HCL\Domino\nAMgr.EXE (5744/0x1670) untergeordnetes Element von 1272/0x4F8 wurde aufgrund eines Fehlers beendet.

Dies passiert allerdings nicht jeden Tag.
Auch hierzu habe ich bereits lange das Internet bemüht und das einzige was ich gefunden habe, ist, dass man alle Domino Objekte recyclen soll.
Dies habe ich natürlich gemacht und mehrfach! kontrolliert. (bzw. kontrollieren lassen)
Was mir hier aufällt ist, dass der Fehler immer kommt wenn der Agent gerade Ansichten oder Dokumente durchläuft.
Kann es sein dass er recycelte() Dokumente doch irgendwie im Speicher behält? Oder das recyceln() länger dauert?
Natürlich laufen noch weitere Dinge am Domino, jedoch gab es vor dem Agenten nicht dieses Problem.
Egal welche Methode. Letztes Mal war es eben diese.

Das nsd.log spuckt folgendes aus: 
Code
############################################################
### thread 2/131: [ nserver:  04f8:  13a8] 
############################################################
 [ 1] 0x7FF874F55D14 ntdll.ZwWaitForSingleObject+20 (0,7FF84EE114B6,21E32D2ED14,3ED044EE18F0D)
 [ 2] 0x7FF8715C6D1F KERNELBASE.WaitForSingleObjectEx+143 (0,3FF00000000,0,608)
@[ 3] 0x7FF84EE13E89 nnotes.WaitOnNativeSemaphore+153 (ffffffff,7FF851921C28,0,0)
@[ 4] 0x7FF84EE12345 nnotes.OSWaitEvent+149 (21E30C0858C,3e8,0,3e8)
@[ 5] 0x7FF84F611414 nnotes.MQGetExtended+436 (3C00011648,3CC1DBF5C0,310,0)
@[ 6] 0x7FF84F6111E2 nnotes.MQGet+50 (0,21E2F830000,0,0)
@[ 7] 0x7FF84F5DD66D nnotes.fileWriterT+125 (21E32B30000,0,0,0)
@[ 8] 0x7FF84EDEF688 nnotes.ThreadWrapper+264 (0,0,0,0)
 [ 9] 0x7FF8735B84D4 KERNEL32.BaseThreadInitThunk+20 (0,0,0,0)
 [10] 0x7FF874F01791 ntdll.RtlUserThreadStart+33 (0,0,0,0)
 ....
 ############################################################
### thread 120/131: [ nserver:  04f8:  0f44] FATAL THREAD (Panic)
############################################################
 [ 1] 0x7FF874F55D14 ntdll.ZwWaitForSingleObject+20 (68736100000010,0,0,3CC95F90E0)
 [ 2] 0x7FF8715C6D1F KERNELBASE.WaitForSingleObjectEx+143 (10,FFFFFFFFFFFFFFF,7FF800000000,257c)
@[ 3] 0x7FF84EDDCAEA nnotes.OSRunExternalScript+1546 (5,0,179c,0)
@[ 4] 0x7FF84EDD917C nnotes.FRTerminateWindowsResources+1532 (5,0,0,1)
@[ 5] 0x7FF84EDDAB63 nnotes.OSFaultCleanupExt+1395 (0,5670,0,3CC95FD8A0)
@[ 6] 0x7FF84EDDA5E7 nnotes.OSFaultCleanup+23 (5670,0,0,0)
@[ 7] 0x7FF84EE47856 nnotes.OSNTUnhandledExceptionFilter+390 (3CC95FA820,7FF8503F62D8,3CC95FD8A0,FFFFE1E99563353)
@[ 8] 0x7FF84EDDD77A nnotes.Panic+1066 (1670,1258A4D003604C9,9,4f8)
@[ 9] 0x7FF8691023BD nserverl.CheckTheProcesses+461 (800207ea,3C00000055,1670,2)
@[10] 0x7FF8691047E0 nserverl.ProcessMonitorTask+368 (88,0,7FF8731B0000,0)
@[11] 0x7FF869101A78 nserverl.Scheduler+888 (0,0,d215005c,21E38C58BF0)
@[12] 0x7FF84EDEF688 nnotes.ThreadWrapper+264 (0,0,0,0)
 [13] 0x7FF8735B84D4 KERNEL32.BaseThreadInitThunk+20 (0,0,0,0)
 [14] 0x7FF874F01791 ntdll.RtlUserThreadStart+33 (0,0,0,0)


- Info:
Der Agent läuft ohne Probleme auf Windows PC bzw Windows Server durch.
Von daher habe ich eine funktionierende Alternative. Ich würde dennoch gerne den JavaAgenten am Domino laufen lassen und herausfinden
was diese Fehler verursachen könnte.

HCL Domino Server (64 Bit), Release 11.0.1FP3


Mit freundlichen Grüßen,
Benjamin

Offline jBubbleBoy

  • Gold Platin u.s.w. member:)
  • *****
  • Beiträge: 1.290
  • Geschlecht: Männlich
Schon mal daran gedacht, den Java-Agenten in einen Java-Server-Task umzuwandeln und so auf dem Domino laufen zu lassen?
Gruss Erik :: Freelancer :: KI-Dev, Notes, Java, Web, VBA und DomNav 2.5 / NSE 0.16 / OLI 2.0

--
Nur ein toter Bug, ist ein guter Bug!

Offline mapbug

  • Frischling
  • *
  • Beiträge: 7
Nein, habe ich nicht.

Muss auch zugeben ich weiß auch gar nicht wie das funktioniert.

Offline jBubbleBoy

  • Gold Platin u.s.w. member:)
  • *****
  • Beiträge: 1.290
  • Geschlecht: Männlich
Ich habe das einmal, aber vor langer Zeit, mit diesen Quellen umgesetzt:

entwicklercamp.de/konferenz/ent2009.nsf/bc36cf8d512621e0c1256f870073e627/6ebb5e1b95374ea5c12574fe00501b05/$FILE/T3S7-Java%20Add-In%20Servertask.pdf

www.nsftools.com/tips/JavaAddinTest.java

Gruss Erik :: Freelancer :: KI-Dev, Notes, Java, Web, VBA und DomNav 2.5 / NSE 0.16 / OLI 2.0

--
Nur ein toter Bug, ist ein guter Bug!

Offline Werner Götz

  • Aktives Mitglied
  • ***
  • Beiträge: 248
  • Geschlecht: Männlich
Hallo Benjamin,

genau dieselbe Problematik "durfte" ich mal vor etlichen Jahren bei einem Kunden analysieren, letztendlich hatte ich damals auch das Problem und eine Lösung gefunden.
Auch hier war der Code schon vorher von mehreren Stellen auf Auffälligkeiten geprüft worden ;)

Von der Vorgehensweise her hatten wir ein Logging eingebaut, das an etlichen Stellen den verbrauchten bzw. freien Speicher aufgelistet hat.
Hierdurch konnten wir dann erkennen, an welcher Stelle immer mehr Speicher benötigt wird und haben dann diese Stelle genauer analysiert und letztendlich die Lösung gefunden.
Ich glaube, wir hatten dann an verschiedenen Stellen auch mal den Garbage Collector ( System.gc(); ) aufgerufen.
Bei der Analyse sollten die Ergebnisse beim Aufruf am Client, der ja nicht abstürzt ausreichen.
Aber auch am Server ist es natürlich interessant, im Log zu sehen, wie weit der Server gekommen ist, wie sich hier der freie Speicher verändert hat usw.
Ggf. gibt es da ja auch Unterschiede wg. 64 Bit vs. 32 Bit usw.

Die angesprochene Java-Addin-Servertask ist eine sehr interessante Technik, die ich ebenfalls schon in diversen Projekten eingesetzt habe, die aber aus meiner Sicht keine wirkliche Lösung für das aktuelle Problem darstellt und auch nicht zum Szenario passt, wann man diese Technik nutzen sollte.
Wenn man sich damit beschäftigen möchte, sollte man mal nach Andy Brunner (www.abdata.ch) googlen.
Kann schon sein, dass beim Umstellen auf diese Technik die Routine plötzlich durchläuft (schließlich läuft sie auch am Client durch) - das ganze würde dann aber eher daran liegen, dass der für die Routine verfügbare Speicher ein anderer ist als beim Aufruf über den Agentmanager. Und wenn dann im Lauf der Zeit weitere Dokumente im analysierten Datenbestand hinzukommen, könnte auch die neue Technik dann plötzlich wieder abschmieren.

Genauso könnte (bzw. sollte?) man mal mit diversen Parametern bzgl. Java heap size usw. rumspielen, ggf. bekommt man das Problem auch so "umschifft", sollte dann aber im Blick haben, ob sich der freie Speicher im Lauf der Zeit bei weiteren Dokumenten im analysierten Datenbestand verändert.

Viel Erfolg
-Werner

Offline mapbug

  • Frischling
  • *
  • Beiträge: 7
Vielen Dank für eure schnellen und hilfreichen Antworten.

Den angesprochenen Java-Addin-Servertask werde ich bei Gelegenheit noch probieren.

Mit diversen Parametern haben wir mit der HCL und unseren Partnern viele, viele Tests gemacht -> leider keine Verbesserung.

Ich habe am Samstag noch ein System.gc() nach jedem Speichern eines "Portal"-Dokuments eingebaut und seitdem bekomme ich keine Fehlermeldungen mehr.
Ich werde das natürlich weiterhin beobachten bis der JavaAgent zwei Wochen ohne Fehler durchläuft.

Was mich noch interessiert:
Könnte es sein dass die Speicherallokierung für den AgentenManager einfach Schrott ist ?
Oder hat ein recycle() in einem JavaAgenten eine so geringe Priorität dass der Domino das erst später machen will und sich so den Speicher nie rechtzeitig leert? (und dabei hilft dann der Garbage Collector ?)





« Letzte Änderung: 23.10.23 - 08:28:35 von mapbug »

Offline Werner Götz

  • Aktives Mitglied
  • ***
  • Beiträge: 248
  • Geschlecht: Männlich
Hallo Benjaman!

Das wäre doch die gewünschte Verbesserung?

Wäre auf alle Fälle mal interessant, wenn Du ein Logging einbauen würdest und einen Vergleich mit System.gc() und ohne machen würdest.
Dann sollte man auch sehen, ob der Speicherverbrauch konstant bleibt und nicht von der abgearbeiteten Datenmenge abhängt.

So könnte man dann auch ermitteln, ob der Garbage Collector wirklich nach jedem Dokument aufgerufen werden sollte (kann ich mir nicht vorstellen) oder z. B. alle 100 bzw. 1000 Dokumente - und was das bzgl. Laufzeit an Unterschied ausmacht.

Deine beiden letzten Fragen müssten wohl entsprechende Spezialisten beantworten.
Aus der Doku: "Recycle() destroys the C++ object and sets the Java object for garbage collection."
Problem ist also wohl nicht das recycle, sondern dass der Garbage Collector anscheinend nicht automatisch aufgerufen wird...

-Werner

Offline AlexZX

  • Frischling
  • *
  • Beiträge: 35
Hallo Benjamin,

ich habe bei mir ein ähnliches Problem mit einigen Java Agenten gehabt.

Bei mir hat geholfen den "JavaMaxHeapSize" auf dem Domino auf 1024MB zu erhöhen (in der notes.ini auf dem Domino Server "JavaMaxHeapSize=1024MB" eintragen).

Zusätzlich starte ich den Agent Manager Task jede nacht neu (dadurch werden die evtl. nicht bereinigten Threads entfernt)

Nach diesen Einstellungen laufen bei mir alle Java Agenten schon mehrere Jahre ohne Probleme...


Offline mapbug

  • Frischling
  • *
  • Beiträge: 7
@Werner
Ja, System.gc() war die Lösung.

Ich laufe teilweise mehr als 6000 Dokumente durch um mir Daten zu holen.
Gespeichert werden maximal 50 Dokumente. Nach jeder Speicherung rufe ich den GC auf.
Der Speicherverbrauch hat was mit der abgearbeiteten Datenmenge zu tun.
So wie ich das nun betreibe bringt der Garbage Collector keine nennenswerten Unterschiede in der Laufzeit.


@AlexZX  Danke für den Vorschlag
JavaMaxHeapSize ist gesetzt.
Den AgentManager würde ich ungern nächtlich neu starten, da eigentlich rund um die Uhr Agenten laufen. Aber ein Versuch ist es wert.
« Letzte Änderung: 23.10.23 - 10:03:09 von mapbug »

Offline mapbug

  • Frischling
  • *
  • Beiträge: 7
Dann bedanke ich mich vielmals !!


-closed-

 

Impressum Atnotes.de  -  Powered by Syslords Solutions  -  Datenschutz