この時点ではまだ、クライアントが他のクライアントによって更新された値を待つために、どのように CAS の値を利用するのか疑問に思われるかもしれません。
本質的には、 CAS値はクライアントがアイテムのIDに対するCAS値を"保持"し、CASが変わっていないときだけアイテムを更新することを可能とします。すなわち、 Check And Set (CAS) です。マルチクライアント環境下で、あるクライアントが、他のクライアントによって既に更新されている可能性があるアイテムの値を上書いてしまうことを防ぐために設計されています。
残念ながらアイテムをロックする方法はありません。個々の操作(例えばset)はアトミックな操作ですが、複数の操作はアトミックではなくなります、これこそ CAS が保護する対象として設計されたものです。最後の GET 以降に更新された値を上書きすることを防止できます。
このような状況を示すために、processInput
メソッドに CAS
操作を実行するボールドの行を追加してみましょう。そしてプログラムを2つ同時に起動し、この操作がどのように影響し合うのか確認してみましょう。
} else if (input.startsWith("/who")) { System.out.println("Users connected: " + client.get("CurrentUsers")); } else if (input.startsWith("/cas")) { runCasTest(); } else {
runCasTest()
メソッドをクラスの最下部に追記します:
private static void runCasTest() { System.out.println("Testing a CAS operation."); CASValue<Object> cas = client.gets("CasTest"); if (cas == null) { // Must create it first System.out.println("Creating CasTest value."); client.set("CasTest", 120, "InitialValue"); return; } System.out.println("CAS for CasTest = "+cas.getCas()); System.out.println("Sleeping for 10 seconds."); try { Thread.sleep(10000); } catch (InterruptedException e) { } CASResponse response = client.cas("CasTest", cas.getCas(), "ReplacedValue"); if (response.equals(CASResponse.OK)) { System.out.println("OK response."); } else if (response.equals(CASResponse.EXISTS)) { System.out.println("EXISTS response."); } else if (response.equals(CASResponse.NOT_FOUND)) { System.out.println("NOT_FOUND response."); } cas = client.gets("CasTest"); System.err.println("CAS after = "+cas.getCas()); }
初めてこのテストを実行した時(アプリケーション実行中に"/cas"と入力)、gets()
メソッドは null を返します。そして CasTest
キーは"InitialValue"に設定されます。二回目にこのテストを実行した場合、
gets()
メソッドは
CASValue
<Object>
のインスタンスを返し、その値を出力します、そして10秒間スリープします。スリープ後、
client.cas()
メソッドを呼び出し、値を書き換えます。
これを二つの異なるウィンドウで、適切なタイミングで実行すると、次のような出力になるでしょう:
/cas /cas Testing a CAS operation. CAS for CasTest = 2637312854241 Sleeping for 10 seconds. OK response. CAS after = 2850841875395
二番目のアプリケーションでは、次のような出力になります。
Testing a CAS operation. CAS for CasTest = 2637312854241 Sleeping for 10 seconds. EXISTS response. CAS after = 2850841875395
CAS が 2637312854241
の時に、二つ目のクライアントが一つ目のクライアントがスリープを終えるより先に値を更新しています。OKの結果を得る代わりに、クライアントには既に更新されていることを示す
EXISTS の結果が返り、 client.cas()
操作は失敗します。そしてコードはこの状況を正しく処理する必要があります。
ロックは分散キャッシュでは利用できません。ロックは競合を起し、複雑性が増します。ロックを取得せずに値が既に変更されたことを素早く検知できることはこの問題に対するシンプルな解決策です。ユーザにエラーを返したり、CAS値を再取得して操作をリトライしたり、この状況を処理するコードを書くことが出来ます。