こんにちは。CTOの馬場です。
できるようになった、というのは見たものの、 具体的にどのように実現しているのか気になったので調べてみました。
こういうの、気になりません?わたしは気になります。
最近はコードの追い方の紹介をあまり見ないので、探し方を含めて紹介します。
なお30代後半以降の方にはお馴染みの、
find ... | xargs grep を使っていますが、
GitHubの検索機能でも代替可能です。
- LeakyBucketです
- aws-cliではなくbotoで実装されています
ここからは地道に追ってみます。 読むだけだと何がなんだか…だと思うので、 操作したりリンクに飛んだりしながら試してみてください。
※jediやVSCodeで定義にジャンプできる環境がある場合はインストールしてジャンプしていけばもっと楽に追えると思います
下準備
まずはawscliのソースコードを落としてきます。
❯❯ ~/Documents/project/github.com/aws
❯ git <span class="hljs-built_in">clone</span> git@github.com:aws/aws-cli.git
...
❯❯ ~/Documents/project/github.com/aws
❯ <span class="hljs-built_in">cd</span> aws-cli
❯❯ (git:develop) ~/Documents/project/github.com/aws/aws-cli
❯ git checkout <span class="hljs-number">1.14</span>.<span class="hljs-number">39</span>
...
とっかかり
設定項目が s3.max_bandwidth なので、
aws-cliのリポジトリで max_bandwidth を探します。
❯❯ (git:<span class="hljs-number">1.14</span>.<span class="hljs-number">39</span>) ~/Documents/project/github.com/aws/aws-cli
❯ find awscli -type f -name <span class="hljs-string">"*.py"</span> | xargs grep -Hnw max_bandwidth
awscli/customizations/s3/transferconfig.py:<span class="hljs-number">25</span>: <span class="hljs-string">'max_bandwidth'</span>: None
awscli/customizations/s3/transferconfig.py:<span class="hljs-number">37</span>: <span class="hljs-string">'max_bandwidth'</span>]
awscli/customizations/s3/transferconfig.py:<span class="hljs-number">39</span>: HUMAN_READABLE_RATES = [<span class="hljs-string">'max_bandwidth'</span>]
awscli/customizations/s3/transferconfig.py:<span class="hljs-number">112</span>: <span class="hljs-string">'max_bandwidth'</span>: <span class="hljs-string">'max_bandwidth'</span>,
create_transfer_config_from_runtime_config 内で
TransferConfig を生成する際に利用されているようです。
呼び出し元を探す
この create_transfer_config_from_runtime_config を呼んでいるのはどこでしょう?
❯❯ (git:<span class="hljs-number">1.14</span>.<span class="hljs-number">39</span>) ~/Documents/project/github.com/aws/aws-cli
❯ find awscli -type f -name <span class="hljs-string">"*.py"</span> | xargs grep -Hnw create_transfer_config_from_runtime_config
awscli/customizations/s3/s3handler.py:<span class="hljs-number">22</span>: create_transfer_config_from_runtime_config
awscli/customizations/s3/s3handler.py:<span class="hljs-number">83</span>: transfer_config = create_transfer_config_from_runtime_config(
awscli/customizations/s3/transferconfig.py:<span class="hljs-number">97</span>:def create_transfer_config_from_runtime_config(runtime_config):
transferconfig.py は先程のものなので、 s3handler.py があたりです。
ここでは transfer_configを生成しているようです。
この生成された transfer_config は TransferManager で利用されます。
定義元を探す
TransferManager とは何でしょう?
from s3transfer.manager import TransferManager
を見ると s3transfer 配下です。
awscliではなく外部ライブラリのようです。
外部ライブラリを探す
依存している外部パッケージはsetup.pyに記載しておくことでインストール時にpipにて解決されます。
setup.py を見てみましょう。
s3transfer というそのままの名前のパッケージがありました。
PyPIを探す
外部パッケージはPyPIにあるので、そこでs3transferを探します。
https://pypi.python.org/pypi/s3transfer/
Home Page: https://github.com/boto/s3transfer
https://github.com/boto/s3transfer にコードがあるようです。
外部ライブラリの定義元を探す
boto/s3transferのリポジトリを見てみましょう。
❯❯ ~/Documents/project/github.com/boto
❯ git <span class="hljs-built_in">clone</span> git@github.com:boto/s3transfer.git
...
❯❯ ~/Documents/project/github.com/boto
❯ <span class="hljs-built_in">cd</span> s3transfer
❯❯ (git:develop) ~/Documents/project/github.com/boto/s3transfer
❯ git checkout <span class="hljs-number">0.1</span>.<span class="hljs-number">13</span>
...
❯❯ (git:<span class="hljs-number">0.1</span>.<span class="hljs-number">13</span>) ~/Documents/project/github.com/boto/s3transfer
❯ grep -n TransferManager s3transfer/manager.py
<span class="hljs-number">69</span>: processing a call to a TransferManager method. Processing a
<span class="hljs-number">84</span>: TransferManager method calls that can be queued at a time. A value
<span class="hljs-number">156</span>:class TransferManager(object):
<span class="hljs-number">535</span>: <span class="hljs-string">""</span><span class="hljs-string">"Shutdown the TransferManager
</span>
TransferManagerの定義がありました。
処理を追う
コンストラクタの中でmax_bandwidthを含むtransfer_config(だったもの)が self._config として保持されています。
コンストラクタを下に読み進めると、なんと max_bandwidth が利用されています。
leaky_bucket = LeakyBucket(self._config.max_bandwidth)
self._bandwidth_limiter = BandwidthLimiter(leaky_bucket)
これっぽい
帯域などの制限でよくある leaky bucket (水漏れバケツ) 方式のようですね。 めでたしめでたし。
あとはコードを読んで定義にジャンプしていけば大丈夫です。 このような流れで追っていく形になると思います。
- https://github.com/boto/s3transfer/blob/0.1.13/s3transfer/bandwidth.py#L92-L94
- https://github.com/boto/s3transfer/blob/0.1.13/s3transfer/bandwidth.py#L138-L156
- https://github.com/boto/s3transfer/blob/0.1.13/s3transfer/bandwidth.py#L158-L173
- https://github.com/boto/s3transfer/blob/0.1.13/s3transfer/bandwidth.py#L237-L264
さらにさらに深追いするにはこのあたりを読むことになると思います。
- https://github.com/boto/s3transfer/blob/0.1.13/s3transfer/bandwidth.py#L288
- https://github.com/boto/s3transfer/blob/0.1.13/s3transfer/bandwidth.py#L341
単にユーザとして使ってるだけだと仕組みも何もわからなくて不安ですよね? せっかくコードが公開されているので、活用してきちんと自分の掌に収めていきましょう。