Tech Do | メディアドゥの技術ブログ 

株式会社メディアドゥのエンジニアによるブログです。

ブロックチェーンサービス徹底高速化

f:id:masashi_kutsuna:20201007120721j:plain

モチベーション

メディアドゥでは、デジタルコンテンツをより安全かつ楽しみながら利用できる環境の実現のために、アクセス権管理やユーザアクティビティをブロックチェーンで管理することのできる基盤の開発を進めております。

最近では、そのブロックチェーン基盤にAmazon Managed Blockchainサービスを正式に採用し、サービス活用企業の1社として、サービスサイトにおいて会社ロゴの掲載も行っていただきました。

mediado.jp

ブロックチェーンの特徴はいくつかのユースケースにおいて有用ですが、デジタルコンテンツ市場に基盤としてブロックチェーンを活用する上では、 少なくともピーク時2,000TPS(瞬間的にはその倍以上のスパイク)以上となるリクエストボリュームに対して十分に耐えられる設計が必要となります。

ここでは、我々がどのようにしてブロックチェーン基盤を実用可能なレベルまでパフォーマンス改善したのか、その一部について解説いたします。

※TPS = 一秒間に記録できたStateの数とします。

サービス条件

  • 2,000(瞬間最大4,000)TPS以上を達成できる仕組みであること。
  • ユーザリクエストは平均200msec以内にレスポンスすること。
  • ブロックチェーンへの書き込み自体はリクエストから15分以内に反映。

結果

まず結論から、バルク処理を採用することによって2,000(瞬間最大4,000)TPS以上のパフォーマンスでデータがブロックチェーン上に書き込まれることを実現できました。

以下、構成・方式別のパフォーマンス測定結果です。

方式/構成別スループット

No.処理単位構成TPS
11レコード単位1Member 1Peer (bc.m5.large)160
21レコード単位1Member 1Peer (bc.c5.xlarge)360
31,000レコード単位1member 2Peers(bc.c5.xlarge)3,600
41,000レコード単位2members 4Peers(bc.c5.xlarge) / 2 peers in each member3,800(Peak: 4,800)

アーキテクチャ

ユーザからのリクエストはALB or API Gatewayで一次受けして、その要求をDynamoDBに記録・応答させることで高速なレスポンスを実現。

Blockchain側への書き出しはDynamo Streamのバッチサイズを調整することによって最大1,000レコードを1ブロックにまとめて書き出すことでスループットを最大化させる構成としました。

f:id:masashi_kutsuna:20200916163647p:plain
architecture-overview

パフォーマンス改善のための要素

Amazon Managed Blockchain上において、パフォーマンスを改善するために最も効果的なアプローチはバルク処理化ですが、それ以外でもいくつかのポイントがあります。

以下、列挙します。

  1. MSP(CA)の認証プロセスはキャッシュを活用しスキップさせる。
  2. ブロックチェーンレイヤーにはバルクで書き込む。
  3. PeerのCPUリソースが重要。
  4. ブロックチェーンレイヤーへのバルク書き込みは、並列性を重視するよりもバルク処理あたりのレコード件数を重視する。

ロードテストによるTPS確認

ここからは、実際に2,000TPS以上のスループットを実現するまでの調査・考察についてまとめていきます。

ロードテストのための準備作業

今回のテストだけでなく、継続的にロードテストを実現するために大きなリクエストボリュームをいつでも再現できる必要がありました。

そのため、Lambda + Fargate + JMeterをベースに、次のようなロードテストスクリプトを準備しました。

f:id:masashi_kutsuna:20200916164123p:plain
loadtesting-framework

スケールアップによるパフォーマンス改善調査

1件ずつの書き込みを実施した場合[1回目 1Member 1Peer(bc.m5.large)]

最初の書き込みテストでは、20TPSにも満たないボリュームでchaincode endpoint側のCPUリソースが食い潰され、

どれだけchaincode endpoint側のスケールアウトを実施しようともスループットの向上が見込めない結果となりました。

[ chaincode endpointのCPU、メモリ使用率 ] f:id:masashi_kutsuna:20200916164831p:plain

[ peerのCPU、メモリ使用率 ] f:id:masashi_kutsuna:20200916164912p:plain

chaincode endpoint側のログを確認したところCAサーバへの認証リクエストがタイムアウトでリセットされていることを確認したため、

リクエスト毎に行っていたCAサーバへのenroll処理がボトルネックだと判明。enroll後のChannelClientをキャッシュすることでコストを抑える仕組みを採用することにしました。

1件ずつの書き込みを実施した場合[2回目 1Member 1Peer(bc.m5.large)]

chaincode endpoint側で、アクセスユーザ毎のChannelClientをキャッシュすることにより、CAサーバへのボトルネックを解消した状態でロードテストを再開。

