キャッシングファクトリーデザイン


9

class XFactoryオブジェクトを作成するファクトリがありますclass X。のインスタンスXは非常に大きいため、ファクトリの主な目的は、クライアントコードに対してできるだけ透過的にインスタンスをキャッシュすることです。のオブジェクトclass Xは不変であるため、次のコードは妥当なようです。

# module xfactory.py
import x
class XFactory:
  _registry = {}

  def get_x(self, arg1, arg2, use_cache = True):
    if use_cache:
      hash_id = hash((arg1, arg2))
      if hash_id in _registry:
        return _registry[hash_id]
    obj = x.X(arg1, arg2)
    _registry[hash_id] = obj
    return obj

# module x.py
class X:
  # ...

それは良いパターンですか?(実際のファクトリーパターンではないことはわかっています。)変更すべき点はありますか?

現在、Xオブジェクトをディスクにキャッシュしたい場合があります。pickleそのために使用_registryし、オブジェクトへの参照ではなく、ピクルスオブジェクトのファイル名に値として格納します。もちろん、_registryそれ自体を永続的に保存する必要があります(おそらく、独自のピクルファイル、テキストファイル、データベース、または単にピクルファイルにを含むファイル名を与えることによってhash_id)。

現在、キャッシュされたオブジェクトの有効性は、に渡されるパラメーターだけでなくget_x()、これらのオブジェクトを作成したコードのバージョンにも依存しています。

厳密に言えば、誰かx.pyがその依存関係を変更または変更し、プログラムの実行中にそれを再ロードすると、メモリキャッシュされたオブジェクトでさえ無効になる可能性があります。これまでのところ、私のアプリケーションではありそうにないため、この危険性は無視しました。ただし、オブジェクトが永続ストレージにキャッシュされている場合は、無視できません。

私に何ができる?hash_id引数arg1とを含むタプルのハッシュ、およびarg2ファイル名と最終更新日、x.pyおよび(再帰的に)依存するすべてのモジュールとデータファイルを計算することで、より堅牢にすることができると思います。二度と役に立たなくなるキャッシュファイルを削除する_registryために、各レコードの変更された日付のハッシュ化されていない表現に追加します。

しかし、理論的には誰かがモジュールを動的にロードする可能性があるため、このソリューションでさえ100%安全ではありません。ソースコードを静的に分析することからは、私はそれについて知りません。私がすべてに出て、プロジェクト内のすべてのファイルが依存関係であると想定すると、一部のモジュールが外部のWebサイトなどからデータを取得した場合でも、メカニズムは機能しなくなります。

さらに、x.pyとその依存関係の変更の頻度が非常に高いため、キャッシュの無効化が頻繁に発生します。

したがって、私はある程度の安全性を放棄し、明らかな不一致がある場合にのみキャッシュを無効化することも考えました。これはclass X、キャッシュを無効にする変更が発生したと開発者が信じる場合は常に変更する必要があるクラスレベルのキャッシュ検証識別子を持つことを意味します。(複数の開発者と、別失効識別子がそれぞれに必要とされる。)この識別子は、一緒にハッシュされるarg1arg2とに格納されたハッシュキーの一部となります_registry

開発者は検証識別子の更新を忘れるか、既存のキャッシュが無効化されたことに気付かないclass X可能性があるため、別の検証メカニズムを追加するほうがよいように思われますX。たとえばX、がテーブルの場合、すべての列の名前を追加します。ハッシュ計算には、特性も含まれます。

このコードを書くことはできますが、重要な何かが欠けていると思います。また、このすべてをすでに実行できるフレームワークまたはパッケージがあるかどうかも疑問に思っています。インメモリキャッシングとディスクベースキャッシングを組み合わせるのが理想的です。

編集:

