axelclark

axelclark

How to handle max concurrency for ChromicPDF?

I’m using ChromicPDF in my application to allow a user to download a pdf. In my application, I have a button they can click that sends a request to my controller which generates the pdf with ChromicPDF and sends it back to the user with send_download from the controller.

ChomicPDF docs state:

To increase or limit the number of concurrent workers, you can pass pool configuration to the supervisor. Please note that this is a non-queueing session pool. If you intend to max it out, you will need a job queue as well.

I was able to generate the error in testing:

** (ChromicPDF.Browser.ExecutionError) Caught EXIT signal from NimblePool.checkout!/4

      ** (EXIT) time out

This means that your operation was unable to acquire a worker from the pool
within 5000ms, as all workers are currently occupied.
You're experiencing this error randomly under load. This would indicate that
   the number of concurrent print jobs exceeds the total number of workers in
   the pool, so that all workers are occupied.

   To fix this, you need to increase your resources, e.g. by increasing the number
   of workers with the `session_pool: [size: ...]` option.

   However, please be aware that while ChromicPDF (by virtue of the underlying
   NimblePool worker pool) does perform simple queueing of worker checkouts,
   it is not suitable as a proper job queue. If you expect to peaks in your load
   leading to a high level of concurrent use of your PDF printing component,
   a job queue like Oban will provide a better experience.

The error message suggests Oban. However, I’m not sure Oban is the best option for a couple reasons:

  1. Oban isn’t designed for the caller to block awaiting the result of the job. However, it does have the concept of a Notifier which can be used to return the pdf to the controller process to send back to the user.
  2. I would prefer not to rebuild the conn and assigns within the job because I’m reusing some functions in my controller. But I also don’t want to pass the results of ChromicPDF.Template.source_and_options/1 to the job directly.

The other options I’ve looked at are:

Any suggestions for the best option to queue the pdf printing work when I don’t want the work to be done in the background?

Marked As Solved

axelclark

axelclark

I upgraded to ChromicPDF version 1.14 and updated the :session_pool :timeout and :checkout_timeout to match what I had in poolboy. ChromicPDF and NimblePool handle my concurrency test just like poolboy.

For anyone interested in the updated ChromicPDF config options, check out the session pool docs.

Also Liked

akoutmos

akoutmos

Author of Build a Weather Station with Elixir and Nerves

Currently on mobile, so I can add more info when I am in front of a computer. I make heavy use of ChromicPDF in eaglemms.com so that customers can generate invoices. I think I used to have issues similar to what you are describing so I put poolboy in front of ChromicPDF and haven’t had any issues. I also do something similar to what you are describing where a controller generates the PDF on demand versus generating and storing it in S3 or something and poolboy+ChromicPDF has no problem keeping up (at least for my current customer load).

sorentwo

sorentwo

Oban Core Team

Using a dedicated pooler is a great option.

As an alternative for others in the future, there is a blocking option in Oban called Relay, like a transparently distributed task with concurrency limits (Pro only though).

maltoe

maltoe

Hi, author of ChromicPDF here.

I’m currently scratching my head a bit as the accepted solution in this thread does not make sense to me (putting a worker pool in front of a worker pool), yet two people independently report good experience with it :slight_smile: - I wonder if this points to an obscure bug in Chromic, or merely to an opportunity to improve its documentation.


@axelclark Let me first address your original post. I believe the docs you cited led to some confusion, rather than being helpful. Especially this line:

Please note that this is a non-queueing session pool.

This is definitely misleading. I should have written that it is not a persistently queueing session pool. The NimblePool library used in Chromic’s SessionPool does in fact queue “worker checkout” commands (by virtue of OTP message boxes & genserver). But its queue isn’t persistent, as in, if you overwhelm your system, worker checkouts will timeout and requests will be dropped; which leads to the exception you have posted. That’s also perfectly fine depending on your use-case, if you expect only few concurrent requests, or if you’re fine with the occassional failure when you exceed the pool concurrency, there’s nothing you need to do about it at all :slightly_smiling_face:.

In other words, what I meant to say in the docs is this: If you expect peaks in concurrent demand (= greater than max throughput of your system) and you are required to handle them gracefully, you must begin to write requests down for later and asynchronously process them (e.g. with a job queue like Oban, backed by a database table, and so on). This is actually not specific to ChromicPDF in any way, just generally job processing. And vice versa, if your scenario does not have peaks or you don’t care about them, that’s 100% cool, too, and you don’t need any of this.

I probably should just ditch that statement fromt the docs.


Now, with regards to putting poolboy in front of it: I’m honestly curious how that would change anything in your test setup? You should now get a poolboy exception instead of ChromicPDF’s when you overwhelm your system. Even the default checkout timeout (think, “max queue wait time”) is the same, I believe, at 5 seconds. Question goes to @akoutmos , too, of course.


Fun fact: ChromicPDF used to depend on poolboy before v0.6.0. Which is when I realized that a process pool in front of a single connection process is pointless. NimblePool eliminates the worker processes and only pools “resources”.

Last Post!

akoutmos

akoutmos

Author of Build a Weather Station with Elixir and Nerves

Thanks for the reply! Looking at the git blame for the pooling code that I wrote…it looks like I wrote it in December 2021. Not sure what version of Chromic I was using back then, but I do recall putting poolboy in front of Chromic fixing the issues that we were having when we had too many requests come in to render PDFs.

It sounds like I will no longer need this pool logic though :tada:. Thanks for the explanation and the great library!

Where Next?

Popular in Questions Top

RisingFromAshes
I’ve read in another post that it may be possible with a router helper - but I couldn’t find an appropriate one, and tbh, I’m still just ...
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" => #BSON.ObjectId<58eb1a7a9ad169198c3dXXXX>, "email" => ...
New
hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a > b) do {:ok, "a"} end if (a < b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New

Other popular topics Top

Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New
axelson
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!) This post collects co...
239 49084 226
New
saif
Hello everyone, Long time lurker first time poster here. I’ve recently begun working on Elixir full-time again! :raised_hands: It’s been...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New

We're in Beta

About us Mission Statement