結果としてTPSは160TPS前後まで向上。CAサーバ側のボトルネックが解消され、Peer側のCPUリソース枯渇によるスループットの頭打ちが確認できました。

[ chaincode endpointのCPU、メモリ使用率 ] f:id:masashi_kutsuna:20200916165057p:plain

[ peerのCPU、メモリ使用率 ] f:id:masashi_kutsuna:20200916165129p:plain

1件ずつの書き込みを実施した場合[​3回目 1Member 1Peer(bc.c5.xlarge)​]

PeerのCPUリソースが枯渇することによるスループットの頭打ちがみられたとき、メモリ使用量自体は安定していることからPeerのインスタンスタイプをbc.m5large(vCPU:2) ->bc.c5.xlarge(vCPU:4)にスケールアップすることでTPS改善を図りました。

結果として160TPS -> 360TPSの改善が見られ、その時点でPeer側のCPUが90%近くまで上昇し、頭打ちとなりました。

[ chaincode endpointのCPU、メモリ使用率 ] f:id:masashi_kutsuna:20200916232958p:plain

[ peerのCPU、メモリ使用率 ] f:id:masashi_kutsuna:20200916233022p:plain

結果からの考察

Hyperledger Fabricの仕様上、一秒間に取り扱うことのできるトランザクション(ブロック)数には限界があります。これはスケールアップを行ったとしても一定以上の効果を達成することは困難です。

Hyperledger Fabricは一連の処理の流れでもブロック生成にかかる処理コストが支配的であると考えられるため、一つのトランザクション内にて複数のステートを更新するバルク処理の採用により、スループットの向上が達成できるかを検証しました。

バルク書き込みのための準備作業

最大1,000ステートをバルクでBlockchain側に書き出す仕組みとして、DynamoDB streamを採用。一旦書き出しの要求をDynamodbで受け、それをlambdaで受信してBlockchain側に送りつける仕組みとしました。

f:id:masashi_kutsuna:20200916163647p:plain
architecture-overview

データ書き込みのバルク化によるパフォーマンス改善調査

バルク書き込みの検証[1回目 1Member 2Peers(bc.c5.xlarge)]

バルク書き込みに変更したところ、1件ずつ書き出すパターンと比較して10倍近くとなる3,600TPSを達成。

仮説の通り、更新されるStateの数ではなく、1ブロックを生成する処理コストが支配的であったと判断しました。

[ chaincode endpointのCPU、メモリ使用率 ] f:id:masashi_kutsuna:20200916182728p:plain

[ peerのCPU、メモリ使用率 ] f:id:masashi_kutsuna:20200916182804p:plain

バルク書き込み検証[2回目 2Members 4Peers(bc.c5.xlarge)]

MemberおよびPeerを追加して同様の負荷をかけたところ、3,800TPS程度を達成。

Member追加、Peer追加がパフォーマンスに悪影響を及ぼすことはなく、むしろパフォーマンスは少し上振れする結果となりました。計測内容から、リーダーPeerの役割が分散されたことによる効果と思われます。

[ chaincode endpointのCPU、メモリ使用率 ] f:id:masashi_kutsuna:20200916182848p:plain

[ peerのCPU、メモリ使用率 ] f:id:masashi_kutsuna:20200916182917p:plain

その他ヒント

ParallelizationFactorとBulk性能の関係性について

Dynamodb stream経由でBulk処理をする際、ParallelizationFactorを1より大きい値にする時は注意が必要です。並列性が上がるとともに、リクエストボリュームによっては一回あたりの処理でまとめて扱われるレコード数が減少するため、Managed Blockchain側へのトランザクション数増加に比べて、単位時間あたりに処理されるレコード件数は低下する可能性があります。

参考情報

Hyperledger Fabricの仕様

f:id:masashi_kutsuna:20200916183319p:plain

出典:Architecture Explained — hyperledger-fabricdocs master documentation

Hyperledger Fabricはトランザクション記録要求から、実際に台帳に記録されるまでに多くのプロセスが必要です。

  1. クライアントによるトランザクション開始要求
  2. トランザクション開始要求の妥当性チェック(権限含む)& トランザクション実行
  3. トランザクション応答の検証&Ordererへのコミット要求
  4. 各Peerへのトランザクション結果送信
  5. トランザクション結果のコミット
  6. 台帳更新

これ以外にも、クライアントがチェーンコードを呼び出すための権限を取得するためのMSP(CA)による認証手続きなどのオーバーヘッドが存在します。

まとめ

今回、バルク処理を採用したブロックチェーンレイヤーの高速化の効果について説明させていただきました。

もちろん、全てのユースケースにおいて適用できるパターンではありませんが、相当のスパイクが発生した際においても所定時間内にブロックチェーンに書き出すことができるこの仕組みは幅広いシーンに適用できると期待しております。