私のニーズはプールパターンで十分に対応できるように思えるかもしれません。しかし、さらに調査すると、そうではありません。私は違いを挙げようと思いました:

  1. オブジェクトは複数のクライアントで使用できますか?

    • プール:いいえ、各オブジェクトをチェックアウトして、不要になったときにチェックインする必要があります。正確なメカニズムは複雑かもしれません。
    • XFactory:はい。オブジェクトは不変であり、無限に多くのクライアントが一度に使用できます。同じオブジェクトの2番目のコピーを作成する必要はありません。
  2. プールサイズを制御する必要がありますか?

    • プール:多くの場合、はい。もしそうなら、そうするための戦略はかなり複雑かもしれません。
    • XFactory:いいえ。オブジェクトはオンデマンドでクライアントに配信する必要があり、既存のオブジェクトが適切でない場合は、新しいオブジェクトを作成する必要があります。
  3. すべてのオブジェクトは自由に置き換え可能ですか?

    • プール:はい、オブジェクトは通常自由に置き換え可能です(そうでない場合は、クライアントが必要とするオブジェクトを確認するのは簡単です)。
    • XFactory:絶対にありません。特定のオブジェクトが特定のクライアント要求に対応できるかどうかを確認することは非常に困難です。それは、(a)同じ引数と(b)同じバージョンのソースコードで作成された既存のオブジェクトが使用可能かどうかによって異なります。パート(b)はXFactoryで検証できないため、クライアントに支援を求めます。クライアントは2つの方法でこの責任を果たします。最初に、クライアントは指定された複数の内部バージョンカウンター(開発者ごとに1つ)のいずれかを増分できます。これは実行時に発生することはありません。ソースコードの変更により既存のオブジェクトが使用できなくなると考えた場合にのみ、これらのカウンターを変更できます。次に、クライアントは必要なオブジェクトに関する不変条件を返し、XFactoryはオブジェクトをクライアントに提供する前にこれらの不変条件が違反されていないことを確認します。これらのチェックのいずれかが失敗した場合、
  4. パフォーマンスへの影響は注意深い分析が必要ですか?

    • プール:はい。場合によっては、オブジェクト管理のオーバーヘッドがオブジェクトの作成/破棄のオーバーヘッドよりも大きいと、プールが実際にパフォーマンスを低下させることがあります。
    • XFactory:いいえ。問題のオブジェクトの計算コストは​​非常に高いことが知られており、それらをメモリまたはディスクからロードすることは、オブジェクトを最初から再計算するよりも間違いなく優れています。
  5. オブジェクトはいつ破壊されますか?

    • プール:プールがシャットダウンされたとき。リソースを(部分的に)解放するように指示された場合、または特定のオブジェクトがしばらく使用されていない場合、オブジェクトを破壊する可能性もあります。
    • XFactory:不変の違反またはカウンターの不一致のいずれかによって証明されるように、最新ではないソースコードのバージョンでオブジェクトが作成された場合。このようなオブジェクトを適切なタイミングで見つけて破棄するプロセスは非常に複雑です。さらに、すべてのオブジェクトの時間ベースの無効化を実装して、無効なオブジェクトを使用することで蓄積されたリスクを減らすことができます。XFactoryはそれがオブジェクトの唯一の所有者であることを決して確認しないので、このような無効化は、クライアントオブジェクトの追加の「バージョンカウンター」によって最適に達成されます。
  6. マルチスレッド環境にはどのような特別な考慮事項がありますか?

    • プール:オブジェクトのチェックアウト/チェックインでの衝突を回避する必要があります(2つのクライアントに対してオブジェクトをチェックアウトしたくない)
    • XFactory:オブジェクト作成時の衝突を回避する必要があります(2つの同一の要求に基づいて2つのオブジェクトを作成したくない)
  7. クライアントがオブジェクトを解放しない場合、何をする必要がありますか?

    • プール:しばらく待ってから、オブジェクトを他のユーザーが使用できるようにする場合があります。
    • XFactory:適用されません。クライアントは、オブジェクトがいつ処理されたかをXFactoryに通知しません。
  8. オブジェクトを変更する必要がありますか?

    • プール:再利用する前にデフォルトの状態にリセットする必要がある場合があります。
    • XFactory:いいえ、オブジェクトは不変です。
  9. オブジェクトの永続化に関連する特別な考慮事項はありますか?

    • プール:通常はありません。プールはオブジェクト作成のコストを節約するためのものなので、すべてのオブジェクトがメモリに保持されます(ディスクからの読み取りは目的に反します)。
    • XFactory:はい、XFactoryは複雑な計算を実行するコストを節約することを目的としているため、事前計算されたオブジェクトをディスクに保存することは理にかなっています。その結果、XFactoryは永続ストレージの一般的な問題に対処する必要があります。たとえば、初期化時に、永続ストレージに接続し、そこから現在利用可能なオブジェクトに関するメタデータを取得し、要求された場合はそれらをメモリにロードする準備をする必要があります。オブジェクトは、「存在しない」、「ディスク上に存在する」、「メモリ内に存在する」の3つの状態のいずれかになります。XFactoryの実行中、状態は一方向(このシーケンスでは右側)にのみ変化します。

