Data corruption with Nashorn + Handlebars and concurrent requests
A. Sundararajan
sundararajan.athijegannathan at oracle.com
Tue May 26 05:45:37 UTC 2015
Hi,
Thanks for raising the issue with handlebars project as well! Actually
either the Handlebars.compile or the compiled function could be
MT-unsafe. FWIW, I tried the following change to handlebars's render.js
script:
function renderHandlebars(template, model) {
// Create a real Javascript Object from the model Map
var data = {};
for (var k in model) {
// Convert Java Iterable and List to real Javascript arrays
if (model[k] instanceof Java.type("java.lang.Iterable")) {
data[k] = Java.from(model[k]);
} else {
data[k] = model[k];
}
}
// TODO Manage compiled template cache
var compiledTemplate = Handlebars.compile(template);
// call compiled template function as synchronized
// use Handlebars object as lock
return Java.synchronized(function(data) {
compiledTemplate(data);
}, Handlebars);
}
With that change, it appears handlebars MT-usage seems fine [i.e., no
exception seen]. This suggests that the Handlebars.compile itself is
MT-safe but the compiled handlebars function may not be MT-unsafe. FYI.
PS. I'm updating the bug with these info..
Thanks,
-Sundar
On Tuesday 26 May 2015 01:54 AM, Sebastien Deleuze wrote:
> Hi,
>
> Thanks for your feedback. I have asked the question on Handlerbars.js issue
> tracker [1], let's see what is their answer.
>
> Regards,
> Sébastien Deleuze
>
> [1] https://github.com/wycats/handlebars.js/issues/1031
>
> On Mon, May 25, 2015 at 9:56 AM, A. Sundararajan <
> sundararajan.athijegannathan at oracle.com> wrote:
>
>> Hi,
>>
>> Thanks for (near!) standalone test. I've further simplified -- copied all
>> relevant scripts, removed testng dependency. Just a main class, Comment
>> class and associated scripts. I've filed a nashorn bug to track this
>> issue/thread:
>>
>> https://bugs.openjdk.java.net/browse/JDK-8080975
>>
>> I've attached a .zip file with the standalone program which I mentioned.
>>
>> Now, even with loadWithNewGlobal, you're leaking Handlebars object to the
>> new global - that is quite not an isolation. New global isolation will work
>> only if you use simple data being passed (like string, numbers etc.). In
>> your case, you're passing "Handlebars: object to the script executed in new
>> global. And "compile" method of Handlebars object is called. That compile
>> method is executed in the old global- i.e., it'd refer to global variables
>> in which Handlebars object was picked from! We do *not* really know if
>> handlebars.js is designed with concurrency in mind at all!! In other words,
>> is handlebars library thread safe? For eg. if "compile" function uses any
>> global variable, the variable is from that old global in which Handlebars
>> object is defined. That is asking for trouble.
>>
>> -Sundar
>>
>>
>> On Friday 22 May 2015 07:46 PM, Sebastien Deleuze wrote:
>>
>>> Hi,
>>>
>>> I have added a very simple unit test, without any Spring Boot code, that
>>> reproduces the issue [1].
>>>
>>> Rendering concurrently templates with Mustache works as expected.
>>> Rendering concurrently templates with Handlebars generates the exceptions
>>> described previously, even if I use loadWithNewGlobal().
>>>
>>> Does that help you to find the issue?
>>> Is it the expected way to use loadWithNewGlobal() to avoid concurrency
>>> issues?
>>>
>>> Regards,
>>> Sébastien
>>>
>>> [1]
>>>
>>> https://github.com/sdeleuze/script-engine-test/blob/master/src/test/java/demo/NashornConcurencyIssueTests.java
>>>
>>> On Tue, May 12, 2015 at 6:49 PM, A. Sundararajan <
>>> sundararajan.athijegannathan at oracle.com> wrote:
>>>
>>> Hi,
>>>> Sorry for the delayed response. It has been very difficult locating
>>>> anything in particular.
>>>>
>>>> Is it possible for you to reduce the test further? I think you'd in
>>>> better
>>>> position because of the domain knowledge..
>>>>
>>>> Thanks,
>>>>
>>>> -Sundar
>>>>
>>>>
>>>> On Wednesday 06 May 2015 05:32 AM, Sebastien Deleuze wrote:
>>>>
>>>> Hi,
>>>>> I currently try to implement support for Script based templating in
>>>>> Spring
>>>>> Framework [1] using Nashorn and template engines like Mustache or
>>>>> Handlebars.
>>>>>
>>>>> As discussed today on Twitter [2], I have some strange argument
>>>>> corruption
>>>>> issues when using Nashorn + Handlebars with concurrent request. I am
>>>>> aware
>>>>> of loadWithNewGlobal() but it seems I don't use it properly because I
>>>>> still
>>>>> see the issue using it.
>>>>>
>>>>> In order to make things simpler, I have created a repo project [3]. To
>>>>> make
>>>>> it work, simply execute TestApplication main class.
>>>>>
>>>>> MustacheJS seems to works with concurrent requests :
>>>>> ab -n 5000 -c 4 http://localhost:8080/mustache/
>>>>>
>>>>> But Handlebars seems to have some strange data corruption issues [4]
>>>>> (the
>>>>> template contains <title>{{title}}</title> but <title>{title}}</title>
>>>>> is
>>>>> printed in the log error message !!!) running both raw
>>>>> and loadWithNewGlobal() alternatives:
>>>>> ab -n 5000 -c 4 http://localhost:8080/handlebars/
>>>>> ab -n 5000 -c 4 http://localhost:8080/handlebars/newglobal/
>>>>> (the errors are displayed in the system output)
>>>>>
>>>>> Could you please have a look to this repro project in order to see if:
>>>>> - There is a better way to use loadWithNewGlobal() (without the
>>>>> intermediate render() function maybe)
>>>>> - If there is an issue in my code ?
>>>>> - If there is an issue in Nashorn ?
>>>>>
>>>>> Running ab -n 5000 -c 1 http://localhost:8080/handlebars/ produces no
>>>>> error, so this is really related to concurrency.
>>>>>
>>>>> This is a feature we would like to ship with Spring Frameworks 4.2, so
>>>>> making it running flawlessly is mandatory for us, I hope it will be
>>>>> possible!
>>>>>
>>>>> Thanks in advance for your help,
>>>>> Sébastien Deleuze
>>>>>
>>>>> [1] https://jira.spring.io/browse/SPR-12266
>>>>> [2] https://twitter.com/sdeleuze/status/595586467298222082
>>>>> [3] https://github.com/sdeleuze/script-engine-test
>>>>> [4] jdk.nashorn.internal.runtime.ECMAException: Error: Parse error on
>>>>> line
>>>>> 3:
>>>>> ...ead> <title>{title}}</title>
>>>>> ----------------------^
>>>>> Expecting 'EOF', 'COMMENT', 'CONTENT', 'END_RAW_BLOCK',
>>>>> 'OPEN_RAW_BLOCK',
>>>>> 'OPEN_BLOCK', 'OPEN_INVERSE', 'OPEN_INVERSE_CHAIN', 'INVERSE',
>>>>> 'OPEN_ENDBLOCK', 'OPEN', 'OPEN_UNESCAPED', 'OPEN_PARTIAL', got 'INVALID'
>>>>> at
>>>>>
>>>>>
>>>>> jdk.nashorn.internal.scripts.Script$\^eval\_$2._L36$_L874$_L880$parseError(<eval>:1025)
>>>>> at
>>>>>
>>>>>
>>>>> jdk.nashorn.internal.scripts.Script$\^eval\_$2._L36$_L874$_L880$parse(<eval>:1077)
>>>>> at
>>>>>
>>>>>
>>>>> jdk.nashorn.internal.scripts.Script$\^eval\_$4._L36$_L1910$parse(<eval>:1936)
>>>>> at
>>>>>
>>>>>
>>>>> jdk.nashorn.internal.scripts.Script$\^eval\_$5._L36$_L1944$compile$compileInput(<eval>:2396)
>>>>> at
>>>>>
>>>>>
>>>>> jdk.nashorn.internal.scripts.Script$\^eval\_$4._L36$_L1944$compile$_L2403(<eval>:2405)
>>>>> at
>>>>> jdk.nashorn.internal.scripts.Script$\^eval\_.renderHandlebars(<eval>:17)
>>>>> at
>>>>>
>>>>>
>>>>> jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:502)
>>>>>
>>>>>
More information about the nashorn-dev
mailing list