Python3+Django内でZIPファイルを生成する処理で、複数ユーザーが同時に実行すると、一人が成功し、他が失敗するよくわからない状態になり、更にまずいことに成功したユーザーのZIPには他のユーザーのファイルが入ってしまっていた。
処理は下記のようになっていた。
最初はtempfile.TemporaryDirectory()が原因かと思ったけど、そんな雑な作りをしているわけはなく、原因はos.chdirだった。
一部抜粋(動かない)
with TemporaryDirectory() as temp_dir: os.chdir(temp_dir) subprocess.run(['zip', '-r', file_name, "./"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True) temp_zip = File(open(zip_name, "rb")) temp_zip.seek(0) return {'name': zip_name, 'content': temp_zip.read()}
os.chdirで変更するカレントディレクトリはスレッドで共有しているため、同時に処理すると順不同だが下記のように処理されてしまっていたと推測
os.chdir('/tmp/aaaa') #一人目 os.chdir('/tmp/bbbb') #二人目 ここで上書きされる subprocess.run(['zip', '-r', file_name, "./"], #一人目 ./は'/tmp/bbbb' subprocess.run(['zip', '-r', file_name, "./"], #二人目 ./は同じく'/tmp/bbbb'に もうファイルがなくなっておりエラー
zipコマンドの対象ディレクトリに./と、カレントディレクトリを使ってしまったのが諸悪の根源だと思われたので、全部temp_dir込のフルパスを使うように修正すればいいと思った。
が、できたzipファイルにtmpディレクトリが入ってしまうので素直に標準ライブラリのzipfileを使う方向に考え中。
subprocess.run(['zip', '-r', file_name, temp_dir],
そもそもなぜわざわざpython標準ライブラリのzipfileを使わずに、subprocess.runでOSのコマンドを叩いているのかと思ったけど、そういえばzipfileはパスワード付きのzipの作成ができなかったから。展開はもちろんできる。
スレッドとか、並列実行はテストが難しいなと実感。
あとなるべくos系は使わないことと、どうしても使うときはスレッドセーフかどうかを確認が必要だと思った。
13.5. zipfile — ZIP アーカイブの処理 — Python 3.6.5 ドキュメント
独学プログラマー Python言語の基本から仕事のやり方まで
- 作者: コーリー・アルソフ,清水川貴之監訳,清水川貴之,新木雅也
- 出版社/メーカー: 日経BP社
- 発売日: 2018/02/24
- メディア: 単行本
- この商品を含むブログ (1件) を見る
退屈なことはPythonにやらせよう ―ノンプログラマーにもできる自動化処理プログラミング
- 作者: Al Sweigart,相川愛三
- 出版社/メーカー: オライリージャパン
- 発売日: 2017/06/03
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (5件) を見る