Long database queries blocking main thread in HandleURL

I have a very large database. I’m using it in MariaDB on my staging server but just to give you an example of how large we’re talking, the SQLite file on my dev box is 242 GB. It’s all searchable text data.

My Xojo web app on the server is an API so I’m doing all the work in HandleURL. Pretty much all my endpoints involve the database, and queries can take anywhere from 500ms to 10000ms to return a result (searches take the longest but are necessary functionality). Unfortunately this seems to hang up the main thread of the web app and it is unresponsive during this time to other queries.

As an experiment, I attempted a ticket/checkup system where the API returns a job ID and the client checks back on the result later. Unfortunately the web app is still unresponsive while waiting for the database, while the app is “doing the work.” A ticket/checkup system is not ideal for my use case so I’d rather avoid it if I could.

What is the best way to handle this?

Could you share a sample project where we can reproduce it? HandleURL requests should be running on their own Thread.

MySQL queries are main thread blocking (like FolderItem functions). Without preemptive threading, these long queries will need a separate helper.

2 Likes

Could this be solved with a preemptive thread pool? (a preemptive thread started inside the cooperative thread serving each HandleURL request)

This would require the cooperative thread waiting for the database-querying preemptive thread to finish.

So the follow-up question would be: Are we safe having a Sleep or DoEvents loop inside HandleURL?

I promise, you wouldn’t need either of those things when this is done correctly.

HamdleURL needs to complete as quick as possible, so OPs ticket and response design is actually the correct and standard way to go.

The queries just need to be run in a separate process or preemptive thread because in Xojo MySQL queries block everything.

1 Like

I noticed the same behavior when shelling to mysql on the command line as well. Shells seem to block everything too so that was not a viable workaround.

I’m not sure how to do this without putting my database on the open internet. Here is a sample project showing the same behavior using a shell.

Click the “run” button on the left and try to load the “time” endpoint on the right while it’s still waiting. They will both return at the same time, once the wait has completed.

webShell.xojo_binary_project.zip (11.9 KB)

For the record, I totally agree with you Tim.

A fully preemptive thread-based web framework engine would be one of the biggest improvements Xojo Web could see as a product.

I just don’t see it happening soon and I hope Xojo proves me wrong :face_savoring_food:

1 Like

Set the task and poll for completion. Shell async should not block. (I can’t check your project right now.)

1 Like

Thanks, yeah, this seems like the only way to make this work in Xojo. The ticket/response adds a layer of complexity to both server and client that I really don’t want to deal with, so I might end up switching to php for this.

Could you not get your HandleURL method to run the database query in a preemptive thread and loop / yield until it had finished?

Decent idea, I could give it a try. But that puts me in DoEvents() territory which significantly raises my blood pressure. :face_in_clouds:

The HTTP response needs to be as fast as possible, regardless of Xojo’s limits. A 10 second query needs to be ticket / response even if you use PHP.

1 Like

Why do you think you need to use DoEvents?

Other requests come in on co-operative threads. As web is a framework on top of console, we know we need DoEvents to perform the other tasks while we wait.

With just a while loop in HandleURL it will lock up waiting, which is the original problem with extra steps.

1 Like

OK - makes sense.

Here’s an updated version of the Shell example that lets you switch between synchronous and asynchronous shells for the wait.

Async shells use the while / doevents / wend loop and are indeed responsive to new connections. This is really more of an academic exercise because I am not eager to deploy something that uses DoEvents or has such a long http response time.

Interestingly enough, in the screenshot (or if you run the demo for yourself), you can see that the Query number (3 in the screenshot) is the same on both the async shell connection and the time connection. This is a great illustration of why this approach is so dangerous. The querycount is a private integer on the app class. The /wait call was QueryCount=2 and while the shell was executing it was incremented to 3 during the /time call. After the shell completed the HandleURL thread resumed, reading the new QueryCount value of 3. If you’re not expecting this, you can introduce some weird bugs in your code that are really difficult to track down (the fix in this case would be to read the QueryCount value into a local variable in the HandleWait method prior to executing the shell).

webShell sync+async.xojo_binary_project.zip (13.0 KB)

1 Like