Errno::ENOENT: No such file or directory @ dir_chdir

969 views Asked by At

I have a model Report to export all the Pdf attachments done for the bills. I have other models Bill, Upload.

Bill has many uploads

Tech Stack used

Ruby 2.5.8 Rails 5.0.7

Dummy Code

  require 'fileutils'
  require 'zip'
  require 'open-uri'

  class Report < ApplicationRecord
    
    def to_a  

      main_dir = "tmp/Attachment-#{self.id}"

      FileUtils::mkdir_p main_dir

      bills.each do |bill|
        bill_directory_name = "Bill_#{bill.id}"

        bill.uploads.each do |bill_upload|
            FileUtils::cd main_dir do
              FileUtils::mkdir_p "#{bill_directory_name}"

              file = open(bill_upload.file.url, 'rb')

              upload_name = "Invoice-#{bill_upload.file_file_name}-#{bill_upload.id}"
              FileUtils::cd "#{bill_directory_name}" do
                IO.copy_stream(file, "#{upload_name}")
              end
           end
        end
      end

      path = "tmp/Attachment-#{self.id}"
      archive = File.join(path, "Attachment-#{self.id}" + '.zip')
      FileUtils.rm archive, force: true

      Zip::File::open(archive, 'w') do |zipfile|
        Dir["#{path}/**/**"].reject{|f| f==archive }.each do |file|
          zipfile.add(file.sub(path + '/', ''), file)
        end
      end
    end
 end

Task

I am creating main directory tmp/Attachment-#{self.id}. Inside it, I am creating multiple billing directories (bill_directory_name) for the bills. And inside each of the billing directories I am including uploaded invoice after reading actual invoice saved inside upload model. And Final zip folder is created, archived and downloaded.

Problem

When I execute my code only once, it works perfect.

But if I execute it multiple times in multi threading environment then it says No such file or directory.

Following are the errors

/Users/siv/.rbenv/versions/2.5.8/lib/ruby/2.5.0/fileutils.rb:122: warning: conflicting chdir during another chdir block

WARN: Errno::ENOENT: No such file or directory @ dir_chdir - test-1111-Sup-14-invoice

WARN: /Users/siv/.rbenv/versions/2.5.8/lib/ruby/2.5.0/fileutils.rb:122:in `chdir'

/Users/siv/.rbenv/versions/2.5.8/lib/ruby/2.5.0/fileutils.rb:122:in `cd'

/Users/siv/proj/app/models/report.rb:46:in `block in to_a'

Error in this line: bill.uploads.each do |bill_upload|

/Users/siv/proj/app/models/report.rb:48:in `block (2 levels) in to_a'

Error in this line: FileUtils::cd main_dir do

/Users/siv/script/proj/app/models/report.rb:55:in `block (3 levels) in to_a'

Error in this line: FileUtils::cd "#{bill_directory_name}" do

Any feedback will be appreciated.

1

There are 1 answers

2
nPn On

I believe FileUtils makes use of Dir for the chdir/cd method, and while it may not be documented in FileUtils, it is documented in Dir that cd/chdir is not thread safe.

If a block is given, it is passed the name of the new current directory, and the block is executed with that as the current directory. The original working directory is restored when the block exits. The return value of chdir is the value of the block. chdir blocks can be nested, but in a multi-threaded program an error will be raised if a thread attempts to open a chdir block while another thread has one open or a call to chdir without a block occurs inside a block passed to chdir (even in the same thread).

https://ruby-doc.org/core-3.1.2/Dir.html#method-c-chdir