リスクなしで
ソフトウェアを
より速く革新
Innovate Your Software Faster without Risk
Feature Flagsおよび観測ツールの使用による段階的なデータベース移行
この記事は LifeCycle によって書かれました。ここで英語で書かれたオリジナルの記事を確認できます
シナリオの説明
多くの企業は、セルフホステッドデータベースサービスからクラウドベースサービスへ、オンプレミスデータセンターよりクラウドデータセンターへ、古いデータベースから新しいものへの移行に移行が必要な状況に直面することがあります。移行プロセス全体で、安定性を確保し、データ損失を防ぎ、サービスの停止を回避することが重要です。最も一般的な移行手法の1つが「デュアルライトデータベース移行」です。
解決策
デュアルライト移行のプロセスは、以下のとおりです。下の図に示されています。
- 初期段階 - アプリケーションは古いデータベースとのみやりとり(読み書き)します。
- 古いデータベースに読み書きする既存のコードに、新しいデータベースに読み書きするコードを追加する必要があります。たとえば、あるテーブルへのレコード挿入時には、データは古いデータベースと新しいデータベースの両方に同時に挿入される必要があります。通常、これら2つの挿入操作は並行して実行され、元のサービス呼び出し処理時間をできるだけ維持するようにします。
- データベースへの書き込みリクエストが到着すると、古いデータベースに書き込まれ、一部のトラフィックも新しいデータベースに書き込まれます。
- 新しいデータベースに書き込まれるトラフィックの割合を徐々に増やし、最終的に100%に達するまで増やします。このプロセス中に問題が発生した場合は、本番環境に影響を与えることなく迅速にロールバックすることができます。
- 書き込み移行が完了した後、新しいデータベースから読み取られるデータの量を徐々に増やし、最初はトラフィックの10%を新しいデータベースで読み取り操作を行えるようにします。このプロセス中、パフォーマンスを測定し、結果を比較します。問題が発生した場合は、本番環境に影響を与えることなく、新しいデータベースの読み取りトラフィックを迅速にロールバックできます。
- ある期間、新しいデータベースが問題なく全ての読み書き操作を処理していることが確認されたら、古いデータベースとその関連コードサービスを停止できます。
実際の運用プロセスでは、段階的に本番環境サービスの新しいデータベースの読み書きコードを更新する必要があり、安定的な繰り返し移行を確実にする必要があります。
実用的な手法とツール
システムアーキテクチャの設計だけでなく、2つの特定のツールが非常に重要な役割を果たします。
-
柔軟でリアルタイムな安定トラフィックのスムーズな増減とロールバックを担当するFeature Flagsサービス。この記事では、FeatBitをFeature Flagsサービスとして使用しました。
-
サービスの異常を包括的に監視し、プロセス全体でタイムリーに警告を行う観測サービス。この記事では、観測ツールとしてGuanceCloudを使用しました。
FeatBitを使用してリアルタイムデータベース移行リクエストトラフィックを制御する
以下の擬似コードは、特定のサービスのデータベース読み取り操作を分割する方法を示しています。
- コードの6行目では、
_fbService.BoolVariation("read-sport-olddb")
メソッドを呼び出すことで、トラフィック制御値を取得しています。それがtrue
の場合、古いデータベースから読み取るクエリ関数を並列タスク実行キューに追加します。 - コードの9行目では、
_fbService.BoolVariation("read-sport-newdb")
メソッドを呼び出すことで、トラフィック制御値を取得しています。それがtrue
の場合、新しいデータベースから読み取るクエリ関数を並列タスク実行キューに追加します。 - コードの19行目では、FeatBit Feature Flags SDKを使用して2つのデータベース読み取り操作を同時に実行し、結果を比較して正しい値を返し、実行状況に応じて関連する例外データを観測ツールに送信します。
public async Task<List<Sport>> GetSportsByCityAsync(int cityId, int pageIndex, int pageSize)
{
var tasks = new List<Task<List<Sport>>>();
// Sport関連サービスの古いデータベースの読み込み用のFeature Flagがtrueの場合は、読み込みタスクを実行タスクキューに追加します。
if (_fbService.BoolVariation("read-sport-olddb"))
{
tasks.Add(GetSportsByCityQueryAsync(_oldDbContext, cityId, pageIndex, pageSize));
}
// Sport関連サービスの新しいデータベースの読み込み用のFeature Flagがtrueの場合も、読み込みタスクを実行タスクキューに追加します。
if (_fbService.BoolVariation("read-sport-newdb"))
{
tasks.Add(GetSportsByCityQueryAsync(_newDbContext, cityId, pageIndex, pageSize));
}
// 2つの読み取り操作を同時に実行し(新データ読み取りによる要求時間の増加を避けるため)、結果を比較して返します。
// 結果が一貫していない場合は、古いデータベースの読み取り結果を返し、不一致を記録します。
return await _fbService.RunAndCompareDbTasksAsync(
tasks,
timeoutDelayForNewDB: 3000, // ユーザーエクスペリエンスの悪化を避けるため、新しいデータベースの最大待機時間を設定
(timeoutInfo) => { }, // 新しいデータベースのコールがタイムアウトした場合は、観測ツールにメッセージを送信します
(unMatchInfo) => { }, // 結果が一貫していない場合は、観測ツールにメッセージを送信します
(exception) => { } // 例外が発生した場合は、観測ツールにメッセージを送信します
);
}
上記のコードをプロジェクトに統合した後、Feature FlagsのUIツールを使用して、データベース移行のデュアルライトおよびデュアルリードトラフィックをスケーリングできます。たとえば、特徴フラグ read-sport-from-newdb
のトラフィックスケーリングを最初に5%に調整できます。観測ツールで一定期間異常が観測されない場合、トラフィックのスケーリング率を10%に増やすことができます(下図参照)。

