データの一貫性の保持

データ設計を行う際には、データの一貫性に対する考慮も必要です。

Kii Cloud の API は、スケーラビリティを重視している設計のため、リレーショナルデータベースとは異なり、アトミックなトランザクションをサポートしていません。そのため、データの一貫性はモバイルアプリのロジックによって保証する必要があります。

トランザクションについての詳細は、「トランザクション」(AndroidiOS) を参照してください。

トランザクションのサポートがない環境では、データの一貫性を保持するためにデータ構造や更新ルールを工夫する必要があります。

Kii Balance では、収支の合計値を KiiObject として保存せず、収支データの KiiObject を全件取得して合計値を計算し、表示しています。合計値を KiiObject として保存する設計では、データ項目を更新した際に、各収支と合計値の両方を更新する必要があり、エラー発生や複数クライアントからのデータ更新により、容易に不整合が発生します。

以下は合計値を KiiObject に保存した場合の更新の例です。書き込み 1 が成功後、書き込み 2 が失敗すると不整合になります。リレーショナルデータベースのように、書き込み 1 はロールバックされません。

以下は複数デバイスからの更新の例です。更新では「楽観的ロック」(AndroidiOS)の仕組みにより、他のデバイスからの更新を検出してエラーにする機能がありますが、整合性までは保証されません。たとえば、両デバイスでデータを読み込んだ後、書き込み A1、書き込み A2、書き込み B1、書き込み B2 が順に行われると、書き込み B2 で楽観的ロックによりエラーを検出できますが、成功した書き込み B1 はロールバックされないため、データ上、不整合となります。

この例のように、他のデータ項目の集計結果や、他のデータ項目に依存する項目が保存されていると、同時更新に失敗した際に整合性が崩れます。

整合性が崩れる現象を防止するのは非常に困難です。パフォーマンスチューニングなどの理由で集計結果や依存項目を持たせたい場合は、ロジックで工夫する必要があります。

たとえば、Kii Balance の例であれば、balance_book に支出や収入の KiiObject を書き込んだ際に、その更新時刻と合計値のセットを合計用の KiiObject として保存するなどの方法が考えられます。取得時には、balance_book を更新時刻が新しい順に 1 件だけ取得し、合計値の KiiObject に保存されている更新時刻と一致しなければ合計を再計算するなどのロジックを実装する必要があります。

また、Kii Balance のようなモバイルアプリでは、月末で当月データの更新を禁止し、そのタイミングで月ごとの合計値を持たせるなど、モバイルアプリの仕様を使った工夫も有効です。