Cutelyst 2.7.0 released, async is back!

Cutelyst a Qt/C++ Web Framework just got a new version. This time bringing back proper async support.

Perl Catalyst Framework was conceived as a sync/blocking framework, due that the Mojolicious framework was created, NodeJS and a few other web frameworks have pushed the async programming to the web. Performance wise being async doesn't mean you get faster response times, rather the opposite, the need to unroll stack and make extra calls makes a CPU bound application slower.

But depending on the problem to solve it allows you to serve more users at the same time, using the same CPU cores. A typical modern application might receive a request from a Phone App then do a REST API call to an external API which might take 2ms or 200ms for the reply. While waiting for the response, typical sync/blocking applications can't process more requests, or have to spawn more threads increasing RAM consumption and leveraging concurrency to the OS scheduler. On an Async web application, you can process another request while you wait for the previous request, thus possibly making the first request reply to be sent back to the user at a later time than if there was a dedicated process just waiting for his reply.

So, both ways have pros and cons and IMHO I'd like to support them both. When I started Cutelyst I thought that if I ever need async I could have a local QEventLoop to wait for the reply and would be able to deal with async requests, not recently I found out that supporting QEventLoop was causing stack overflow due it being used in pipelined scenarios, after that I removed it's usage and performance improved in PlainText tests of TechEmpower, so I advised against using it and marked Cutelyst as not async.

Recently I got the need to call a few APIs from a REST API I did with Cutelyst, QNeworkAccessManager is an async only API, so either I create a thread for it to mimic sync behavior or allow Cutelyst to be used in async mode, of course I did the latter. The Context class has now detachAsync() and attachAsync() methods.

When you c->detachAsync(), you tell the engine to not send the headers and body immediately to the client, this will also break the action chain, meaning an end() method won't be called, then when you get your async reply you call c->attachAsync().

Once c->attachAsync() is called the action chain is resumed so the end() method that possibly would render the HTML can perform it's job.

It's important to always be child ( QObject->setParent(...) ) of the Context pointer as if the client closes the connection it might be deleted.

This release was not only made of async:

  • A helper UserAgent singleton was added so that you have a thread_local QNetworkAccessManager with static methods to do more common REST tasks like sending a QJsonObject (yes, eventually I find time to write a patch for QNAM)
  • A new Sql::Transaction class gives you a scoped SQL transaction, preventing the mistake of not rolling back when you leave the scope in case of an error.
  • A few Headers helpers like ETag and If-Match comparison
  • Sebastian Held added support for dealing with Front-End Proxy Headers
  • Improved Stats precision using QElapsedTimer::nsecsElapsed()

And finally Cutelyst is not "half modern" CMake anymore, if that can even exist. The include directories of the exported Targets are now properly exported, so that you can remove:

include_directories(
    ${Cutelyst2Qt5_INCLUDE_DIR}
)

Have fun https://github.com/cutelyst/cutelyst/archive/v2.7.0.tar.gz