PythonでのFirebirdのマルチマスターレプリケーション

Firebird DBMSで実行されている2つのデータベースを同期するタスクがありました。 状況は簡単にそのようなものです。



互いに数キロメートル離れた2つの店舗で営業する会計プログラムがあります。 インターネット接続-私たちの現実に対応した信頼性と速度を備えたさまざまなプロバイダーを通じて。 いずれの場合も、プロバイダーを通信品質の低いより高価なプロバイダーにのみ変更できるため、いずれかのストアにベースを配置し、別のストアからリモートで接続してもソースは機能しません。 各ストアでは、受信ドキュメントと送信ドキュメントが入力され、ディレクトリが編集され、他のビジネスアクティビティが保持されます。 入力情報にはある程度の価値はありますが、商業的な価値があるため、送信データのセキュリティの問題も無視できません。 このような入門書を受け取ったので、私は考えに行きました。 私は思考の結果を共同体裁判所に提出します。







内側から作業する



Python firebirdレプリケーション

まず、すべてのデータが複製されるわけではありません。 サービステーブル、ログテーブル、プライマリドキュメントのテーブルから自動的に生成されるレジストリとジャーナルがあります-除外する必要があります。 そのため、最初に同期するものを決定します。 これを行うために、テーブルRPL_TABLESには、同期されたテーブルの名前とサービス情報(すべてのフィールドを複製する必要があるかどうか、およびこのテーブルがドキュメントヘッダーかどうか)が含まれます(これはデータベースのロジックに影響します)。 テーブルRPL_FIELDSには、それぞれ、完全に同期する必要がないテーブルのフィールド名が格納されます。



次に、この情報に基づいて、指定されたテーブルのすべての変更を記録するために必要なトリガーを生成する3つの手順を作成しました。 それらのうちの2つ( RPL_ALLFIELDSおよびRPL_SOMEFIELDS )はトリガーを生成します。トリガーは、他のレプリケーション参加者が実行するSQLクエリを生成します。 それらを記述する際の最も難しい部分は、引用符の数を間違えないことでした:) 3番目-RPL_INSTALL-上記の設定テーブルを分析し、それらに必要な手順を開始します。 これらのプロシージャを作成するとき、多数のexecuteステートメント / executeブロックを使用しました。これにより、とりわけ、プロシージャ内からデータベースメタデータを変更できます。



その結果、プログラムのユーザーが行ったすべての変更は、ユーザー(ユーザー)が変更(変更)を行った場所、時期、方法に関係なく、RPL_LOGテーブルに保存されます。 次に、いくつかのデータベース間の相互作用に移りましょう。



同期は「スター」スキームに従って実行されます。各データベースは同期サーバーに接続され、最後の同期セッションの瞬間から変更をアップロードし、他のクライアントデータベースから変更セットを受信し、それ自体に適用して切断されます。 サーバーは、それ自体がまったく同じように動作し、両方がクライアントです。

これは詳細に発生します。 各クライアントデータベースにはRPL_SESSIONSテーブルがあります。 レプリケーションセッションの開始時に、新しいセッションの開始時にそれをマークします。その識別子は、生成された各リクエストのRPL_LOGテーブルに格納されます。 その後、RPL_LOGテーブルから、以前のセッションで蓄積されたすべてのリクエストが選択され、ファイルに書き込まれ(問題が発生した場合のさらなる分析のために、万が一に備えて)、RPL_BLOBテーブルのサーバーに注がれます。 Blobとともに、データベース識別子とセッション番号が書き込まれます。 他のデータベースからの同様のブロブは、サーバーからさらに抽出され、データベースに適用されます。 その後、各隣接データベースの最後に正常に同期されたセッションの数に関する情報がクライアントのテーブルRPL_DATABASESに記録され、(再び問題分析の場合)ベース、ブロブ、セッション、および同期時間の識別子がテーブルRPL_RECEIVEDに書き込まれます。



外で働く





拠点間相互作用は、単純なpythonスクリプトを使用して行われます。 実際、これは私の最初の有効なPythonスクリプトです。そのため、ソリューションのアプローチ、構文、曲率についてforられないでください。 スクリプトは条件付きで2つの部分に分けることができます- データベースの操作を簡単にするクラス と、上記のリクエストの順次実行です



このスクリプトは、システムスケジューラによって15分ごとに実行され、実際に示されているように、同期中に接続および切断できないことを正常に処理します。 最初のケースでは、タイムアウトによって単純にクラッシュします。2番目のケースでは、既存のデータを挿入しようとすると、データベースは主キーの制限によりそれらを破棄します。



データベースが実行されているサーバー間のセキュリティを確保するために、VPNが作成され、すべての作業はそれを介してのみ行われます。 さらに、データベースのレプリケーション用に別のユーザーが作成されており、RPL_ *テーブルのみに対する読み取り権限があります。



改善





現時点では、異なるデータベースで同じデータが同時に変更されている場合、レプリケーションでは状況を正しく解決できません。 既存のアプローチでは、2つのデータベースは単純に「情報を交換」します。複製後の最初のデータベースでは、2番目のデータベースからデータが保存されます。 異なるデータベースの同じデータが編集されることは非常にまれであり、原則として1人が編集するため、この制限は重要ではありません。 そのため、誰かがストアからストアへと移動する間、次のレプリケーションセッションを既に実行している可能性があります。 しかし、それにもかかわらず、問題があり、それは解決されます。



まとめ





信頼できない通信チャネルで接続されたリモートサーバー間でデータを同期するために、必要に応じて許容可能な速度と信頼性を提供するソリューションが作成されました。 さらに、このソリューションは特定のデータベースに関連付けられていないため、実質的に変更を加えずに他のデータベースに簡単に使用できます。 さらに、同期サーバーが完全に独立したデータベースであり、RPL_ *テーブルのみが存在するという意味で、ソリューションは自給自足です。 これは、同期が必要なすべてのデータベースサーバーがNATの背後にあり、そこから抜け出す方法がない場合に役立ちます(例はウクライナのモバイルインターネット)。 さらに、このソリューションはデータベースで動作するプログラムの変更を必要とせず、データベースサーバーにインストールされているOSに依存しません-firebird、python、および(オプションで)vpnのみを起動できる場合。



*実際、唯一のバインディングはRPL_TABLESテーブルのis_docheaderオプションです。これは、対応するテーブルにコミットされたフィールドが存在することを意味し、このフィールドのみが変更されるとクエリを生成します。



ソリューションを自分で試してみたい人は、 完全なSQLスクリプトPythonクライアントをファイル全体でダウンロードできます。 見つかったアイデア、批判、バグに感謝します。



All Articles