要約すると、プールの複雑さはアイテム1、2、4、6、およびおそらく5、7、8にあります。XFactoryの複雑度はアイテム3、6、9にあります。唯一のオーバーラップはアイテム6であり、コアではありませんプールまたはXFactoryのいずれかの機能ですが、マルチスレッド環境で機能する必要があるすべてのパターンに共通する設計上の制約です。


1
これは間違いなく工場ではなく、実際には閉鎖されています。ファクトリーは、抽象的な仕様から具体的なタイプを作成できるようにする、間接的な構造です。これはプールです。それ自体は悪いことではありませんが、探しているオブジェクトプールであることがわかったので、プールの優れたプラクティスを読み、人々が回避するために学んだ警告と、問題の再実装を回避する方法を探すことをお勧めします。 dが苦しんだ。ここから開始:en.wikipedia.org/wiki/Object_pool_pattern
Jimmy Hoffa

1
ありがとう。この読みは非常に便利でしたが、必要なのはプールのパターンではありません。その理由を示すために質問を編集しました。
最大

回答:


4

あなたの懸念は非常に有効であり、元の簡単なキャッシングソリューションが最終的にアーキテクチャの一部になり、自然に新しいレベルの問題を引き起こしていることがわかります。

キャッシングに対する優れたアーキテクチャソリューションは、注釈をIoCと組み合わせて使用​​し、説明したいくつかの問題を解決することです。例えば:

  • キャッシュされたオブジェクトのライフサイクルをより適切に制御できます
  • (実装を変更する代わりに)アノテーションを変更することで、キャッシュ動作を簡単に置き換えることができます。
  • たとえば、メモリ、次にディスクキャッシュに格納できる多層キャッシュを簡単に構成できます
  • アノテーション自体の各メソッドのキー(ハッシュ)を定義できます

私のプロジェクト(JavaまたはC#)では、Springキャッシングアノテーションを使用しています。ここに簡単な説明があります

IoCを使用すると、必要に応じてキャッシュシステムを構成できるため、このソリューションの主要な概念です。

Pythonで同様のソリューションを実装するには、アノテーションの使用方法を見つけて、プロキシを構築できるIoCコンテナを検索する必要があります。これがアノテーションがすべてのメソッド呼び出しをインターセプトし、キャッシュのためのこの特定のソリューションを提供するために機能する方法です。


おかげで、これまでIoCについて聞いたことがありません。興味深く関連性があるようです。PythonのIoCの良い例いくつかあるようです。
最大

@max IoCはそれほど大したことではないでしょう。ただし、優れたキャッシュフレームワークを実現するには、メソッド呼び出しをインターセプトする方法(通常は自動プロキシを使用)を見つけ、アノテーションを使用して必要なキャッシュ動作を実装する必要があります。幸運を!
Alex

1

キャッシュの見方は良いです-Xはそうではありません。

単一インスタンスのIMHO逆シリアル化は、キャッシュの問題ではありません。それは相応のクラスのための仕事です。ここでの主な問題は、このクラスが頻繁に変更されることです。インスタンスをキャッシュすることの懸念とオブジェクトを逆シリアル化することの懸念を分離することをお勧めします。後者は、Xが同様に古いフォーマットを逆シリアル化できるように改善する必要があります。これは非常にトリッキーで高価な場合があります。それが高すぎる場合、Xが頻繁に変更される限り、古いバージョンをロードする必要があるかどうかを自問する必要があります。

ところでバージョン識別子は必須のようです。XIの構造についてのさらなる知識がなければ、推測はできますが、Xの構造は論理的にモジュール化されているようです(たとえば、特性について話しました)。もしそうなら、多分それはこの構造を明示的にすることを助けるでしょう。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.