2.6K Views
February 05, 25
スライド概要
DeNA のゲーム開発では過去から未来の全てのバージョンから、さらにクライアント・サーバなどのランタイムの違いや、データベースまで、ひとつのスキーマで一元管理しているらしい!?しかもそのスキーマから C# の MasterMemory や Go 言語でデータを取り扱うコードまで自動生成できるらしい!?そんなバージョンからランタイムの違いまで統合した統合スキーマ言語である Muscle についてご紹介いたします。Muscle は Protocol Buffer で定義したスキーマから MasterMemory(C#) や Go 言語で取り扱えるようなランタイムコードやデータを出力することで、ランタイム間やバージョン間で発生するスキーマの差異を吸収し、スキーマの効率的なバージョン管理を可能にしています。本講演では Muscle を用いたマスタデータ管理について、Muscle の作者である大竹と、ゲーム開発の現場に Muscle を導入している山部が解説します。モバイルゲームの並行開発が抱える課題への挑戦をお伝えします。
◆ チャンネル登録はこちら↓
https://www.youtube.com/c/denatech?sub_confirmation=1
◆ X(旧Twitter)
https://x.com/denaxtech
◆ DeNA Engineering
https://engineering.dena.com/
◆ DeNA Engineer Blog
https://engineering.dena.com/blog/
◆ DeNA × AI Day ‖ DeNA TechCon 2025 公式サイト
https://techcon2025.dena.dev/
DeNA が社会の技術向上に貢献するため、業務で得た知見を積極的に外部に発信する、DeNA 公式のアカウントです。DeNA エンジニアの登壇資料をお届けします。
バージョンの違いすら統合する!? 統合スキーマ⾔語Muscle ゲームサービス事業本部開発運営統括部 ⼭部 薫 / ⼤⽵ 悠⼈ 1 © DeNA Co., Ltd.
⼤規模‧並⾏開発を⽀えるマスタデータ技術 Oyakata マスタデータ専⽤バージョン管理ツール Muscle Runtime/Versionによって異なるスキーマを 1つに統合したスキーマで管理する 2 © DeNA Co., Ltd.
⼤規模‧並⾏開発を⽀えるマスタデータ技術 Oyakata DeNAのマスタデータ技術の最前線である Muscleについて解説していきます マスタデータ専⽤バージョン管理ツール Muscle Master-data Universal SChema LanguagE Runtime/Versionによって異なるスキーマを 1つに統合したスキーマで管理する 3 © DeNA Co., Ltd.
⾃⼰紹介Profile ⼭部 薫 2014年新卒⼊社 ブラウザゲームのサーバエンジニア‧アプリ ゲームのクライアントエンジニア‧開発会社の 技術サポート‧Oyakataの開発など幅広く活躍 4 © DeNA Co., Ltd.
Outline 1. 2. 3. 4. 5. 6. 7. 5 マスタデータとは モバイルゲームにおけるマスタデータ事情 並⾏開発ブランチが増えた世界で起きたこと 全部⼊りマスタデータとRelease Toggles Release Togglesのための統合Schema⾔語Muscle 統⼀Schemaを宣⾔することでできること まとめ © DeNA Co., Ltd.
マスタデータとは 6 © DeNA Co., Ltd.
マスタデータとは マスターデータは、ビジネスエンティティ(従業員、顧客、製品、財務構 造、資産、場所など)に関するデータであり、業務トランザクションや業 務データ分析に意味を与える。エンティティは実世界のオブジェクト (⼈、組織、場所、物)である。エンティティはエンティティインスタン スによって具体化され、データ∕レコードの形式をとる。 DAMA International; DAMA ⽇本⽀部 Metafind コンサルティング株式会社. データマネジメント知識体系ガイド 第⼆版 改定新版 (p.691). ⽇経BP. Kindle 版. 7 © DeNA Co., Ltd.
モバイルゲームにおけるマスタデータ Character Entity ID Name HP ATK DEF AGI c1 Asmodeus 685 135 39 31 c2 Sitri 460 119 51 48 データベース等に ⼊れることが多い Weapon キャラクターや武器など ID Name ATK Weight Type w1 Bronze Sword 300 120 Sword w2 Bronze Spear 400 200 Spear Database ゲームバランスなどを考慮した設定値 ゲームのエンティティに対して 意味を与えるような設定データ 8 © DeNA Co., Ltd.
モバイルゲームにおけるマスタデータ Character Edit ID Name HP ATK DEF AGI c1 Asmodeus 685 135 39 31 c2 Sitri 460 119 51 48 Weapon ゲームデザイナー ID Name ATK Weight Type レベルデザイナー w1 Bronze Sword 300 120 Sword w2 Bronze Spear 400 200 Spear プランナー 表計算ソフトが使われることが多い マスタデータを⼊⼒することで遊びや難易度を調整する 9 © DeNA Co., Ltd.
本講演におけるマスタデータの範囲 Character ID Name HP ATK DEF AGI c1 Asmodeus 685 135 39 31 c2 Sitri 460 119 51 48 Weapon 10 SpreadSheet Database 主に表計算ソフ トで⼊⼒される 主にDBで 管理される ID Name ATK Weight Type w1 Bronze Sword 300 120 Sword w2 Bronze Spear 400 200 Spear ゲームの設定データ © DeNA Co., Ltd.
モバイルゲームにおけるマスタデータ事情 11 © DeNA Co., Ltd.
アプリストアとデータアップデート App 容量制限 Store クライアント アップデート ※ 緩和傾向 User リッチ化 Asset Asset Server Upload 12 Download データ アップデート © DeNA Co., Ltd.
アプリストアとデータアップデート App 容量制限 Store クライアント アップデート ストアのアプリ容量制限の影響で起動後サーバーから User アセットをダウンロードするデータアップデートを多⽤する傾向 100MB リッチ化 Asset Asset Server Upload 13 Download データ アップデート © DeNA Co., Ltd.
データアップデート ⽉4回のイベントリリース 2週⽬イベント 1週⽬イベント クライアントアップデート Code 14 Asset 3週⽬イベント 4週⽬イベント データアップデート データアップデート データアップデート Asset Asset Asset © DeNA Co., Ltd.
データアップデート ⽉4回のイベントリリース 2週⽬イベント 1週⽬イベント クライアントアップデート Code 15 コードの更新が不要 Asset 3週⽬イベント 4週⽬イベント データアップデート データアップデート データアップデート Asset Asset Asset © DeNA Co., Ltd.
アセットのリッチ化と開発の⼤規模‧並列化 市場の成⻑とともにアセットもリッチ化 制作に6ヶ⽉以上かかることも 16 © DeNA Co., Ltd.
アセットのリッチ化と開発の⼤規模‧並列化 6ヶ⽉以上 2週⽬イベント 1週⽬イベント クライアントアップデート Code 17 6ヶ⽉以上 Asset 6ヶ⽉以上 3週⽬イベント 6ヶ⽉以上 4週⽬イベント データアップデート データアップデート データアップデート Asset Asset Asset © DeNA Co., Ltd.
アセットのリッチ化と開発の⼤規模‧並列化 6ヶ⽉以上 6ヶ⽉以上 6ヶ⽉以上 6ヶ⽉以上 何も対策しないと 4イベント x 6ヶ⽉ 3週⽬イベント = 24ブランチ 2週⽬イベント 1週⽬イベント 4週⽬イベント の並⾏開発をすることになる クライアントアップデート Code 18 Asset データアップデート データアップデート データアップデート Asset Asset Asset © DeNA Co., Ltd.
並⾏開発ブランチを増やすとどうなるか マージコストの増⼤ コンフリクト確率UP x Repository数 ビルドジョブの整備 ビルドマシンのディスク圧迫 CIサーバの負荷増加 サーバ環境の整備 ブランチ切り替えコスト増 ブランチ選択ミスの増加 とにかく⼤変 19 © DeNA Co., Ltd.
モバイルゲームはバージョン管理試練の地 ⻑期化する開発 ⼤規模化するチーム 増加する並⾏開発ライン マスタデータ開発はいかに⽴ち向かうべきか 20 © DeNA Co., Ltd.
並⾏開発ブランチが増えた世界で起きたこと 21 © DeNA Co., Ltd.
バージョンごとに必要なカラムやレコードが違うことで ⼊⼒作業が⼤変になる 22 © DeNA Co., Ltd.
パラメータ設計はバージョン横断で⾏う v1リリース v2リリース v3リリース v4リリース キャラクターのリリース計画はこうしよう! プランナー 23 © DeNA Co., Ltd.
⼊⼒はバージョンごとに分ける? v1 ID Name HP ATK DEF AGI + c1 Asmodeus 685 135 39 31 1⾏⼊⼒ごとにブランチ切り替えが必要 v2 ID Name HP ATK DEF AGI c1 Asmodeus 685 135 39 31 + c2 Sitri 460 119 51 48 v3でカラムが追加されているので v1/v2で修正があったらコンフリクトする v3 24 ID Name HP ATK DEF AGI + SkillID c1 Asmodeus 685 135 39 31 + s1 c2 Sitri 460 119 51 48 + s2 + c3 Purson 576 126 54 36 + s3 © DeNA Co., Ltd.
バージョンごとにブランチを分けると ハイレベルなバージョン管理リテラシーが要求される ブランチ切り替え プランナー マージ順序 コンフリクトへの配慮 マージやコンフリクト解消などのコストが発⽣する エンジニア 前バージョンのマージ 差分のAuthorの特定 コンフリクト解消 Schema変更対応 ブランチ数が増えると特にしんどい ブランチ分けずにまとめて⼊⼒したい 25 © DeNA Co., Ltd.
ランタイムごとに必要なカラムやレコードが違うことで ⼊⼒作業が⼤変になる 26 © DeNA Co., Ltd.
ランタイムごとのマスタデータの違い Schedule ID OpenedAt ClosedAt event1 25/02/05 12:30 25/02/05 13:30 TRUE イベント期間 limited_shop_1 25/02/05 13:30 25/02/05 14:30 FALSE 期間限定ショップ開催期間 クライアント エンジニア サーバ エンジニア インフラ エンジニア Scaling Comment ツール エンジニア みんなでスケジュールマスタを設計する 27 © DeNA Co., Ltd.
ランタイムごとのマスタデータの違い Schedule ID クライアント エンジニア 28 OpenedAt ClosedAt Scaling Comment event1 25/02/05 12:30 25/02/05 13:30 TRUE イベント期間 limited_shop_1 25/02/05 13:30 25/02/05 14:30 FALSE 期間限定ショップ開催期間 ショップの期間がクライアントに組み込まれると ネタバレと買い控えにつながるので⼊れたくない © DeNA Co., Ltd.
ランタイムごとのマスタデータの違い Schedule ID クライアント エンジニア サーバ エンジニア 29 OpenedAt ClosedAt Scaling Comment event1 25/02/05 12:30 25/02/05 13:30 TRUE イベント期間 limited_shop_1 25/02/05 13:30 25/02/05 14:30 FALSE 期間限定ショップ開催期間 ショップの期間がクライアントに組み込まれると ネタバレと買い控えにつながるので⼊れたくない 期間限定ショップの情報は実装上必要 © DeNA Co., Ltd.
ランタイムごとのマスタデータの違い Schedule ID クライアント エンジニア 30 OpenedAt ClosedAt Scaling Comment event1 25/02/05 12:30 25/02/05 13:30 TRUE イベント期間 limited_shop_1 25/02/05 13:30 25/02/05 14:30 FALSE 期間限定ショップ開催期間 不要なカラムもできれば⼊れたくない © DeNA Co., Ltd.
ランタイムごとのマスタデータの違い Schedule ID クライアント エンジニア インフラ エンジニア 31 OpenedAt ClosedAt Scaling Comment event1 25/02/05 12:30 25/02/05 13:30 TRUE イベント期間 limited_shop_1 25/02/05 13:30 25/02/05 14:30 FALSE 期間限定ショップ開催期間 不要なカラムもできれば⼊れたくない インスタンス数などを制御するために ユーザー数が増えそうなタイミングを知りたい © DeNA Co., Ltd.
ランタイムごとのマスタデータの違い Schedule ID クライアント エンジニア 32 OpenedAt ClosedAt Scaling Comment event1 25/02/05 12:30 25/02/05 13:30 TRUE イベント期間 limited_shop_1 25/02/05 13:30 25/02/05 14:30 FALSE 期間限定ショップ開催期間 コメントはネタバレリスクあるし 流⽯に出⼒しないよね? © DeNA Co., Ltd.
ランタイムごとのマスタデータの違い Schedule ID クライアント エンジニア ツール エンジニア 33 OpenedAt ClosedAt Scaling Comment event1 25/02/05 12:30 25/02/05 13:30 TRUE イベント期間 limited_shop_1 25/02/05 13:30 25/02/05 14:30 FALSE 期間限定ショップ開催期間 コメントはネタバレリスクあるし 流⽯に出⼒しないよね? スケジュールをカレンダーに⼊れるために 出⼒するオプションが欲しい © DeNA Co., Ltd.
ランタイムごとのマスタデータの違い Schedule ID OpenedAt ClosedAt event1 25/02/05 12:30 25/02/05 13:30 TRUE イベント期間 limited_shop_1 25/02/05 13:30 25/02/05 14:30 FALSE 期間限定ショップ開催期間 クライアント エンジニア サーバ エンジニア インフラ エンジニア Scaling Comment ツール エンジニア なんか思ってたのと違う 34 © DeNA Co., Ltd.
ランタイムごとのマスタデータの違い Schedule ID OpenedAt ClosedAt Scaling Comment event1 25/02/05 12:30 25/02/05 13:30 TRUE イベント期間 limited_shop_1 25/02/05 13:30 25/02/05 14:30 FALSE 期間限定ショップ開催期間 ランタイムごとに必要となる レコードもカラムも微妙に異なる 全部別々に⼊⼒するんですか? プランナー 35 © DeNA Co., Ltd.
全部⼊りマスタデータとRelease Toggles 36 © DeNA Co., Ltd.
Release Togglesとは Feature Togglesの⼀種。⼗分にテストされていないコードやデータを 共有統合ブランチ(main/develop/trunkなど)にマージしつつも、本番環 境で有効にならないようにすることで継続的デリバリーを達成する⼿ 法。トランクベース開発などでよく使われる。 37 © DeNA Co., Ltd.
バージョン別のRelease Toggles Character ID Name HP ATK DEF AGI SkillID Version c1 Asmodeus 685 135 39 31 s1 1 c2 Sitri 460 119 51 48 s2 2 c3 Purson 576 126 54 36 s3 3 c4 Vinea 601 81 40 72 s4 4 全部⼊りのマスタデータを⽤意する 38 © DeNA Co., Ltd.
バージョン別のRelease Toggles Character ID Name HP ATK DEF AGI SkillID Version c1 Asmodeus 685 135 39 31 s1 1 c2 Sitri 460 119 51 48 s2 2 c3 Purson 576 126 54 36 s3 3 c4 Vinea 601 81 40 72 s4 4 Versionカラムの値で レコードを出し分ける 全部⼊りのマスタデータを⽤意する 39 © DeNA Co., Ltd.
バージョン別のRelease Toggles Character ID Name HP ATK DEF AGI SkillID Version c1 Asmodeus 685 135 39 31 s1 1 c2 Sitri 460 119 51 48 s2 2 c3 Purson 576 126 54 36 s3 3 c4 Vinea 601 81 40 72 s4 4 v2 Versionカラムの値で レコードを出し分ける 不要なカラム‧レコードをフィルタリング v2: Character 40 ID Name HP ATK DEF AGI SkillID Version c1 Asmodeus 685 135 39 31 s1 1 c2 Sitri 460 119 51 48 s2 2 c3 Purson 576 126 54 36 s3 3 c4 Vinea 601 81 40 72 s4 4 © DeNA Co., Ltd.
バージョン別のRelease Toggles v2: Character ID Name HP ATK DEF AGI SkillID Version c1 Asmodeus 685 135 39 31 s1 1 c2 Sitri 460 119 51 48 s2 2 c3 Purson 576 126 54 36 s3 3 c4 Vinea 601 81 40 72 s4 4 バージョンごとに出⼒結果を変える v3: Character 41 ID Name HP ATK DEF AGI SkillID Version c1 Asmodeus 685 135 39 31 s1 1 c2 Sitri 460 119 51 48 s2 2 c3 Purson 576 126 54 36 s3 3 c4 Vinea 601 81 40 72 s4 4 © DeNA Co., Ltd.
ランタイム別のRelease Toggles Schedule ID OpenedAt ClosedAt Scaling Comment Runtime event1 25/02/05 12:30 25/02/05 13:30 TRUE イベント期間 All limited_shop_1 25/02/05 13:30 25/02/05 14:30 FALSE 期間限定ショップ開催期間 Server Runtimeカラム 42 © DeNA Co., Ltd.
ランタイム別のRelease Toggles Schedule ID OpenedAt ClosedAt event1 25/02/05 12:30 25/02/05 13:30 TRUE イベント期間 All limited_shop_1 25/02/05 13:30 25/02/05 14:30 FALSE 期間限定ショップ開催期間 Server Comment Runtime ID 43 Comment Runtime クライアント向け Client: Schedule クライアント エンジニア Scaling OpenedAt ClosedAt Scaling event1 25/02/05 12:30 25/02/05 13:30 TRUE イベント期間 All limited_shop_1 25/02/05 13:30 25/02/05 14:30 FALSE 期間限定ショップ開催期間 Server ネタバレ要素がなくなって安⼼ © DeNA Co., Ltd.
ランタイム別のRelease Toggles Schedule ID OpenedAt ClosedAt event1 25/02/05 12:30 25/02/05 13:30 TRUE イベント期間 All limited_shop_1 25/02/05 13:30 25/02/05 14:30 FALSE 期間限定ショップ開催期間 Server Comment Runtime ID 44 Comment Runtime サーバ向け Server: Schedule サーバ エンジニア Scaling OpenedAt ClosedAt Scaling event1 25/02/05 12:30 25/02/05 13:30 TRUE イベント期間 All limited_shop_1 25/02/05 13:30 25/02/05 14:30 FALSE 期間限定ショップ開催期間 Server 必要なレコードが取得できて安⼼ © DeNA Co., Ltd.
ランタイム別のRelease Toggles Schedule ID OpenedAt ClosedAt event1 25/02/05 12:30 25/02/05 13:30 TRUE イベント期間 All limited_shop_1 25/02/05 13:30 25/02/05 14:30 FALSE 期間限定ショップ開催期間 Server Comment Runtime ID 45 Comment Runtime インフラ向け Server: Schedule インフラ エンジニア Scaling OpenedAt ClosedAt Scaling event1 25/02/05 12:30 25/02/05 13:30 TRUE イベント期間 All limited_shop_1 25/02/05 13:30 25/02/05 14:30 FALSE 期間限定ショップ開催期間 Server プロダクト影響なく スケーリング設定変更を⾃動化できて嬉しい © DeNA Co., Ltd.
ランタイム別のRelease Toggles Schedule ID OpenedAt ClosedAt event1 25/02/05 12:30 25/02/05 13:30 TRUE イベント期間 All limited_shop_1 25/02/05 13:30 25/02/05 14:30 FALSE 期間限定ショップ開催期間 Server Comment Runtime ID 46 Comment Runtime ツール向け Server: Schedule ツール エンジニア Scaling OpenedAt ClosedAt Scaling event1 25/02/05 12:30 25/02/05 13:30 TRUE イベント期間 All limited_shop_1 25/02/05 13:30 25/02/05 14:30 FALSE 期間限定ショップ開催期間 Server プロダクトに含められないデータも ⼀括管理できて安⼼ © DeNA Co., Ltd.
全部⼊りマスタデータとRelease Toggles 全部⼊りマスタデータ フィルタリングによるRelease Toggles クライアント 47 サーバ インフラ ツール 各バージョン © DeNA Co., Ltd.
ここまでのまとめ 1. モバイルゲームでは並⾏開発ライン数が多くブランチ切り替えコスト が⾼い 2. ランタイムの種類ごとにもマスタデータへの要求が異なる 3. これらを全てブランチワークで対応するのは厳しい 4. 全部⼊りマスタデータを⽤意しフィルタリングすることで各バージョ ン‧各ランタイムに出⼒し分けることができる 48 © DeNA Co., Ltd.
ここまでのまとめ 1. モバイルゲームでは並⾏開発ライン数が多くブランチ切り替えコスト が⾼い 2. ランタイムの種類ごとにもマスタデータへの要求が異なる 3. これらを全てブランチワークで対応するのは厳しい 4. 全部⼊りマスタデータを⽤意しフィルタリングすることで各バージョ ン‧各ランタイムに出⼒し分けることができる ⼀⽅、Schemaの種類が多く 管理が煩雑になることが予想される 49 © DeNA Co., Ltd.
⾃⼰紹介Profile ⼤⽵ 悠⼈ (Haruto Otake) エンジニア モバイルゲームの開発効率を ⾼めるための様々な仕組み作りに 継続的に取り組んでいます。 50 © DeNA Co., Ltd.
Release Togglesのための 統合スキーマ⾔語Muscle 51 © DeNA Co., Ltd.
全部⼊りマスタデータのスキーマ定義の課題 システムごとにスキーマ定義を管理すると、 記述に重複が発⽣する バージョンごとにスキーマ定義をブランチ管理すると、 マージの⼿間とコンフリクトのリスクが増⼤する。 52 © DeNA Co., Ltd.
スキーマ定義の課題解決に必要なもの 全てのRelease Togglesによる分岐を考慮し、 Oyakata/クライアント/サーバ各々で共通で使える、 単⼀の全部⼊りスキーマを実現できる⼿段 53 © DeNA Co., Ltd.
マスタデータ統合スキーマ⾔語 Muscle ● マスタデータのスキーマに求められる全てを1つのスキーマの中に 記述することで、複数のシステムへの対応とFeature Togglesの 運⽤を実現するマスタデータ統合スキーマ⾔語 ● Protocol BuffersによるIDLから各システム⽤のコードや設定を⽣成 ● モバイルゲームのマスタデータに関連して社内で利⽤している Oyakata(データ⼊⼒‧管理) / Takasho(ゲームサーバ) / MasterMemory(クライアント⽤OSS)に対応 ● マスタデータの⼊⼒から変換、読み込みまで責任を持ち マスタデータのゆりかごから墓場までをカバーする 54 © DeNA Co., Ltd.
Protocol Buffersによるスキーマ⾔語の構築 ● Protocol Buffers(=Protobuf)とは? ○ Google製の、様々な⾔語に対応した著名なシリアライザ ○ protocコマンドに、protobuf⾔語で記述されたスキーマを渡して 各⾔語⽤のシリアライザを⽣成して利⽤する ● MuscleはProtobufをスキーマ⾔語構築の基盤として利⽤ ○ protocプラグインの仕組みを利⽤すると、 効率的に独⾃のスキーマ⾔語を実装できる ○ 利⽤者はProtobufの既存のエコシステムを利⽤して、 ⼊⼒補完やLintなどの⾼度な開発体験を得られる 55 © DeNA Co., Ltd.
Protobuf IDLの基本 syntax = 'proto3'; message CharaMaster { string Id = 1; ● messageによって直積型(構造体)を宣⾔ string Name = 2; ○ フィールドの型,名前,番号を宣⾔ uint64 Power = 3; ● フィールドに使⽤できる主な型 repeated string Labels = 4; CharaTypes CharaType = 5; ○ string/int32等のプリミティブ型 ○ message/enum 等のユーザ定義型 } enum CharaTypes { ○ repeatedを付けると任意の型の CharaTypes_Unknown = 0, リスト型 CharaTypes_Playable = 1, ● enumで列挙型を宣⾔ CharaTypes_Enemy = 2 } 56 © DeNA Co., Ltd.
ProtobufのカスタムオプションによるIDL構築
● message,field,enum等の構⽂ごとに
メタ情報の付与が可能
○ extendで指定した型の値を
option構⽂で記述できる
● カスタムオプションを利⽤して、
統⼀スキーマとして必要な情報を
スキーマ中に宣⾔的に記述
できるようにする
57
message CustomMessageOptions {
optional string string_option = 1;
optional bool bool_option = 2;
}
extend google.protobuf.MessageOptions {
CustomMessageOptions custom = 7739036;
}
message MessageWithCustomOption {
option (sample.custom) = {
string_option: "hoge",
bool_option: true
};
}
© DeNA Co., Ltd.
Muscleの実際のIDLをRelease Togglesを軸に解説 58 © DeNA Co., Ltd.
シンプルなテーブルの定義 ● テーブルごとにMessageを宣⾔ ○ カラムごとにそのFieldとして宣⾔ ● Oyakata上でのマスタデータの⼊⼒設定、 クライアント/サーバでの参照⽤コード、 JSONからのデータコンバータなど、 必要なものを全て⾃動⽣成する 59 message CharaMaster { option (muscle.message) = { //テーブルとして扱うことを宣言 table: true, //Oyakataで入力することを宣言 oyakata:{use:true} //サーバとクライアントの双方で //利用することを宣言 master_memory:{use:true}, takasho:{use:true} }; //カラムを宣言 string Id = 1 [(muscle.field) = { //主キーであることを宣言 primary_key: true }]; string Name = 2; uint64 Power = 3; } © DeNA Co., Ltd.
列挙型(Enum)の宣⾔ ● 各システム向けのenumがも⽣成される ● Oyakata上でもenumとして ○ enum毎にテーブルが作成され、 enum値に応じたレコードも⽣成される 60 enum AbilityTargets { option (muscle.enum) = { master_memory: {use: true} oyakata: {use: true} }; AbilityTargets_Unknown = 0; AbilityTargets_Single = 1; AbilityTargets_All = 2; } message HealAbilityMaster { string Id = 1 [(muscle.field) = { primary_key:true }]; int64 HealAmount = 2; AbilityTargets Target = 3; } © DeNA Co., Ltd.
MuscleとRelease Toggles ● Release Togglesと⼀⼝に⾔っても、そのかたちは⾮常に多様 ● 様々な種別のRelease Togglesを、様々な対象に対して適⽤できる ● Release Togglesの種別及び対象に応じた 様々なアノテーションをスキーマに対して記述していく 61 © DeNA Co., Ltd.
Muscleが扱えるRelease Togglesの種別 ● バージョントグル ○ リリースごとの⼤⼩⽐較可能なバージョン値による分岐 ○ 有効化したものは後続バージョンでも有効になる ● 機能トグル ○ 特定のフラグ名ごとの真偽値による分岐 ○ 特定のテスト⽤環境であったり、特定の状況でのみ データを有効化する場合に利⽤ ● デバッグトグル ○ デバッグ⽤かどうかの真偽値による分岐 ● システムトグル ○ 利⽤するシステム種別による分岐 62 © DeNA Co., Ltd.
Muscleが扱えるRelease Togglesの分岐対象 ● テーブル ○ 特定のスキーマを持つテーブルが存在するか を制御する ○ スキーマのメッセージに定義された値を元に判断する ● カラム ○ テーブル内の特定の列が存在するか を制御する ○ スキーマのフィールドに定義された値を元に判断する ● レコード ○ テーブルの特定のレコードが存在するか を制御する ○ ⾏ごとの特定のカラムの値を元に判断する ○ レコードでは、スキーマではなく データ側に基準値が記述される 63 © DeNA Co., Ltd.
Muscleが扱えるRelease Togglesの分岐対象 ● 列挙型(Enum) ○ 特定の列挙型が存在するか を制御する ○ スキーマのenumに定義された値を元に判断する ● 列挙⼦(Enum Value) ○ 特定の列挙型の定数が存在するか を制御する ○ スキーマのenum valueに定義された値を元に判断する 64 © DeNA Co., Ltd.
テーブル単位のバージョントグル
message Character {
option (muscle.message) = {
//...省略
//表単位のバージョントグルを利用する
//Versionが >= 2 の時にテーブルが有効になる
required_version: 2
//Versionが >= 5 の時にテーブルが無効になる
removed_version: 5
};
string Id = 1 [(muscle.field) = {primary_key: true}];
//...省略..
}
● あるバージョンから新たなテーブルを
追加(または削除)する場合に記述する
● required_version
○ 指定したバージョン以上の場合
テーブルが有効になる
● removed_version
○ 指定したバージョン以上の場合
テーブルが無効になる(削除時に使⽤)
● 扱う実装を追加/削除するタイミングで
制御したいため、指定値以上かで判定する
65
© DeNA Co., Ltd.
カラム単位のバージョントグル
● あるバージョンから既存テーブルに
message Character {
//...省略..
カラムを追加する場合に記述する
SkillID = 7 [(muscle.field) = {
● required_version / removed_version int64
//カラム単位のバージョントグルを利用する
//Versionが >= 3 の時にカラムが有効になる
を⽤いてテーブル単位同様に指定
required_version: 3
}];
}
v2: Character
66
ID
Name
HP
ATK
DEF
AGI
SkillID
c1
Asmodeus
685
135
39
31
s1
c2
Sitri
460
119
51
48
s2
c3
Purson
576
126
54
36
s3
c4
Vinea
601
81
40
72
s4
© DeNA Co., Ltd.
レコード単位のバージョントグル ● あるバージョンからマスタデータの レコードを追加する場合に記述する ● row_versioning ○ Versionカラムが⾃動的に追加で定義され、 この値が対象バージョン以下の レコードのみが含まれる v2: Character 67 ID Name HP ATK DEF AGI SkillID Version c1 Asmodeus 685 135 39 31 s1 1 c2 Sitri 460 119 51 48 s2 2 c3 Purson 576 126 54 36 s3 3 c4 Vinea 601 81 40 72 s4 4 message Character { option (muscle.message) = { //...省略 oyakata: { //レコード単位の //バージョントグルを利用 row_versioning: true } }; //...省略.. } © DeNA Co., Ltd.
Enum単位のバージョントグル ● required_version / removed_versionを ⽤いてテーブル単位同様に指定 ● 列挙⼦(EnumValue)単位でも指定可能 ○ 運⽤中に列挙⼦を追加できる v1: AbilityTargets Key 68 Value v3: AbilityTargets v2: AbilityTargets Version Key Value enum AbilityTargets { option (muscle.enum) = { master_memory: {use: true} oyakata: {use: true} //Enum単位のバージョントグル required_version: 2 }; AbilityTargets_Unknown = 0; AbilityTargets_Single = 1; AbilityTargets_All = 2; AbilityTargets_Multiple = 2 [(muscle.enum_value) = { //EnumValue単位のバージョントグル required_version: 3 }]; } Version Key Value Version 0 Undefined 2 0 Undefined 2 0 Undefined 2 1 Single 2 1 Single 2 1 Single 2 2 All 2 2 All 2 2 All 2 3 Multiple 3 3 Multiple 3 3 Multiple 3 © DeNA Co., Ltd.
複数の対象の組み合わせ
v1: Character
ID
Name
HP
ATK
DEF
AGI
SkillID
Version
c1
Asmodeus
685
135
39
31
s1
1
c2
Sitri
460
119
51
48
s2
2
c3
Purson
576
126
54
36
s3
3
c4
Vinea
601
81
40
72
s4
4
v1はテーブル全体がフィルタされる
v2ではカラムとレコードがフィルタされる
v2: Character
69
ID
Name
HP
ATK
DEF
AGI
SkillID
Version
c1
Asmodeus
685
135
39
31
s1
1
c2
Sitri
460
119
51
48
s2
2
c3
Purson
576
126
54
36
s3
3
c4
Vinea
601
81
40
72
s4
4
message Character {
option (muscle.message) = {
//...省略
//表単位のバージョントグルを利用
required_version: 2
oyakata: {
//行単位のバージョントグルを利用
row_versioning: true
}
};
//...省略..
int64 SkillID = 7 [(muscle.field) = {
//カラム単位のバージョントグルを利用
//Versionが >= 3 の時にカラムが有効
required_version: 3
}];
}
© DeNA Co., Ltd.
複数の対象の組み合わせ
v3: Character
ID
Name
HP
ATK
DEF
AGI
SkillID
Version
c1
Asmodeus
685
135
39
31
s1
1
c2
Sitri
460
119
51
48
s2
2
c3
Purson
576
126
54
36
s3
3
c4
Vinea
601
81
40
72
s4
4
v3はレコードがフィルタされる
v4は全部⼊り
v4: Character
70
ID
Name
HP
ATK
DEF
AGI
SkillID
Version
c1
Asmodeus
685
135
39
31
s1
1
c2
Sitri
460
119
51
48
s2
2
c3
Purson
576
126
54
36
s3
3
c4
Vinea
601
81
40
72
s4
4
message Character {
option (muscle.message) = {
//...省略
//表単位のバージョントグルを利用
required_version: 2
oyakata: {
//行単位のバージョントグルを利用
row_versioning: true
}
};
//...省略..
int64 SkillID = 7 [(muscle.field) = {
//カラム単位のバージョントグルを利用
//Versionが >= 3 の時にカラムが有効
required_version: 3
}];
}
© DeNA Co., Ltd.
機能トグル ● 特定の状況でのみ対象を有効する場合に 記述する ○ オートスケール⽤のデータなど ● required_feature ○ 指定した機能トグルが有効な場合 対象が有効になる ● バージョントグル同様に、 テーブル/カラム/レコードを対象に 指定することが可能 71 message EventQuestMaster { option (muscle.message) = { //...省略 //テーブル単位の機能トグルを利用する // “Event” 機能トグルが有効な場合のみ有効 requreid_feature: “Event” oyakata: { row_required_feature: “Event” } }; //...省略.. string Id = 2 [(muscle.field) = { //カラム単位の機能トグルを利用する // “SpecialEvent” 機能トグルが //有効な場合のみ有効 requreid_feature: “SpecialEvent” }]; } © DeNA Co., Ltd.
バージョンと機能トグルの関係 ● バージョン毎に暗黙的に有効化する機能トグルを指定して、 後続のバージョンに引き継げる ○ 明⽰的に機能トグルを指定して追加で有効化することもできる ● 開発中機能を管理するケースもカバー。⾮常に柔軟に運⽤できる 72 © DeNA Co., Ltd.
デバッグトグル ● デバッグ⽤か否かによる分岐 message EventQuestMaster { option (muscle.message) = { ○ テーブル/レコードを対象にとる //...省略 ● 開発時には必要だが製品には不要な //テーブル単位のデバッグトグルを利用 debug: true データのフィルタリングが可能になる oyakata: { ○ 開発初期に⽤いるダミーデータや、 //レコード単位のデバッグトグルを利用 contains_debug_row: true 開発⽤のプリセットの管理に利⽤ } ● 製品には該当テーブルのコードを含まない }; } ようにコード⽣成される 73 © DeNA Co., Ltd.
システムトグル ● Oyakata(⼊⼒環境)/Takasho(サー バ)/MasterMemory(クライアント) といった、システム単位での分岐 ○ テーブル/カラムを主に対象に取る ● ⼀部のシステムでのみ使うデータやコードを フィルタリングする ○ クライアントでは使わないカラムなど ● 広義のシステムトグルとして、 インデックスやメモリ上の分割単位など、 システム固有で必要な情報の記述も⾏う 74 message ShopItem { option (muscle.message) = { //Oyakataで入力することを宣言 oyakata:{use:true} //サーバとクライアントの双方で //利用することを宣言 master_memory:{use:true}, takasho:{use:true} }; google.protobuf.Timestamp Schedule = 2 [ (muscle.field) = { //クライアントでは除外 master_memory: {ignore: true} }]; } © DeNA Co., Ltd.
MuscleによるRelease Toggles運⽤で出来ること 単⼀のブランチ上で全部⼊りスキーマを定義することで 全部⼊りマスタデータから必要なデータをフィルタして 並⾏開発している全てのバージョンと、 マスタデータを参照する全てのシステムを扱うための 必要な全てのコードと設定を出⼒できる 75 © DeNA Co., Ltd.
統合スキーマを宣⾔することでできること 76 © DeNA Co., Ltd.
統合スキーマ⾔語のあるべき形 統合スキーマの記述のあり⽅として、 情報を集約し、重複を排除することが求められる。 従来のスキーマ運⽤の課題も重複こそが根本課題であり 統合スキーマでも重複の排除は追求する必要がある。 本当に場合分けが必要なことを慎重に⾒極めて 可能な限り普遍的な性質としてシンプルに記述 できるようにすることで、重複を排除できる 77 © DeNA Co., Ltd.
統合スキーマ⾔語は様々なことを可能にする 正しく構成された統合スキーマ⾔語は各システムに散開 していた情報を集約して重複を排除することに繋がり、 信頼できる唯⼀の情報源を実現する。 Muscleはこれを活⽤することで、 Release Togglesの運⽤の実現だけではなく、 マスタデータに関する 様々な効率化が可能になった。 78 © DeNA Co., Ltd.
テーブル間のリレーションを表現する message CharaMaster { option (muscle.message) = { table: true, master_memory:{use:true}, takasho:{use:true} }; string Id = 1 [(muscle.field) = {primary_key: true }]; string Name = 2; uint64 Power = 3; AbilityMaster AbilityId = 4; } message AbilityMaster { option (muscle.message) = { table: true, master_memory:{use:true}, takasho:{use:true} }; string Id = 1 [(muscle.field) = { primary_key:true }]; string Name = 2; } ● 他テーブルを主キーで参照するカラムに 外部キー制約をかける機能 ● 参照先テーブルのmessage/enum型を フィールドの型にスキーマ上で指定 ● Oyakata上で⼊⼒補完される他、C#では 参照先を取得するコードも出⼒ 79 © DeNA Co., Ltd.
リレーションの参照整合性検証 ● リレーションで設定したIDのレコードが存在するように参照整合性を 常に保たないと、ランタイムでの参照時の不具合を誘発してしまう ● すべての⾏の参照整合性の検証を⾏うスクリプトを、 Oyakataのバリデーション機能⽤のスクリプトとして⾃動⽣成する ● スキーマで宣⾔するだけで参照整合性が検証されるようになる Skill Character 80 ID Name Skill Version ID Name c1 Asmodeus s1 1 s1 Critical 1 c2 Sitri s5 2 s2 Stun 2 c3 Purson s3 3 s3 Paralysis 3 c4 Vinea s4 4 s4 Burn 4 Version © DeNA Co., Ltd.
バージョンを考慮した参照整合性検証 ● 古いバージョンのテーブルやレコードからは、 それより新しいバージョンのテーブルやレコードを参照できない ● 古いバージョンの時にだけ、参照整合性が崩壊することがある ● 全部⼊りマスタではバージョンによる参照整合性の崩壊リスクが⾼い ● デバッグデータとリリースデータの間にも同様のリスクがある Skill Character 81 ID Name Skill Version ID Name c1 Asmodeus s1 1 s1 Critical 1 c2 Sitri s3 2 s2 Stun 2 c3 Purson s3 3 s3 Paralysis 3 c4 Vinea s4 4 s4 Burn 4 Version © DeNA Co., Ltd.
バージョンを考慮した参照整合性検証 ● Muscleは全てを知っているので、検証スクリプトを⾃動⽣成できる ● 参照元のバージョンが参照先のバージョン以上であることを検証 ● リレーションのあるカラムに要求バージョンがある場合、 参照元のバージョンとカラムの要求バージョンの⼤きい⽅と⽐較 ○ カラムが追加された時点のバージョン以下の⾏は参照可能 ● 全てのバージョンでの参照整合性を1回で⾼速に検証する Skill Character 82 ID Name Skill Version ID Name c1 Asmodeus s1 1 s1 Critical 1 c2 Sitri s3 2 s2 Stun 2 c3 Purson s3 3 s3 Paralysis 3 c4 Vinea s4 4 s4 Burn 4 Version © DeNA Co., Ltd.
バリデーション条件の宣⾔
●
●
83
message CharaMaster {
//...省略
カラムの正常な値の条件を宣⾔的に記述
○ 宣⾔に応じてスクリプトが⾃動⽣成され uint64 Power = 3 [(muscle.field) = {
validations: [
Gated Check-inに組み込まれる
{
○ 違反時は宣⾔したメッセージを表⽰
message: "最大値は1000",
condition: { max: {long: 1000} }
スキーマ定義のフローのなかで
}
バリデーションを組み込むことができる
]
○ スキーマ定義の時点でレビューでき、 }];
//...省略
⼊⼒データの品質を保ちやすくなる
}
© DeNA Co., Ltd.
マスタデータ動作確認のイテレーション⾼速化 ● マスタデータの動作確認には、クライアント/サーバそれぞれへの データコンバート及びデプロイ作業が必要になる ● マスタデータの変更後にデプロイを実⾏し、完了を待って動作確認 する必要があるため、CIで⾃動化されていてもジョブの実⾏待ちが 多発したり、完了まで注意深く待機する必要があった ● 動作確認のイテレーションを阻害する⼤きな要因になっていた 84 © DeNA Co., Ltd.
マスタデータ動作確認のイテレーション⾼速化 ● Muscleは全領域のスキーマ定義とコンバート⼿段を掌握している ○ コンバータをUnityからも実⾏可能なC#コードとして提供 ○ サーバが利⽤するマスタはヘッダで動的に変更できるよう実装 ● Editorでの再⽣時に、CI上でのデプロイ処理を⼿元PCで透過的に⾏い デプロイしたデータを即座に利⽤する事で、CIの完了待ちを無くした 85 © DeNA Co., Ltd.
マスタデータ動作確認のイテレーション⾼速化 ● Unity Editorで再⽣するだけで、サーバを含めて 任意のブランチの最新のマスタデータを使ってゲームが動作する ● 差分ダウンロードされるためデプロイの待ち時間は数秒 86 © DeNA Co., Ltd.
まとめ 87 © DeNA Co., Ltd.
まとめ モバイルゲーム開発は必要な並⾏開発ラインが⾮常に多く、 ブランチ主体の開発スタイルでは様々な問題が発⽣する。 DeNAはこれを、全部⼊りマスタデータに対して適宜 フィルタリングする戦略で⽴ち向かうために、 バージョンやシステムの違いを統合して扱える 統合スキーマ⾔語 Muscle を開発した 88 © DeNA Co., Ltd.
まとめ 統合スキーマ⾔語Muscleにより Release Toggles⽤いた複雑なブランチワークのない、 効率的なマスタデータ運⽤を可能にすると同時に、 信頼できる唯⼀の情報源が構成され、これによって マスタデータの信頼性を向上させ、 マスタデータ開発のイテレーションを⾼速化する、 様々な⾼度な機能を実現することができた 89 © DeNA Co., Ltd.
Thank you for listening! 90 © DeNA Co., Ltd.