概要
Amazon S3の特定キーを持つオブジェクトの合計サイズを求めたい場合にこうすると多少早く取得できそうというメモです。
現状、boto3を利用して合計サイズを求めたい場合のベストプラクティスだと思います。このほかにより良い手法がありましたら、コメントください。
課題
特定のキー(プレフィックス)を持つ(ここでは、4/device=10/dt=2021-05-10
)を持つオブジェクトの合計サイズを算出したいとします。
> aws s3 ls kyb-iot-processed-dev/4/device=10/dt=2021-05-10/ --recursive --human-readable
2021-05-10 01:10:06 219 Bytes 4/device=10/dt=2021-05-10/20210510_000005_JNSA_RL01A_DGEA004_10_4.csv
2021-05-10 01:10:06 219 Bytes 4/device=10/dt=2021-05-10/20210510_000105_JNSA_RL01A_DGEA004_10_4.csv
2021-05-10 01:10:06 219 Bytes 4/device=10/dt=2021-05-10/20210510_000205_JNSA_RL01A_DGEA004_10_4.csv
2021-05-10 01:10:06 219 Bytes 4/device=10/dt=2021-05-10/20210510_000305_JNSA_RL01A_DGEA004_10_4.csv
2021-05-10 01:10:06 219 Bytes 4/device=10/dt=2021-05-10/20210510_000405_JNSA_RL01A_DGEA004_10_4.csv
2021-05-10 01:10:07 219 Bytes 4/device=10/dt=2021-05-10/20210510_000505_JNSA_RL01A_DGEA004_10_4.csv
2021-05-10 01:10:07 219 Bytes 4/device=10/dt=2021-05-10/20210510_000605_JNSA_RL01A_DGEA004_10_4.csv
...
AWS Data Wranglerにて提供されているawswrangler.s3.size_objectsにて合計サイズを求めようとしましたが、対象オブジェクト数が多いと非常に長い処理時間がかかりました。数十オブジェクトでは対応できそうだが、1000を超えてくると別の手段を用いたほうがいいです。 おそらく、head_objectで各オブジェクトごとに情報を取得しているのが原因だと思います。
boto3にて提供されているbucket.objects.all()を利用することも可能ですが、いろいろ問題があるようです。(AWS Lambda Python S3でフォルダ以下のファイル一覧を取得する)
また、bucket.objects.all()の利用は非推奨です。
そこで、list_objects_v2を利用して計算します。
解決策
以下にオブジェクトの合計サイズを算出するサンプルコードを記載します。
list_objects_v2は一回に読み込めるオブジェクト数が1000なのですが、レスポンスにIsTruncated
がありすべてのオブジェクトが返されたかを判定することが可能です。また、リクエストを行う際、StartAfter
にオブジェクトキーを指定することで、指定したオブジェクト以降に絞った読み込みを行うことができます。これらを利用することにより1000以上のオブジェクトに対応可能です。
import boto3
import time
client_s3 = boto3.client('s3')
def calculate_sum_of_object(
bucket_name: str,
prefix: str,
):
start_after = ''
size = 0
object_num = 0
while True:
param = {
'Bucket': bucket_name,
'Prefix': prefix,
'StartAfter': start_after
}
res = client_s3.list_objects_v2(**param)
if 'Contents' in res:
object_num += len(res['Contents'])
for content in res['Contents']:
size += content['Size']
start_after = content['Key']
if res['IsTruncated'] is False:
break
return size, object_num
if __name__ == '__main__':
size, object_num = calculate_sum_of_object('kyb-iot-processed-dev', '4/device=10/dt=2021-05-10')
print(f'Total size of objects with same prefix: {size / 1024} [KB]')
print(f'Total objects: {object_num}')
# Total size of objects with same prefix: 303.0498046875 [KB]
# Total object: 1417
まとめ
AWS Data Wranglerにて特定のプレフィックス配下のオブジェクトサイズ合計算出がうまくできなかったので、代替え案を検証しました。