自分のための備忘録です。
seed-fuを使ってデータ投入していたのだが、データを作り直そうと思ってseed-fuを再び実行したところ、minioのバケットを作るところでコケた。原因は、既にバケットがあったからだった。
require 'aws-sdk-s3' storage_yml = YAML.safe_load(ERB.new(File.read(Rails.root.join('config', 'storage.yml'))).result).fetch('minio') bucket_name = storage_yml.delete('bucket') # 既にバケットがある場合はNGだった… Aws::S3::Client.new( access_key_id: storage_yml['access_key_id'], secret_access_key: storage_yml['secret_access_key'], region: storage_yml['region'], endpoint: storage_yml['endpoint'], force_path_style: storage_yml['force_path_style'] ).tap { |s3_client| s3_client.create_bucket(bucket: bucket_name) }
なので、AWS SDK for Rubyを使ってバケットを削除しようとしたのだが、またコケた…。バケットにファイルが残っていたらバケットは消せないらしい。ファイルごと消せるかと思いきや、現時点ではそんなオプションはなかった。
# 省略 ).tap do |s3_client| # バケットにファイルがある場合は削除できず… if s3_client.list_buckets.buckets.any? { |bucket| bucket.name == bucket_name } s3_client.delete_bucket(bucket: bucket_name) end s3_client.create_bucket(bucket: bucket_name) end
なので!バケットにあるファイルを全部削除してからバケットを削除しようとしたのだが、またコケた!
# 省略 ).tap do |s3_client| if s3_client.list_buckets.buckets.any? { |bucket| bucket.name == bucket_name } # バケット内のファイルを削除 s3_client.list_objects(bucket: bucket_name).contents.each do |object| s3_client.delete_object(bucket: bucket_name, key: object.key) end # 何故かまだバケットが削除できない!? s3_client.delete_bucket(bucket: bucket_name) end s3_client.create_bucket(bucket: bucket_name) end
削除できない原因
原因はシンプルで、まだバケットにファイルが残っていたからだった。list_objects
メソッドで取得できるオブジェクトの数の上限が1,000だったので、1,000個以上ファイルがバケットにある場合は消しきれない。ということは、ファイルを全部消すまでループするようにしなければならない。
削除できるように直す
最初の実装
シンプルにこうした。
# 省略 ).tap do |s3_client| if s3_client.list_buckets.buckets.any? { |bucket| bucket.name == bucket_name } # コンテンツが有る限り削除し続ける while s3_client.list_objects(bucket: bucket_name).contents.present? s3_client.list_objects(bucket: bucket_name).contents.each do |object| s3_client.delete_object(bucket: bucket_name, key: object.key) end end s3_client.delete_bucket(bucket: bucket_name) end s3_client.create_bucket(bucket: bucket_name) end
しかし、これだとファイルが大量にある場合はminioに都度ファイルの有無を確認する通信が発生するので、ちょっとダサいなと感じた。
リファクタリング後
begin .. end while
を使って書き直した。これならば、minioに通信する回数は最小限となって効率がよい。
# 省略 ).tap do |s3_client| if s3_client.list_buckets.buckets.any? { |bucket| bucket.name == bucket_name } contents = nil begin contents&.each do |object| s3_client.delete_object(bucket: bucket_name, key: object.key) end contents = s3_client.list_objects(bucket: bucket_name).contents end while contents.present? s3_client.delete_bucket(bucket: bucket_name) end s3_client.create_bucket(bucket: bucket_name) end
まとめ
- バケットを消すにはファイルを全削除しなければならない
- list_objectsメソッドは1,000件しか取れないので注意
- AWS CLIだったらファイルごとバケットを削除可能らしい(同僚に教えてもらった)