Scenario:
Has anybody else experienced timeouts This article explains how timeouts are handled in libraries which call each other ? Right now I have lots of business logic in Groovy libraries, but because of this error I have had to set the timeouts to max. When a Groovy library logic is called, the timeout seems to be summed into some global cache which records the amount of time spent inside that element during the current execution thread. The better approach should be that the timeout is only applicable per-call to the element. Maybe easier would be to only obey the parent elements timeout, disregard all sub-elements timeouts.
Explanation:
Let me add some info on how timeouts technically work. and how they are handled in general.
Example
In the example below, running lib.ElementA.getAverageMargin()
for a large set of SKUs will time out after 2 seconds. The reason is that even though each time the getProductMargin
method is called, it takes less than 2 seconds each time, the cumulative total time spent inside that method (because of its reference in the loop) will be more than 2 seconds.
lib.ElementA: (timeout 300 seconds)
Code Block |
---|
Number getAverageMargin(List<String> skus){
List<Number> margins = []
for(sku in skus){
margins.add(lib.ElementB(getProductMargin(sku)))
}
return margins.sum() / margins.size()
} |
lib.ElementB: (timeout 2 seconds)
Code Block |
---|
Number getProductMargin(String sku){
def costPrice = api.find("PX30")... //get cost from some data source
def sellPrice = api.find("PLI")... //get sellprice from another data source
return sellPrice - costPrice
} |
Explanation and Technical Background
Timeouts are essentially safety catches against a bad logic (such as accidental infinite loops). Now the JVM does not have something like a setting like "run this thread, but only for x seconds". So we need to apply some tricks to make this work. The trick here is use a trick that we inject (at compile time) some code that throws an exception if the system time has elapsed the start time + timeout. Now the different logic elements are essentially Groovy script classes. So there is no easy sharing of a script-global "start time" variable in all thinkable cases. So we resort to using a start time variable per element (normal or lib element) which is initialized at the class instantiation. So essentially a private script class member variable which is set to the system time in the constructor of the script class. The engine "resets" that start time for lib elements by re-instantiating the lib elements before executing every normal formula element. Normal elements are only instantiated once (hence the topic of "timer starts running when the element is executed first time" when you call previous elements further down).
...
Hint: The suggestion "simply do not compile in timeouts for lib elements" does not work, as then an infinite loop in a lib would not be caught. .. And no, from From a system perspective we cannot trust any groovy Groovy code to be "safe".