和記システムの基本設計:アーキテクチャ設計 データ管理 MongDBの非同期設計① | 続・ティール組織 研究会のブログ

続・ティール組織 研究会のブログ

ティール組織が話題になっているが、具現化するにはどうしたらよいか?
その研究を続けるにあたり、さらに次の形態である、続・ティール組織なるものまで視野に入れ、具体的な施策・行動内容を研究・支援する会。

先までは、"和記"についての記載で、どのようにブロックチェーンSNSに組み込んで実装していけばよいのか、概念的なところからアプローチ方法を記載していった。概念設計としてはひとまず終えた。次は、フェデレーションモデル全体の基本設計といえるところまで、基本設計書に着手できるようなところまで、概念を具体化していきたい。そして、それにつながるDApps側である「和記システム」を、Pythonプログラムで開発していきたい。

 

では、DApps側での設計の続きを記載していこう。和記システムを設計するための基本手順を以下に示す。このシステムは、Pythonを用いて市町村のブロックチェーンと連携し、個人および市町村単位での和の行動のデータを収集、記録し、決算書(PL、BS)として公表するものである。

基本設計のステップ

  1. 要件定義
  2. アーキテクチャ設計
  3. データベース設計
  4. API設計
  5. ブロックチェーンインターフェース
  6. 決算書の生成
  7. フロントエンド開発
  8. テストとデプロイ

基本設計の各ステップを順番に進めることで、ブロックチェーンとDAppsとして繋がる「和記システム」の詳細な設計が可能になる。各ステップでは、関係者との協議やレビューを通じて設計内容を確定していくことが重要である。

 

2.アーキテクチャ設計

ブロックチェーンにおいて、なぜブロックにする必要あるのか?なお、suiはブロックにせず、トランザクションのままをオブジェクトにしてDAGにて処理している。私のフェデレーションモデルにおいて、suiを真似したgossipアルゴリズムを独自設計して実装したい。一つずつやってみよう。

 

次はmongoDBの設計をしていきたい。なお、同じく、rust側もpython側も非同期の超高速処理をさせたい。以下は、MongoDB を非同期で高速に利用するための設計例である。ここでは、Python 側は非同期ドライバ Motor、Rust 側は公式の MongoDB Rust ドライバ(async モード)を用いる前提で、設計方針と実装例を示す。


1. 要件と設計方針

要件

  • 高スループット・低レイテンシ
    非同期 I/O を活用し、同時多数のリクエストに対してブロッキングを回避する。

  • 非同期処理

    • Python:asyncio と Motor を利用
    • Rust:async/await 構文と公式 MongoDB Rust ドライバ(async 対応)を利用
  • スケーラビリティ
    MongoDB のレプリカセット、シャーディング、接続プーリングなどを活用し、負荷分散・冗長化を実現する。

  • 統一したデータアクセスレイヤー
    両方の言語で同じデータ構造(JSON ドキュメント形式)を扱うため、スキーマ設計とインデックス設計をしっかり行う。


2. アーキテクチャ概要

A. MongoDB クラスター構成

  • レプリカセット
    高可用性のため、少なくとも 3 台のノードでレプリカセットを構成する。
    → 読み取り分散、フェイルオーバー対応

  • シャーディング(必要に応じて)
    データ量や書き込み負荷が高い場合、シャーディングにより水平方向のスケールアウトを行う。

  • 接続プール
    各クライアント(Python、Rust)のドライバは内部で接続プーリングを実装しているため、適切な設定で高い並列性を確保。

B. 非同期データアクセスレイヤー

  • Python (Motor)

    • Motor は MongoDB の非同期ドライバである。
    • asyncio イベントループ上で動作し、コルーチンとしてデータベースアクセスを実装できる。
  • Rust (mongodb crate)

    • 公式の MongoDB Rust ドライバは async/await をサポートしている。
    • Tokio や async-std のランタイム上で動作させることで、非同期 I/O を実現できる。

3. 設計例

3.1 Python 側のサンプルコード (Motor を使用)

import asyncio
from motor.motor_asyncio import AsyncIOMotorClient

async def main():
    # MongoDB の接続 URI を設定(レプリカセットのオプションなども追加可能)
    client = AsyncIOMotorClient("mongodb://localhost:27017", maxPoolSize=100)
    db = client.my_database
    collection = db.my_collection

    # ドキュメントの挿入
    doc = {"name": "Alice", "age": 30}
    result = await collection.insert_one(doc)
    print("Inserted document id:", result.inserted_id)

    # ドキュメントの検索(非同期カーソルを利用)
    cursor = collection.find({"age": {"$gte": 25}})
    async for document in cursor:
        print(document)

if __name__ == '__main__':
    asyncio.run(main())

 

  • ポイント:

    • 非同期の挿入、検索を行い、maxPoolSize の設定で接続プールの上限を調整可能。

3.2 Rust 側のサンプルコード (mongodb crate を使用)

