RuntimeException: Limit of 100 reached

Hallo,


wir haben merkwürdigerweise einen Fehler in einem Flow seit gestern um 20:30 Uhr:


Teppichcenter Aufträge zu NAV


Letzte Meldung: 16 Rows with errors, showing First 10 : <br />Row: 127 - {Neu-versenden=Script error: (Root Causes: TemplateModelException: No error description was specified for this error; low-level message: RuntimeException: Limit of 100 reached. ---- FTL stack trac

Im SpreadsheetMapper - "Sind Artikel aus Überverkäufen wieder verfügbar?"

WARNING
WARNING:16 Rows with errors, showing First 10 :
Row: 127 - {Neu-versenden=Script error: (Root Causes: TemplateModelException: No error description was specified for this error; low-level message: RuntimeException: Limit of 100 reached.----FTL stack trace ("~" means nesting-related): - Failed at: ${setVariable(OrderItemArticleNumber,... [in template "Neu-versenden" at line 3, column 1]---- RuntimeException: Limit of 100 reached.): <#if getVariable(OrderItemArticleNumber!) == ""><#assign temp = OrderItemQuantity!?number>${setVariable(OrderItemArticleNumber,temp)}<#else><#assign temp = OrderItemQuantity!?number + getVariable(OrderItemArticleNumber!)?number>${setVariable(OrderItemArticleNumber,temp)}</#if><#if (getVariable(OrderItemArticleNumber!)?number <= meta.map@KeyValueSpreadsheet_54.get(OrderItemArticleNumber))> ja <#else> nein </#if>},

Könnt ihr da feststellen ob da ein Fehler vorliegt? Der Flow lief vorher schon monatelang ohne Fehler.

Hallo,


die Fehlermeldung kommt wenn die max. Anzahl (100) verschiedener Keys erreicht wurde (siehe https://apps.synesty.com/transformy?action=showTemplateFunctionDocumentation&target=functions#setVariableTransformy). Vermutlich hast du dieses Limit bisher noch nicht überschritten, weil die Anzahl der verschiedenen OrderItemArticleNumbers kleiner als 100 war.



Optimal, vielen Dank für eure schnelle Hilfe :-)


Viele Grüße


Alexander

Ich verstehe dass das ein hartes Limit ist, aber gibt es irgend eine Möglichkeit das zu umgehen außer das Limit im Input-Step auf 1 zu stellen, so immer nur einen Datensatz zu verarbeiten und so sicher zu stellen dass wir nicht ins Limit laufen?


Ich tue etwas in diesem Stil:

<#if (Variant_inLager == "1")>
  <#if getVariable(OrderItemsVariantID) == "">
    ${(getStocks?number - OrderItemsQuantity?number)}
    ${setVariable(OrderItemsVariantID, getStocks?number - OrderItemsQuantity?number)}
  <#else>
    ${getVariable(OrderItemsVariantID)?number - OrderItemsQuantity?number}
    ${setVariable(OrderItemsVariantID, getVariable(OrderItemsVariantID)?number - OrderItemsQuantity?number)}
  </#if>
</#if>

Das prüft für alle Aufträge im Abruf ob der Bestand ausreichend ist, und zählt dabei mit von einem Auftrag zum nächsten: Also ich habe Bestand von 10, der erste Auftrag möchte 2, also bleiben noch 8, der nächste zieht dann von den 8 zB 5 ab, und so weiter, bis die Variable auf 0 läuft. Alle dann noch folgenden Aufträge haben nicht mehr genug Bestand, und werden in Warteposition geschoben, anstatt zum Versand freigegeben.


Das hat im kleinen super funktioniert: wir haben einen Versandstandort der nur einige dutzend verschiedene Artikel bereithält, und so konnte ich gezielt diesem Lager nur Aufträge zuweisen, für die es Bestand hatte. Der Rest wurde im anderen Standort behalten und von dort versendet.


So bin ich nie über die 100 gekommen, und weils so gut lief, wollte ich es jetzt auch für ein größeres Lager einsetzen. Damit sind dann sowohl die Anzahl der betroffenen Artikel selbst gestiegen (die Variable OrderItemsVariantID), als auch die Anzahl der zu verarbeitenden Aufträge, und auch die möglichen Positionen in jedem einzelnen Auftrag.


Das sah in der Entwicklung mit Filter auf einzelne OrderIDs alles super aus, aber sobald ichs dann live genommen habe, hat dieser Fehler zugeschlagen.


Hat jemand ne kreative Idee wie ich das umgehen kann? Wie gesagt: nur noch einzelne Aufträge verarbeiten ist wenig praktikabel. Der Cron läuft derzeit alle 15 Minuten, und hat dann gern mal um die 80 Aufträge drin. Es ist also gar kein "Platz", selbst wenn wir jede Minute laufen lassen, sind das immer noch (möglicherweise) zu viele Aufträge mit zu vielen Positionen.


Muss ich mich von dem Gedanken verabschieden?

Grüße Daniel

Wir schauen mal, ob wir eine kurzfristige Lösung finden.

Kurzer Zwischenstand: Wir haben da etwas. Melden uns heute oder morgen dazu.

Hallo Daniel,


wir haben als kurzfristige Lösung das Limit auf 1000 Keys erhöht. Ich hoffe das hilft dir erstmal weiter.


Viele Grüße

Torsten

Hi @synesty-Torsten und @synesty-Sales,

die kurzfristige Lösung hatte mir in 2021 tatsächlich geholfen, aber jetzt sind wir doch zu stark gewachsen :sweat_smile:

Ich wollte jetzt schon fast schon wieder mein Problem umständlich erklären, aber es ist egtl genau das von oben, der Code ist gleich geblieben, der Forenwechsel hat aber den oben abgeschnitten, hier erneut:

<#if (Variant_inLager == "1")>
  <#if getVariable(OrderItemsVariantID) == "">
    ${(getStocks?number - OrderItemsQuantity?number)}
    ${setVariable(OrderItemsVariantID, getStocks?number - OrderItemsQuantity?number)}
  <#else>
    ${getVariable(OrderItemsVariantID)?number - OrderItemsQuantity?number}
    ${setVariable(OrderItemsVariantID, getVariable(OrderItemsVariantID)?number - OrderItemsQuantity?number)}
  </#if>
</#if>

Ich habe hier nur ein paar Variablen zur Verständlichkeit umbenannt im Vergleich zur Live-Version

→ Zählvariable für jede Auftragsposition/Varianten-ID, getStocks enthält den relevanten Bestand, ich ziehe die Quantity der Position ab, speichere in die Zählvariable.

Seit vorhin läuft mein Flow in den bekannten Fehler:

Row: 1236 - {stockAfterReservation_Thale=Script error: (Root Causes: TemplateModelException: No error description was specified for this error; low-level message: IllegalArgumentException: Limit of 1000 map entries reached.

Naheliegende „Lösung“ wäre natürlich, das Limit aufzubohren. Ich wäre aber auch aufgeschlossen jeder anderen Lösung gegenüber, die das Ganze eleganter löst. Ihr müsst nur sagen wie :wink:

Vielleicht gabs ja in der Zeit irgendwelche Features die mir hier helfen könnten? :thinking:


Kleines Schmankerl habe ich noch: wenn ich auf den SpreadsheetMapper der den Code enthält einen CSVWriter anschließe (in einer anderen Gruppe, ganz am Ende), und mir den dann per Email senden lasse: dann hören die WARNINGs auf :astonished:

Ich hab keinerlei Ahnung warum, aber läuft dann „grün“ durch. Ich kanns mir nicht erklären, und weiß nicht obs ein Ansatzpunkt darstellt, oder der Zufall ganz böse Spiele mit mir spielt. Aber ich wollts erwähnt haben.


Perspektivisch wächst auf jeden Fall sowohl die Zahl der Artikel pro Auftrag, als auch deren jeweilige Bestände am Lager weiter an. Und die Hauptsaison steht bei uns kurz bevor, bzw läuft grade schon an.

Deshalb wäre eine skalierende Lösung dringend nötig.

Ich lass das ganze schon alle 15 Minuten laufen, kürzer kann ich fast nicht mehr takten, das ganze Prozedere (mehrere Flows die sich gegenseitig aufrufen und Aufträge durch verschiedene Status schieben, wo sie dann der nächste Flow abholt) läuft schon zwischen 5 und 10 Minuten, bzw läuft auch jetzt schon manchmal in ein „Flow already running“-Warning, weil der letzte Durchlauf noch nicht zu Ende war. Ich hab da noch 2-4 Minuten Optimierungspotential, aber befürchte das reicht nicht aus.

Bin um jeden Input dankbar, und wenns nur „das Limit ist jetzt 5000“ ist :stuck_out_tongue:

Grüße Daniel

Hallo Daniel,

weißt du, wieviele Zeichen in dem Wert-Feld von den Variablen möglich ist?

Man kann in der Variable ja durchaus eine Hashmap mit Varianten-IDs und zugehörigem Bestand pflegen und so mehrere Varianten in einer Variable abfrühstücken.

Rein theoretisch könnte man auch über eine Logik mehrere Variablen mit Hashmaps verwenden, wenn nicht alles in eine Variable passt.
Also wenn in der Variable HASHMAP_1 kein Platz mehr für weiter ID-Bestandspaare mehr ist, wird eine weitere Variable HASHMAP_2 angelegt und dort neue ID-Bestandspaare abgelegt. Nachteil von der Lösung wäre, dass du für jede Varianten-ID erstmal alle Hashmaps durchsuchen musst, ob dort irgendwo die ID enthalten ist.
So richtig begeistert bin ich von der Lösung aber auch nicht. Überlege nochmal, ob mir nicht was besseres einfällt.

Gruß
Gustav

Hi Gustav,

Maximale Länge des Wert laut Handbuch 10.000.

Kannst du mich bzgl der Hashmap vielleicht in die richtige Richtung stoßen, meinst du eine „manuelle“ zu erzeugen mit etwas wie

<#assign stockMap = stockMap + {OrderItemsVariantID: getStocks}>

und das dann in eine der Variablen schreiben?

Mir ist grade unklar wie ich das korrekt initialisiere, wie ich merke wann ich die Variablen switchen muss, bzw das ganze dann später navigiere.

Oder meinst du die HashMap die das KeyValueSpreadsheet zur Verfügung stellt? Da ich ständig die Werte anpassen muss darin: wie schreibe ich dort rein?

Ich glaube ich steh auf dem Schlauch. War ein langer Tag :sweat:

Also die Idee versteh ich, an der Umsetzung haperts. Will dir jetzt aber auch kein Proof-of-concept abreden wenn du selbst sagst, die Idee ist ausbaufähig. Aber wenn du mir nen Knochen hinwerfen könntest, würde ich schauen damit weiter zu kommen.

Ich hab mal meinen Code in ein Beispiels-Transformy gepackt:

Hier zähle ich von jedem brandname von einem Bestand von 999 runter, in jeder Zeile jeweils um stock.

Wie ginge das jetzt mit einer Hashmap die in eine Variable gespeichert wird? :thinking:

…vielleicht kommt auchTorsten noch mit ner Idee um die Ecke :ok_hand:
Grüße Daniel

Hallo Daniel,

ich hatte tatsächlich daran gedacht, dass man eine selbst gebaute Hashmap in eine Variable packt.
Also um bei dem Markenbeispiel aus der Transformy-Testdatei zu bleiben, würdest du irgendwie sowas wie:

{
   "K-Star":760,
   "3-Stripes":100,
   "Runnerschoice":10,
...
}

in eine Variable packen und kontinuierlich Bestände modifiziert bzw. um neue Marken erweitern.
Ich hatte das heute mal ganz kurz getestet und das klappt so leider aus mehreren Gründen nicht. setVariable akzeptiert keine Hashmap als Input, das Erweitern von Hashmaps funktioniert nur sehr begrenzt und Modifizieren von vorhandenen Werten ist schonmal garnicht möglich.

Als Alternative gäb es aber noch das Map-Konzept von Synesty. Wenn man statt Hashmaps eben diese Map-Strings, z.B. „K-Star=760,3-Stripes=100,Runnerschoice=10,…“, als Variable ablegt, sollte das erreichbar sein, was ich mir mit den Hashmaps vorgestellt habe.

Mein Proof-of-Concept funktioniert soweit.

Im Moment ist meine Bedingung zum Füllen der Liste, dass dort 3 Elemente vorhanden sind. In deiner Anwendung kannst du die Listen ja aber viel länger benutzen, bis die über 9000 Zeichen lang werden oder so.

Damit solltest du auf jeden Fall weiterkommen. Nur fraglich, ob das tatsächlich die richtige Lösung für dein Problem ist. Vielleicht wartest du nochmal Input seitens Synesty ab, bevor du das so umsetzt.

@Synesty: Könnt ihr mir erklären, warum die .containsKey-Funktion nur funktioniert, wenn ich brandname?trim benutze? Für die Freemarker-Stringfunktion ?contains ist das nicht nötig.

Gruß
Gustav

1 Like

Wow, vielen vielen Dank Gustav :star_struck:
Das hast du ja richtig säuberlich gemacht und sogar kommentiert, super, das nützt mir viel.

Ich komm vor Dienstag/Mittwoch eh zu nix, ich warte mal ab ob sich bis dahin @synesty-Torsten noch meldet…

Dir ein schönes WE,
Grüße Daniel

Hallo Daniel und Gustav,

ich denke der von @gustavfriedeheim (Danke Gustav :+1:) vorgeschlagene Weg ist aktuell die beste Lösung, um möglichst viele Varianten unterzubringen. Wie Gustav schon geschrieben hat, kannst du jede Map mit 10.000 Zeichen befüllen. D.h. bei sagen wir mal (konservativ gerechnet) 50 Zeichen pro ${VariantenID}=${Stock}; Eintrag bekommst du 200 Varianten in eine Map.

An den Limits der setVariable Funktion wollen wir (aktuell) nichts ändern.

@gustavfriedeheim: Das ist leider ein Bug bei der containsKey Funktion. Wir werden das im Laufe der Woche beheben.

Viele Grüße
Torsten

1 Like

Guten Morgen @gustavfriedeheim,

das Problem bei der containsKey Funktion ist jetzt behoben. Vielen Dank für den Hinweis!

image

Viele Grüße
Torsten