Hello all,
I am facing an issue with my application using BaseX 8.6.2.
Basically what I have is one thread (Add thread) receiving data from a socket as XML fragment and storing it in BaseX.
On the other hand, my application provides a REST interface to retrieve data from the DB.
When retrieving data what happen is, I first execute a simple Xquery in a thread (Primary Read), and for each hit returned by this first query, I execute another query in another thread (Secondary read) to retrieve additional data.
The problem is that I end up in a situation where all three threads are stopped waiting for something.
The Add thread is park as it cannot acquire a write lock, while the Primary read is running, as it should be.
But the Secondary read is also parked, thus blocking the Primary read, and so we end up in a kind of deadlock.
The PARALLEL option in .basex is set to 4, and the FAIRLOCK option is set to true.
Some technical details:
In the primary read I am using
session.setOutputStream(pos);
session.execute(new XQuery(flwor));
where session is a LocalSession and pos is a PipedOutputStream
In Secondary read, I am reading the other side of the piped stream, and for each line read from the stream, I execute another query (well not exactly in the real case scenario, but this simplification seems good enough to exhibit the problem, and is shown in the attached BaseXDeadlock.java example). You can also see the stacks of all three threads in BaseXDeadlockStacks.txt
In this example, if I don’t start the Add thread, then Primary and Secondary read are running as expected.
On the other hand, if I change my implementation of Primary and Secondary read, and use a blocking queue to transfer data from Primary to Secondary read like shown below:
Query query = session.query(flwor)
while (query.more()) {
queue.put(query.next());
}
And then taking from the queue in Secondary read, then it works. This implementation is shown in BaseXNoDeadlock.java.
Using the latter is good enough for me, as it does the job, even if there is a small performance penalty compared to the first implementation, in my real case scenario.
So my question is: is my first implementation fundamentally broken or is there something wrong on BaseX side?
Best regards and many thanks to all the BaseX team for this efficient and nice to use tool.
Simon Chatelain
Dear Simon,
Sorry for letting you wait. I discarded my first answer to you, because I had some error messages that I couldn’t reproduce later (possibly caused by an existing DB1 database instance?).
You have already described very well how the starvation is caused. I saw that (and I was not sure why) br.readLine() is called three times in the secondary read thread. However, the behavior didn’t change after reducing it to a single call.
I must admit I haven’t digged into the details. My initial assumption is that the deadlock is eventually not caused by our client/server code, but rather to the buffering of the input stream. I would indeed recommend to stick with the elegant blocking queue approach, and I would hope that the decreased performance is hardly measurable.
More feedback on your experiences is welcome, Christian
basex-talk@mailman.uni-konstanz.de