Eugenio Pace – Software as a Service Architecture Guidance
Published Monday, April 14, 2008 7:31 PM by eugeniop
LitwareHR on SSDS – Part V – Searching across Containers
In SQL Server Data Services, the scope of a query is bound to a Container, but in LitwareHR we had a requirement of searching entities across multiple tenants, and because in our implementation each tenant gets its own Container, we had to create a way of performing queries (the same query to be more precise) that spanned across Containers.
SQL Server Data Services において、クエリーのスコープは Container にバインドされているが、LitwareHR では、マルチ・テナントに対応した、エンティティの検索という要件を持っている。さらに、その実装においては、それぞれのテナントが自身の Container を持つため、Container 間をまたぐように、クエリーを実行する方式(これまでクエリーを更に正確にする)が必要となる。
Remember, LitwareHR actually "owns" all containers. LitwareHR’s metadata (which is simply another Container as explained in a previous post), contains information about the tenant including a pointer to the tenant’s Container.
現実に、LitwareHR では、すべてのコンテナが「own」されることを、忘れないで欲しい。 LitwareHR のメタデータ(前回のポストで説明したように、メタデータも別のコンテナである)は、テナントに関する情報を取り込むが、そこには、テナントが所有するコンテナに対するポインタも含まれる。
The trivial way of implementing a cross-Container search would be to iterate on all tenant containers and query each of them. Something like this:
クロス・コンテナ検索を実装するための普通の方法は、すべてのテナント・コンテナ上でイテレーションを行い、それぞれに対してクエリーを実行するものになるだろう。 以下のような感じになる:
foreach (Container c in TenantContainerCollection)
{
IEnumerable<Entity> result = proxy.Query(CreateScope(c.ContainerId), query);
CombinedResults.Merge(result);
}
return CombinedResults;
Naturally, this doesn’t scale very well and is not taking advantage of the fact that this is a highly parallel problem. In fact, most probably each Query sent to SSDS will end up in a different node of the SSDS fabric, minimizing chances of server side contention.
必然的に、スケールを上手く処理するものにはならず、また、パラレルに関する大きな問題であるという事実において、SSDS のアドバンテージを活用するのにもなっていない。 現実的に考えても、おそらく SSDS に送られる Query は、SSDS ファブリックの別のノードで終わり、サーバー・サイドにおける競合を最小化するだろう。
So we created a helper class: CrossTenantSearch, that essentially takes an array of TenantId’s, a query statement (SLINQ) and returns the combined result sets.
そのため、ヘルパークラスである CrossTenantSearch を作成した。それは本質的に、TenantId の配列を取得し、クエリー・ステートメント(SLINQ)を発行し、結合された結果をリターンするものとなる。
Internally, it will asynchronously launch several concurrent searches, each one on a different thread of a ThreadPool and then will wait for all of them to complete, merge the partial results into a single collection and return.
内部的には、いくつかのコンカレントな検索が、ThreadPool の個々のスレッド上で非同期に開始される。そして、それら全てが完了するのを待って、シングル・コレクションに部分的な結果をマージし、最後にリターンが行われる。
The following diagram illustrates an example of running 3 concurrent queries (with different colors to identify each thread). Notice how the red thread returns even before the 3rd thread is launched, the green thread is longer (potentially returning a lot of results) and the yellow is very short.
以下のダイアグラムは、3つのコンカレント・クエリー(スレッドを識別するために色を変えている)を実行するための例を示している。 3番目のスレッド(黄色)が開始される前であっても、赤スレッドがリターンされる状況に注意すべきだ。緑のスレッドはLONGであり(多くの結果をリターンする可能性を持つ)、黄色のスレッドは極端にSHORTである。
After all of them return, results are combined into a single result set and sent back to the requestor. With this approach, the max time for a query is approximately the same as the longest query you have (e.g. the green thread in the diagram above). Caveat: this might not be valid for very large sets of containers, that is when there’s a large number of containers you are searching in, because you might get contention issues on the client side and also the merging of all the results might take a lot of time.
それら全てがリターンされた後に、結果がシングル・リザルト・セットに結合され、requestor にリターンされる。このアプローチを用いることで、クエリーの最大時間は、最も長いクエリーと(上記の緑スレッド)、ほぼ同じになる。警告:この方式は、きわめて大きなコンテナ・セットに対して、つまり、膨大な数のコンテナを検索するときには、有効にならないかもしれない。なぜなら、クライアント・サイドで競合が発生する可能性があり、さらには、すべての結果をマージするために、長い時間が消費される可能性があるからだ。
There is of course room for improvements: paging, returning to the client partial results and allow the client to drive further fetches, etc. None of these have been implemented in LitwareHR and are left as an exercise to the reader.
もちろん、改善の余地はある。 具体的には、ページングや、部分的な結果のクライアントに対するリターン、そして、クライアントにおける先読みなどが考えられる。これらの全てが、LitwareHR には実装されず、また、読者の課題として残されている。
Side note: when developing this feature we were puzzled by an exception we got related to thread apartments. It turns out by default, Visual Studio test runner runs in STA. There’s a somewhat obscure configuration parameter that you need to add to the testrunconfig file manually, as it is not exposed in the configuration window:
<ExecutionThread apartmentState="MTA"/>
See here for more info.
<完>








