I have an app that processes images and creates PDFs in the background using Delayed Job. The images are being processed using Process.spawn
(through the subexec gem) which runs a GraphicsMagick process. We are then creating a PDF file using the Prawn gem, which includes these images and text components. I don't believe that the Prawn gem using Fork or Spawn. We are using Ruby 1.9.3. What happens is that our Delayed Job processes balloons from ~120MB to over 800MB of memory after a few PDF files are made.
I know that the spawned GraphicsMagick processes share memory with the parent process, but is that memory given back to the system after the child processes are finished? If I created the PDF files in a forked process, would the memory used in creating the PDF file be returned to the system after that forked process was completed?
I went ahead and forked the PDF creation tasks and tested it out. The forked processes do indeed release the memory back to the system after they exit, and our Delayed Job processes no longer balloon out of control. I have attached the code in case anyone is facing a similar problem. Interestingly enough, running Ruby 1.9.3 + Rails 3.2.x, we don't need to reconnect to the database in either the forked processes or the parent process. There was much debate about this in other Stackoverflow questions.
def run_in_fork
read, write = IO.pipe
pid = fork do
error = nil
read.close
begin
yield
rescue => e
error = e
end
Marshal.dump(error, write)
exit!(0) # skips exit handlers.
end
write.close
result = read.read
Process.wait(pid)
raise "Child process failed" if result.empty?
if exception = Marshal.load(result)
raise exception
end
return true
end
def my_method
object = MyObject.find.first
run_in_fork do
object.long_running_process
end
object.reload
end