Add inherit_fds: so a loader can name an already-open input - #147
Conversation
A caller holding an open file had no way to have the tool read it in place. The one route that carried a descriptor, handing `source` a pre-built `MiniMagick::Tool`, dropped `loader`, `page` and `geometry` without a word. Accept `inherit_fds:` as a loader option and give it to the tool at construction, so the source stays a `/dev/fd/N` path and every other loader option still applies. mini_magick added the option in 5.4.0, so raise `LoadError` naming that version when an older one is loaded.
|
Thanks for that PR! I see you added tests, so it seems like it works. I'm just curious, how does the |
|
@janko Thanks for taking a look. You're right that In Does that answer your question? I'm pretty tired right now, so please tell me if I misunderstood and I'm happy to try to explain more clearly. |
|
Makes sense, thanks for the explanation. And how will this be usable from Active Storage? You don't have access to the raw file at the time of the |
|
Nevermind, Active Storage transformers have access to the raw file, so you could do the plumbing there. Either way it makes sense, as HotCell can be used directly with ImageProcessing gem, from what I understand. |
Motivation / Background
I maintain HotCell, which runs Active Storage's image processing inside a container. The application hands that container its input as an already-open file descriptor, and caps what the container may write with
RLIMIT_FSIZE.Two of HotCell's operations use
ImageProcessing::MiniMagick, and both have to copy that input out to a file first, because the pipeline names its source by path and a descriptor handed over a socket has none. The copy is a write, so the cap bounds what those two operations can read as well as what they write, and an input larger than the cap fails permanently before any processing happens. The libvips operations never had that ceiling, because they read the descriptor in place.minimagick#605, released in 5.4.0, added
inherit_fds:toMiniMagick::Shell#execute. It names IO objects inProcess.spawn's redirect map, so the child inherits those descriptors and the command can be given/dev/fd/Nin place of a filename.ImageProcessing::MiniMagickconstructs theMiniMagick::Toolitself, so a caller has no way to reach that option.Detail
This Pull Request adds
inherit_fds:as a loader option and passes it to the tool at construction. The source stays a/dev/fd/Npath, so every other loader option still applies to it.mini_magickis a soft dependency and theGemfileadmits>= 4.9.5, so a caller can easily be on a version withoutinherit_fds:. Handing the option to one of those raisesArgumentError: unknown keyword: :inherit_fdsfrom insideMiniMagick::Shell#execute, which tells the developer nothing about what to do.ImageProcessing::MiniMagick.convert_shimnow checks the version first and raisesLoadError, matching theLoadErrorthis file already raises whenmini_magickis missing altogether:inherit_fds:defaults toniland is left out of the tool's arguments when it is not given, so nothing changes for existing callers and an oldermini_magickis never handed the keyword.The suite passes with no skips on
mini_magick5.4.0, 5.3.3 and 4.13.2. The descriptor tests run on 5.4.0 and newer; on anything older, the same pipeline is asserted to raise the upgrade error naming the version actually installed.Additional information
The option name is
mini_magick's, taken there fromProcess.spawn's "File Descriptor Inheritance".This is the input side only. The output side cannot be handled the same way, because the pipeline picks its saver from the destination's extension and a
/dev/fdpath has none.A pre-built
MiniMagick::Toolsource still ignoresinherit_fds:, exactly as it already ignoresloader,pageandgeometry. I left that alone rather than makinginherit_fds:the one option that raises there. If you would rather that branch rejected loader options outright, that seems worth doing for all four at once, and I am happy to follow up./dev/fdis not available on Windows; the doc note says so.