こんにちは。ざわかける!のざわ(@zw_kakeru)です。
AWS Glueでジョブでの実行結果を、分割せずにs3へ格納する方法を記述していきます。
また、分割をする場合も任意のファイル数を指定することが可能となります。
動作環境
AWS Glue : 0.9
Spark : 2.2.1
Python : 2.7
やりたいこと
AWS Glueでジョブを実行してどこかしら(今回はs3とします)へ結果を出力すると、その結果は複数のファイルに分割されて出てきます。

私の環境ではデフォルトで20分割される設定になっていたようです。
これは、AWSは全般的に数テラ規模のデータを扱うことを想定しており、その際にデータを複数ファイルに分割する方がパフォーマンスの面で安定するからであるとAWS公式ブログに記述があります。
しかしそこまで大規模なデータを使用するのでなければ無駄に分割されると返って扱いにくくなってしまうため、何とかして1ファイルにまとめて出力してあげたいですね。
解決策
Glueジョブのスクリプトを直接書き換えました。
まず、s3への出力部分のスクリプト(変更前)は以下の通りでした。
DataSink0 = glueContext.write_dynamic_frame.from_options(frame = Transform0, connection_type = "s3", format = "json", connection_options = {"path": "s3:/hogehoge/", "partitionKeys": []}, transformation_ctx = "DataSink0")
これを、次のように書き換えました。
DataSink0 = glueContext.write_dynamic_frame.from_options(frame = Transform0.repartition(1), connection_type = "s3", format = "json", connection_options = {"path": "s3:/hogehoge/", "partitionKeys": [], 'groupSize': '10485760'}, transformation_ctx = "DataSink0")
変更点は次の2箇所です。
- s3への接続オプションに‘groupSize’: ‘10485760’を追加しました。(groupSizeは「一つのファイルサイズがこの値を超えると別ファイルに分割する」という上限値で、デフォルトは1Mみたいです。)
- Transform0.repartition(1)で指定したファイル数(一つ)でパーティションを再設定してs3へ格納しました。
このようにして実行すると、無事1つのファイルで出力されました。

終わりに
repartitionといういかにもファイル数操作に関連しそうなメソッドがあったため割とあっさり実現することができました。
しかし、repartitionの他にcoalesceという関数も用意されており、ドキュメントを読んだ限りだと両者の違いが分かりませんでした。
それぞれ必要だから存在するのだとは思います(実際にTransform0.coalesce(1)として実行しても同様に動きました)が、今回の場合はどちらを使っても良かったということなのでしょう。