Cargo.toml で依存関係に async MongoDB ドライバを追加:

toml

[dependencies]
mongodb = { version = "2.4.1", features = ["tokio-runtime"] }
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
 

Rust のサンプルコード:

use mongodb::{Client, options::ClientOptions, bson::doc};
use tokio;

#[tokio::main]
async fn main() -> mongodb::error::Result<()> {
    // MongoDB の URI を解析し、接続オプションを設定する
    let client_uri = "mongodb://localhost:27017";
    let mut client_options = ClientOptions::parse(client_uri).await?;
    client_options.max_pool_size = Some(100);
    
    // クライアント生成
    let client = Client::with_options(client_options)?;
    let database = client.database("my_database");
    let collection = database.collection("my_collection");

    // ドキュメントの挿入
    let new_doc = doc! { "name": "Bob", "age": 25 };
    let insert_result = collection.insert_one(new_doc, None).await?;
    println!("Inserted document id: {:?}", insert_result.inserted_id);

    // ドキュメントの検索
    let filter = doc! { "age": { "$gte": 20 } };
    let mut cursor = collection.find(filter, None).await?;
    while let Some(result) = cursor.try_next().await? {
        println!("Found document: {:?}", result);
    }

    Ok(())
}

  • ポイント:

    • Tokio ランタイム上で非同期操作を実施。
    • ClientOptions で接続プールの設定を行うことで、並列性を向上。

4. まとめ

  • MongoDB クラスター設計
    レプリカセットやシャーディングを使い、可用性とスケーラビリティを確保する。
  • Python 側
    • Motor を使用して、asyncio に基づく非同期操作を実現する。
  • Rust 側
    • 公式 MongoDB ドライバ(mongodb crate)を使用して、Tokio ランタイム上で非同期処理を実装する。
  • 共通点
    • 両方とも接続プールの設定や適切なインデックス設計、最適なクエリを設計することで、超高速な並列処理が実現できる。

この設計例では、非同期のドライバを使って高い並列処理を実現する方法と、スケーラブルな MongoDB の構成を示した。具体的な要件に合わせて、接続プールサイズ、レプリカセットやシャーディングの設定を調整していく。

 

 

なお、多くの大規模システムでは、可用性やスケーラビリティを高めるためにクラスター設計(レプリカセット、シャーディングなど)を採用している。ただし、必ずしもこれだけが唯一の方法ではなく、システムの要件や運用ポリシーに合わせてさまざまなアプローチが考えられる。


クラスター設計の一般的な手法

  • レプリカセット
    複数のノードでデータのレプリケーションを行い、フェイルオーバーや読み取り分散を実現する。多くの運用環境で一般的に使われている。

  • シャーディング
    大量データや高い書き込み負荷がある場合、データを水平に分割して複数のシャードで管理することで、スケーラビリティとパフォーマンスを向上させる。

  • マネージドクラウドサービス
    MongoDB Atlas のようなクラウドサービスでは、レプリカセットやシャーディング、バックアップ、モニタリングなどが組み込まれており、運用の手間を大幅に軽減できる。


その他のアプローチ

  • 他の NoSQL データベースの利用
    システムの特性によっては、MongoDB 以外にも高いスループットや低レイテンシを実現できるデータベース(たとえば、Cassandra、Redis、CockroachDB など)を採用するケースもある。

  • 分散キャッシュ/メッセージキューの併用
    読み取りや書き込みのピーク時には、Redis のようなインメモリキャッシュや Kafka のようなメッセージキューシステムを併用して、バックエンドのデータベースへの負荷を分散させる方法もよく使われる。

  • マイクロサービスアーキテクチャ
    データの分散や並列処理の要求が高い場合、各機能を独立したサービスとして分割し、各サービスが独自のデータストアを持つ設計もあります。これにより、システム全体のスケーラビリティや障害の局所化が可能になる。

  • イベント駆動型アーキテクチャ
    システム間のデータ同期や処理をイベントストリーム(たとえば Apache Kafka や AWS Kinesis など)を用いて非同期で行う方法もあり、これにより高い並列処理と疎結合を実現するアプローチがある。


まとめ

  • クラスター設計(レプリカセット、シャーディング) は、信頼性とスケーラビリティを高めるための定番の手法であろう。
  • クラウドのマネージドサービス(MongoDB Atlas など)を利用すれば、運用負荷を軽減しながら同様の機能を享受できる。
  • また、システムの要件によっては 他の NoSQL データベースキャッシュ、メッセージキュー、イベント駆動型アーキテクチャ などを組み合わせる方法も検討できる。

どの方法を採用するかは、具体的なアプリケーションの要件(データ一貫性、スループット、運用の容易さなど)によって異なる。これらのアプローチを組み合わせることで、Rust 側も Python 側も非同期で超高速な並列処理を実現できるシステムが構築可能となる。

 

 

いかがであろうか、なかなか選択肢がいくつもあり、設計が難しいが、1つずつ詰めていきたい。