観測ツールを使用して、移行プロセス全体をモニタリングし、潜在的な問題をタイムリーに発見する
データ移行プロセス全体で、自動およびタイムリーなエラー検出に続くロールバックは非常に重要です。これにより、次のような多くの問題を回避できます。
-
新しいデータベースの動作が著しいシステムリソース消費をもたらす場合、Feature Flagsシステムを介して即座に回避する必要があります。
-
タイムアウトにより予想を超える書き込み操作や読み込み操作の数が発生した場合、問題を迅速に特定し、ロールバックおよび迅速な修復を行うことができます。これにより、移行速度が向上します。
-
書き込み操作や読み込み操作が情報エラーを引き起こす場合(結果の不一致、要求時間の過剰、プログラムの例外など)、観測システムを基に特定のエラー情報を素早く特定することができ、デバッグ速度を向上させることができます。
-
など
APM(アプリケーションパフォーマンスモニタリング)、RUM(リアルユーザーモニタリング)、メトリクス機能を統合した観測システムを使用して、異常データとシステム挙動を監視することができます。例としては、DataDog、Guance.one(この記事で使用されています)、などが挙げられます。
"トレース"および"エラートラッキング"を通じて移行エラーを素早く特定する
観測ツールのAPM/トレースページでは、移行プロセス中に一部の赤い項目(つまり、エラー)が発生していることがわかります。リソース列を通じて、新しいデータベースでの読み取り操作でエラーが発生していることが簡単に確認できます(下図参照)。

対応するエラーをクリックすると、それに関連するコールチェーンのフレームグラフをすばやく表示できます。フレームグラフの解釈によると:
- 図の 円1 で示されているSpanにより、ここでデータベース移行中にタイムアウトエラーが発生したことが示されており、新しいデータベースからの読み取り時間が許容可能な要求応答時間の閾値を超過したことが示されています。
- 円2 で示されている位置は、
read-sport-newdb
のFeature Flagがtrue
の場合にエラーが発生したことを示しています。これは、移行リスクを回避するためにロールバックまたは無効にする必要があるFeature Flagsを迅速に特定できることを意味します。 - 円3 のSpanにより、タイムアウトが発生したサーバーサイドAPIサービスを迅速に特定できます。APIのキャプチャされたパラメータとヘッダーは、後でのデバッグと問題の解決に役立ちます。

タイムアウト状態を回避するため、Feature Flagsを使用してリアルタイムで読み取り操作をロールバック
トレースとエラートラッキング情報に基づいて、異常なデータベース読み取り操作を迅速に見つけました。これで、FeaBit UI に戻り、上記で発見した特徴フラグ read-sport-newdb
を見つけ、その割合を前の状態に戻す必要があります。以下の図に示すように、以前に読み取りの異常が観察されなかった 5% にトラフィック割り当ての割合を 10% から戻します。

ロールバック後、以下のコードに示すように、_fbService.BoolVariation("read-sport-newdb")
の戻り値が true
になる確率は 5% のみになります。
// Sport サービスに関連する新しいデータベースからの読み取りに対する特徴フラグが true を返す場合、読み取りタスクを実行キューに追加します。
if (_fbService.BoolVariation("read-sport-newdb"))
{
tasks.Add(GetSportsByCityQueryAsync(_newDbContext, cityId, pageIndex, pageSize));
}
結論と次のステップ
この記事では、オブザーバビリティツールと特徴フラグツールを使用してデュアルライト、デュアルリード操作モードを実装することで、データベース移行リスクを軽減する基本的な方法を紹介しました。実際の運用では、多くのビジネスを扱うことがあり、人間の介入がさまざまな理由で遅いレスポンスを引き起こすことがあります。次回以降の記事では、次のような内容をさらに紹介します。