ADO.NET 粒度の永続化
Orleans のリレーショナル ストレージ バックエンド コードは、汎用的な ADO.NET 機能に基づいて構築されているため、データベース ベンダーに依存しません。 Orleans データ ストレージ レイアウトについては、ランタイム テーブルで既に説明されています。 接続文字列の設定は、「Orleans 構成ガイド」で説明されているように行われます。
特定のリレーショナル データベース バックエンドで Orleans コード関数を作成するには、次が必要です:
- 適切な ADO.NET ライブラリをプロセスに読み込む必要があります。 これは通常どおりに定義する必要があります。たとえば、アプリケーション構成の DbProviderFactories 要素を使用します。
- オプションの
Invariant
プロパティを使用して、ADO.NET インバリアントを構成します。 - データベースが存在し、コードと互換性がある必要があります。 これを行うには、ベンダー固有のデータベース作成スクリプトを実行します。 詳細については、ADO.NET 構成に関するページを参照してください。
ADO .NET 粒度ストレージ プロバイダーを使用すると、リレーショナル データベースに粒度状態を格納できます。 現在、次のデータベースがサポートされています:
- SQL Server
- MySQL/MariaDB
- PostgreSQL
- Oracle
まず、基本パッケージをインストールします:
Install-Package Microsoft.Orleans.Persistence.AdoNet
対応する ADO.NET インバリアントやセットアップ スクリプトなど、データベースの構成については、ADO.NET 構成に関する記事を参照してください。
ISiloHostBuilder を使用して ADO.NET ストレージ プロバイダーを構成する方法の例を次に示します:
var siloHostBuilder = new HostBuilder()
.UseOrleans(c =>
{
c.AddAdoNetGrainStorage("OrleansStorage", options =>
{
options.Invariant = "<Invariant>";
options.ConnectionString = "<ConnectionString>";
options.UseJsonFormat = true;
});
});
基本的には、データベース ベンダー固有の接続文字列と、ベンダーを識別する Invariant
(「ADO.NET 構成」を参照) を設定するだけで済みます。 また、データを保存する形式 (バイナリ (既定値)、JSON、または XML のいずれか) を選択することもできます。 バイナリは最もコンパクトなオプションですが、不透明であり、データを読み取ったり操作したりすることはできません。 JSON がおすすめのオプションです。
AdoNetGrainStorageOptions を使って以下のプロパティを設定できます:
/// <summary>
/// Options for AdoNetGrainStorage
/// </summary>
public class AdoNetGrainStorageOptions
{
/// <summary>
/// Define the property of the connection string
/// for AdoNet storage.
/// </summary>
[Redact]
public string ConnectionString { get; set; }
/// <summary>
/// Set the stage of the silo lifecycle where storage should
/// be initialized. Storage must be initialized prior to use.
/// </summary>
public int InitStage { get; set; } = DEFAULT_INIT_STAGE;
/// <summary>
/// Default init stage in silo lifecycle.
/// </summary>
public const int DEFAULT_INIT_STAGE =
ServiceLifecycleStage.ApplicationServices;
/// <summary>
/// The default ADO.NET invariant will be used for
/// storage if none is given.
/// </summary>
public const string DEFAULT_ADONET_INVARIANT =
AdoNetInvariants.InvariantNameSqlServer;
/// <summary>
/// Define the invariant name for storage.
/// </summary>
public string Invariant { get; set; } =
DEFAULT_ADONET_INVARIANT;
/// <summary>
/// Determine whether the storage string payload should be formatted in JSON.
/// <remarks>If neither <see cref="UseJsonFormat"/> nor <see cref="UseXmlFormat"/> is set to true, then BinaryFormatSerializer will be configured to format the storage string payload.</remarks>
/// </summary>
public bool UseJsonFormat { get; set; }
public bool UseFullAssemblyNames { get; set; }
public bool IndentJson { get; set; }
public TypeNameHandling? TypeNameHandling { get; set; }
public Action<JsonSerializerSettings> ConfigureJsonSerializerSettings { get; set; }
/// <summary>
/// Determine whether storage string payload should be formatted in Xml.
/// <remarks>If neither <see cref="UseJsonFormat"/> nor <see cref="UseXmlFormat"/> is set to true, then BinaryFormatSerializer will be configured to format storage string payload.</remarks>
/// </summary>
public bool UseXmlFormat { get; set; }
}
ADO.NET 永続化には、データをバージョン管理し、任意のアプリケーション ルールとストリーミングを使用して任意のシリアライザー (または逆シリアライザー) を定義する機能がありますが、現在、アプリケーション コードに公開するメソッドはありません。
ADO.NET 永続化の根拠
ADO.NET バックアップされた永続化ストレージの原則は次のとおりです:
- データ、データの形式、コードの進化に伴い、ビジネスクリティカルなデータを安全かつアクセス可能な状態に保ちます。
- ベンダー固有およびストレージ固有の機能を利用します。
実際には、これは、ADO.NET 実装の目標に従うことを意味し、ストレージ内のデータの形状を進化させる ADO.NET 固有のストレージ プロバイダーに実装ロジックを追加しました。
通常のストレージ プロバイダー機能に加えて、ADO.NET プロバイダーには次の機能が組み込まれています:
- ラウンド トリップ状態のときに、ストレージ データを 1 つの形式から別の形式 (JSON からバイナリなど) に変更します。
- 保存またはストレージから読み取る型を任意の方法で整形します。 これにより、状態のバージョンを進化させることができます。
- データベースからデータをストリーミングします。
1.
と 2.
の両方は、粒度 ID、粒度の種類、ペイロード データなどの任意の決定パラメーターに基づいて適用できます。
これは、シリアル化形式 (単純バイナリ エンコード (SBE) など) を選択し、IStorageDeserializer と IStorageSerializer を実装できるようにするためです。 組み込みのシリアライザーは、次のメソッドを使用してビルドされています:
- OrleansStorageDefaultXmlSerializer
- OrleansStorageDefaultXmlDeserializer
- OrleansStorageDefaultJsonSerializer
- OrleansStorageDefaultJsonDeserializer
- OrleansStorageDefaultBinarySerializer
- OrleansStorageDefaultBinaryDeserializer
シリアライザーが実装されている場合は、AdoNetGrainStorage の StorageSerializationPicker プロパティにシリアライザーを追加する必要があります。 IStorageSerializationPicker
の実装は次のとおりです。 既定では StorageSerializationPicker
が使用されます。 データ ストレージ形式の変更やシリアライザーの使用例については、「RelationalStorageTests」を参照してください。
現在、フレームワークで作成された AdoNetGrainStorage
にアクセスするメソッドがないため、シリアル化ピッカーを Orleans アプリケーションに公開するメソッドはありません。
設計の目標
1. ADO.NET プロバイダーを持つバックエンドの使用を許可する
これは、.NET で使用できる可能な最も広範なバックエンド セットをカバーする必要があります。これは、オンプレミスのインストールの要因です。 一部のプロバイダーは「ADO.NET の概要」に記載されていますが、 Teradata など、一部のプロバイダーは一覧表示されません。
2. デプロイの実行中でも、必要に応じてクエリとデータベース構造を調整する可能性を維持する
多くの場合、サーバーとデータベースは、クライアントとの契約関係で第三者によってホストされます。 仮想化されたホスティング環境では、近隣の騒音やハードウェアの不具合など、予期せぬ要因によってパフォーマンスが変動することは珍しい状況ではありません。 Orleans バイナリ (契約上の理由で) またはアプリケーション バイナリを変更および再デプロイできない場合がありますが、通常はデータベースのデプロイ パラメーターを微調整できます。 バイナリなどの標準コンポーネントOrleansを変更するには、特定の状況で最適化するためのより長い手順が必要です。
3.ベンダー固有およびバージョン固有の機能を利用できるようにする
ベンダーは、製品内にさまざまな拡張機能と機能を実装しています。 これらの機能を利用できる場合は、適切に利用できます。 これらは、PostgreSQL のネイティブ UPSERT や PipelineDB、SQL Server の PolyBase またはネイティブ コンパイル テーブルとストアド プロシージャなどの機能です。
4. ハードウェア リソースの最適化を可能にする
アプリケーションを設計する際、どのデータを他のデータよりも高速に挿入する必要があるのか、どのデータをコールド ストレージに入れた方がより安価になるのか (例えば、SSD と HDD の間でデータを分割する) を予測できることがよくあります。 その他の考慮事項としては、データの物理的な場所 (一部のデータの方がコストが高くなる場合があります (SSD RAID の場合は HDD RAID など)、セキュリティで保護されている場合など)、その他の決定基準が含まれます。 ポイント 3 に関連して、一部のデータベースでは、SQL Server のパーティション テーブルやインデックスなど、特別なパーティション分割スキームが提供されています。
これらの原則は、アプリケーションのライフ サイクル全体に適用されます。 Orleans 自体の原則の 1 つが高可用性であることを考慮すると、Orleans のデプロイを中断することなくストレージ システムを調整することも、データやその他のアプリケーション パラメーターに従ってクエリを調整することも可能である必要があります。 動的な変更の例は、Brian Harry のブログ記事で確認できます:
テーブルが小さい場合、クエリ プランの内容はほとんど関係ありません。 中程度であれば、問題ないクエリ プランでも、巨大 (数百万行、数十億行) になると、わずかなクエリ プランのばらつきが命取りになります。 このような理由から、機密性の高いクエリには大きなヒントが与えられます。
5. 組織で使用されるツール、ライブラリ、またはデプロイ プロセスに関する前提条件はない
多くの組織は、 Dacpac や Red Gate など、特定のデータベース ツールのセットに精通しています。 データベースをデプロイするには、アクセス許可または DBA ロールのユーザーなどのユーザーがデータベースをデプロイする必要がある場合があります。 通常、これは、アプリケーションが負荷の見積もりで使用するために生成するクエリのターゲット データベース レイアウトと大まかなスケッチも持つことを意味します。 スクリプトベースのデプロイを義務付ける業界標準の影響を受けるプロセスが存在する可能性があります。 外部スクリプトでクエリとデータベース構造を使用することでこれが可能になります。
6. ADO.NET ライブラリと機能を読み込むのに必要な最小限のインターフェイス機能セットを使用する
これはどちらも高速であり、ADO.NET ライブラリの実装の不一致にさらされる表面が少なくなります。
7. デザインをシャード化可能にする
たとえば、リレーショナル ストレージ プロバイダーで意味がある場合は、デザインを簡単にシャード化できるようにします。 たとえば、これはデータベースに依存するデータを使用しない (例: IDENTITY
) を意味します。 行データを区別する情報は、実際のパラメーターからのデータのみに基づいて構築する必要があります。
8.デザインをテストしやすいものにする
新しいバックエンドの作成は、既存のデプロイ スクリプトの 1 つをターゲットとするバックエンドの SQL 言語に変換し、新しい接続文字列をテストに追加し (既定のパラメーターを想定)、特定のデータベースがインストールされているかどうかを確認してから、それに対してテストを実行するのと同じくらい簡単に行えるのが理想的です。
9. 前の点を考慮して、新しいバックエンドの移植スクリプトと、既にデプロイされているバックエンド スクリプトの変更の両方を可能な限り透過的にする
目標の実現
Orleans フレームワークでは、デプロイ固有のハードウェア (アクティブなデプロイ中に変更される可能性があるハードウェア)、デプロイ ライフサイクル中のデータの変更、特定の状況でのみ使用できる特定のベンダー固有の機能については認識されません。 このため、データベースと Orleans の間のインターフェイスは、これらの目標を達成し、誤用に対して堅牢にし、必要に応じて簡単にテストできるように、抽象化とルールの最小セットに従う必要があります。 ランタイム テーブル、クラスター管理、具体的なメンバーシップ プロトコルの実装。 また、SQL Server 実装には、SQL Server エディション固有のチューニングが含まれています。 データベースと Orleans の間のインターフェイス コントラクトは、次のように定義されます:
- 一般的な考え方は、データは Orleans 固有のクエリを介して読み取られ、書き込まれるということです。 Orleans は、読み取り時の列名と型、および書き込み時のパラメーター名と型に対して動作します。
- 実装では、入力と出力の名前と型を保持する必要があります。 Orleans では、これらのパラメーターを使用して、名前と型でクエリ結果を読み取ります。 ベンダー固有および展開固有のチューニングが許可され、インターフェイス コントラクトが維持されている限り、コントリビューションが推奨されます。
- ベンダー固有のスクリプト間での実装では、制約名を保持する必要があります 。 これにより、具体的な実装全体で一様な名前付けにより、トラブルシューティングが簡素化されます。
- Orleans の場合は、アプリケーション コードのバージョン (または ETag) が一意のバージョンを表します。 実際の実装の型は、一意のバージョンを表す限り重要ではありません。 実装では、Orleans コードは符号付き 32 ビット整数を想定しています。
- 明示的であいまいさをなくすために、Orleans ではいくつかのクエリが TRUE を >0 値として返すか、FALSE を = 0 値として返すことが期待されます。 つまり、影響を受ける行または返される行の数は関係ありません。 エラーが発生した場合、または例外がスローされた場合、クエリではトランザクション全体がロールバックされ、FALSE を返すか例外を伝達する可能性があることを確認する必要があります。
- 現在、1 つのクエリ以外はすべて単一行の挿入または更新です (ただし、関連付けられた
SELECT
クエリが最後の書き込みを実行した場合、UPDATE
クエリはINSERT
に置き換えられます)。
データベース エンジンでは、データベース内プログラミングがサポートされています。 これは、実行可能スクリプトを読み込み、それを呼び出してデータベース操作を実行するという考え方に似ています。 擬似コードでは、次のように表現できます:
const int Param1 = 1;
const DateTime Param2 = DateTime.UtcNow;
const string queryFromOrleansQueryTableWithSomeKey =
"SELECT column1, column2 "+
"FROM <some Orleans table> " +
"WHERE column1 = @param1 " +
"AND column2 = @param2;";
TExpected queryResult =
SpecificQuery12InOrleans<TExpected>(query, Param1, Param2);
これらの原則は、データベース スクリプトにも含まれています。
カスタマイズされたスクリプトの適用に関するいくつかのアイデア
OrleansQuery
でIF ELSE
との粒度永続化のためにスクリプトを変更し、一部の状態が既定のINSERT
を使用して保存されるようにします。ただし一部の粒度状態ではメモリ最適化テーブルが使用される場合があります。SELECT
クエリは、それに応じて変更する必要があります。1.
のアイデアを使用すると、SSD
またはHDD
間でデータを分割したり、暗号化されたテーブルにデータを配置したり、SQL-Server-to-Hadoop、さらにはリンク サーバーを介して統計データを挿入したりするなど、別のデプロイまたはベンダー固有の側面を利用できます。
変更されたスクリプトは、Orleans テスト スイートを実行してテストすることも、SQL Server の単体テスト プロジェクトを使用してデータベース内で直接テストすることもできます。
新しい ADO.NET プロバイダーを追加するためのガイドライン
- 上記の目標の実現に関するセクションに従って、新しいデータベース セットアップ スクリプトを追加します。
- ベンダーの ADO 不変名を AdoNetInvariants に追加し、ADO.NET プロバイダー固有のデータを DbConstantsStore に追加します。 これらは、一部のクエリ操作で使用される可能性があります。 たとえば、正しい統計挿入モードを選択する場合 (つまり、
FROM DUAL
の有無にかかわらずUNION ALL
)。 - Orleans には、メンバーシップ、リマインダー、統計など、すべてのシステム ストアに対する包括的なテストがあります。 新しいデータベース スクリプトのテストを追加するには、既存のテスト クラスをコピーして貼り付け、ADO の不変名を変更します。 また、ADO 不変式のテスト機能を定義するために 、RelationalStorageForTesting から派生させます。
.NET