-
Notifications
You must be signed in to change notification settings - Fork 7
Description
Description
EQL query objects represent particular instances of EQL queries. Structurally, they are just a collection of tokens. These objects are immutable, and are expected to be thread-safe.
However, testing under specific circumstances revealed that EQL query objects are not thread safe.
When multiple threads compile the same EQL query object at the same time, it possible for some mutable state to be shared between them, causing errors during parsing.
Specifically, the use of method AbstractModel.tokenSource.restart() is not thread-safe, as its result depends on unguarded mutable state. It returns a mutable object, which, if reused by multiple threads, is likely to violate some expectations of the ANTLR parser.
In practice, there have been no reports of errors that might be caused by this.
If this were to occur, it would likely be caused by multiple threads compiling the same synthetic model at the same time (some synthetic model objects are stored in static fields).
How to reproduce
To reproduce this issue, use one instance of an EQL query object and compile it in multiple threads simultaneously.
For example, ua.com.fielden.eql.AbstractEqlBenchmark.many_yielded_subqueries does exactly this. There, the shared query object is the synthetic model of TgVehicleFuelUsage.
When running that benchmark, the error occurs after ~10 calls to the benchmark method.
Expression: select(TgFuelUsage) where prop("date") gt iParam("datePeriod.from") and prop("date") lt iParam("datePeriod.to") groupBy prop("vehicle") yield prop("vehicle") as("vehicle") modelAsEntity(TgVehicleFuelUsage)
Source: List
Reason: mismatched input 'where' expecting {'cond', 'expr', 'orderBy
It is reported as a syntax error, which may be confusing since the query itself is 100% syntactically correct.
Expected outcome
EQL query objects can be safely compiled by multiple threads simultaneously.