the ktor httpclient fails when used concurrently, ...
# ktor
c
the ktor httpclient fails when used concurrently, returns an incorrect response looks like, really strange
d
Do you have more information so we can check? Ktor version? How many concurrent requests? Exception thrown, or result returned? And ideally if you have some code to reproduce the problem would help too
c
well I have to extract the code and create a separate project
I am load testing it with testng parallel execution
d
Which engine are you using?
c
applicationEngineEnvironment
do you mean how i start the whole server?
d
I mean, Apache, Jetty or CIO?
c
ah sorry, the httpClient engine
d
when you construct the HttpClient
c
Apache
d
okay
c
I tried all 3 of them, they all fail give the same exception
essentially they return properly but then i check the response returned, the JSON is intermingled
and as soon as I parse it I get this: java.lang.IllegalArgumentException: JSON at 506: Expected string or non-null literal
d
aha
c
indeed the JSON is non well-formed
I isolated it like this: try { val ret = httpClient.get<String>(serviceUrl) unfilteredResultsCache[params.campusId] = JSON.nonstrict.parse(Container.serializer(KlubschuleCalendar.serializer()), ret) } catch(e:Exception) { rootLogger.error(e) }
sometimes it throws the exeption... especially if I run in parallel
d
the endpoint is a ktor-based endpoint?
c
yes
d
are you testing only the HttpClient
or both?
c
the httpClient calls ktor itself
d
(to isolate the problem, maybe it is a problem with the server or the client)
have you tried wireshark to see if the generated content is right?
c
no, should I ?
d
which engine are you using for the server?
no, just asking 🙂 to delimit the issue
c
i am http requesting through loopback, will wireshare see it?
d
not on windows as far as I remember, but I think it works on mac
still, not required at this point
c
i am on windows
d
okay
which engine are you using for the server?
Netty, Jetty, CIO, …?
c
it's really strange the json returned its like this {,{,{,{,{,}
Netty
d
aha
that’s strange
are you using the ContentNegotiation feature to generate the json response?
c
no, because I generate only a String, then later I parse it with kotlinx serializer
d
aha
c
i get the JSON as a String and then use kotlinx to parse it
d
the json serializer that are you using is thread-safe?
c
i asked and yes they said it's state-less
d
nice
and have you dumped to a file the generated JSON string before responding to the call? (to see if it is malformed at that point and if it is a problem at the server or the client)
if you can reproduce it in a small project, I can try in both a windows machine and a mac machine
c
yes i validate the json before sending it, and when i send it it's valid
d
aha
c
call.respond(validateJson(str))
i also checked that
👍 1
that's why my attention focused on the httpClient
really really weird
d
well still, it could be a problem in the server when sending the HTTP content somehow.
Have you tried HttpClient with CIO to see if the problem still reproduces?
c
could be
yes I tried CIO and problem still happens
d
that’s really strange
c
i keep investigating, eventually i isolate the case for u
d
if you can create a project reproducing it, it would help a lot
c
sure
d
fyi @e5l
c
aha i have to return one thing i said
actually the response is already invalid before call.respond
ok i look deeper and let u know
just making sure it's not me
eheheh
e
Hi, @coder82. Could you provide an example how you execute the requests in parallel?
c
ok is the stringify causing the issue
when i call it before calling call.respond
JSON.nonstrict.stringify(Container.serializer(KlubschuleCalendar.serializer()), a)
so essentially there must be an issue in there which i could isolate and make it nicer to debug
d
call.respond(validateJson(str))
so the validateJson was not working fine?
c
exactly
i noticed the exception happens way less frequently if I use JSON.nonstrict.stringify instead of just JSON.stringify
d
Ok. Maybe something needs to be synchronized. And that could be noise because timings are different
c
inside the JSON class u mean? maybe...
but it's a dataclass should all be on it's own, unless it uses some shared state
d
I don’t know, in your code in general. If I don’t see the code. Maybe you have something shared that you are not aware of
c
ok i'll try to reproduce
d
http://ktor.io/servers/configuration.html#embedded-server You can try to adjust the
connectionGroupSize
to maybe 1? But probably it is related to something shared. Or that serializer. Have you tried Jackson or something instead of that JSON class (that I don’t know where it comes from)?
c
it's inside here:
package kotlinx.serialization.json
d
oh
c
it's the official kotlinx JSON class
it works wonders, but as soon as I started loading with test ng... sometimes it stringifies invalid json
d
I see, that additional information is relevant 🙂 maybe it is a bug from there
c
could be
d
I would try with Jackson just to see if the problem reproduces. If it doesn’t reproduce, then I would investigate further in that direction and would report to #serialization . I can help reporting if you manage to reproduce the problem in an isolate project
c
ok and by the way now i returned back where i asked about serializer thread safety and it seems other ppl have the same issue
wow looks like the JSON class is not thread-safe
found it..
finally
i run already 1M tests so I am sure
basically JSON object has static fields, if u use it in a projects from multiple threads, like in my case were i fire quite a lot of load to my servers
then it's shared state essentially... u change stuff from one thread while that thing is parsing on the other, then it cracks
the solutions was suggested by sand
when multithreaded, instantiate it locally, like this JSON(nonstrict = true).stringify(...)
and not JSON.nonstrict.stringify(....)
nonstrict is static...
d
Nice! glad you found the issue 🙂 thanks for the report!
c
yes basically inside: package kotlinx.serialization.json
there is a private variable:
modeCache
that's shared if u mean such object as singleton
indeed all threads were using 1 JSON object, but there is one state
I am looking in the master now, apparently they moved the state out to make it thread-safe
👍 1