I have a Mojolicious App behind a reverse proxy. The Mojo App uses Promises, and in some cases, the synchronous response is returned after 2 minutes, by which time, the reverse proxy times out.
How can I best render the response? (making my subroutine respond sooner, unfortunately, is not an option)
- Can I show a temporary page, but commit / render the final response afterwards?
- Can I use Ajax to show a spinner, then render the final response once done?
- Should I switch to Async, perhaps integrate Mojolicious with Minion, and render a response that checks if the async call is ready via Ajax? (pointers and samples would be appreciated).
I demonstrate this behavior using docker
below with a reverse proxy that times out after 5 seconds, and a mojo app that responds after 10 seconds:
1. Create a Dockerfile
FROM docker.io/library/perl:5.36
# Install Apache2
RUN apt-get update && apt-get install -y --no-install-recommends apache2
# Install Mojolicious
RUN cpanm Mojolicious
# Install Apache mod_proxy and configure Apache2 as a reverse proxy to mojo app with 5s timeout
RUN a2enmod proxy_http
&& perl -i.bak -pe 's#</VirtualHost>#tProxyPass /mojo http://localhost:3000/ keepalive=On timeout=5n</VirtualHost>#' /etc/apache2/sites-enabled/000-default.conf
# Generate a mojo app and simulate a timer
RUN mojo generate app
&& perl -i.bak -pe 's/^.*$/ sleep(10);/ if $. == 6' /my_app/lib/MyApp/Controller/Example.pm
# Expose Apache2 and Mojo ports
EXPOSE 80 3000
# Run Apache 2 in the background and Morbo in the foreground
CMD ["/bin/bash", "-c", "apache2ctl start; /my_app/script/my_app prefork"]
2. Build & run:
docker build -t myapp .
docker run --rm --name myapp -p 8080:80 -p 3000:3000 myapp
3. Browse to:
http://localhost:8080/mojo
2
Answers
I ended up going with an asynchronous approach using Minion as a queue. A full working example of a
LinkCheck
application is provided at:https://github.com/mojolicious/minion/tree/main/examples/linkcheck
The example requires you either:
docker
:OR 2. Replace one line during startup to use SQLite instead of Postgres. (requires
Minion::Backend::SQLite
plugin )In either cases, you must run a separate a background worker process to handle the job queue, for example:
You should look at a streaming response, and writing chunks of your response as you they are built, rather than sending them after the whole response is built, see https://docs.mojolicious.org/Mojolicious/Guides/Rendering#Streaming