UserA と UserB は objectA.filedA objectA.filedB をそれぞれ同時に変更しています。それらは同じフィールドを変更していないため、重複はないと考えるかもしれません。本当?または pm.makePersistnace() の実装は、実際にはオブジェクト全体をオーバーライドします...知っておくと便利です...
2 に答える
これはあなたが思い描いていることですか?
- アリスはオブジェクトを取得します。{ a = 1, b = "フー" }
- ボブはオブジェクトを取得します。{ a = 1, b = "フー" }
- Alice は、b を変更してオブジェクトを変更します。{ a = 1, b = "バー" }
- Bob は、a を変更してオブジェクトを変更します。{ a = 2, b = "フー" }
- Alice はオブジェクトのコピーを永続化します。{ a = 1, b = "バー" }
- Bob はオブジェクトのコピーを永続化します。{ a = 2, b = "フー" }
Bob のオブジェクトのコピーは、変更されたフィールドのセットだけでなく、オブジェクト全体を保持しているため、データストア内のコピーを上書きします。または、一般に、最後に永続化されたオブジェクトのオブジェクト全体がデータストアに永続化されます。
これは、トランザクションで get-set-and-persist 操作をそれぞれ実行することで修正できます。App Engine トランザクションは、オブジェクト全体をローカルで取得または変更できないようにロックするのではなく、他のユーザーが永続化できないようにするだけです。そう:
- Alice はトランザクションでオブジェクトを取得します。{ a = 1, b = "フー" }
- Bob はトランザクションでオブジェクトを取得します。{ a = 1, b = "フー" }
- Alice は、b を変更してオブジェクトを変更します。{ a = 1, b = "バー" }
- Bob は、a を変更してオブジェクトを変更します。{ a = 2, b = "フー" }
- Aliceはオブジェクトを永続化しようとしますが、できません。Bob がトランザクションでオブジェクトを開いているためです。例外がスローされ、アリスはトランザクションを終了して再試行することでこれをキャッチします...
- Alice がトランザクション { a = 2, b = "Foo" } を終了したため、Bob は問題なくオブジェクトを永続化します。
- Alice は、もう一度取得してトランザクションを再試行します。{ a = 2, b = "フー" }
- Alice は、b を変更してオブジェクトを変更します。{ a = 2, b = "バー" }
- アリスはオブジェクトを永続化します。他の誰もトランザクションを開いていないため、オブジェクトは機能します。{ a = 2, b = "バー" }
どのユーザーが例外を受け取るかは完全にはわかりませんが、例外が発生したときに再試行する意思がある限り、最終的にはオブジェクトに変更を加えて永続化することができます。
これは楽観的ロックと呼ばれます。
ご回答有難うございます。残念なことに、makePersistence() の実装では、変更されたフィールドだけでなく、オブジェクト全体をデータストアに書き込みます。この事実は、実際には、GAE でのすべての共有オブジェクトの更新に、ルールとしてトランザクションを使用することを強制しています。さらに - このような場合、トランザクションで例外が発生する可能性があるため、「再試行メカニズム」を実装する必要があります。
だから... GAEで共有オブジェクトを更新すると、常にこれらのエクストラが必要になります:
- トランザクション内で行う
- 再試行メカニズムを実装する
サイトにある Google の例のほとんどは、実際にはそれを考慮していません。ほとんどのアプリは共有オブジェクトを使用しないと仮定しているかのように
例 ( http://code.google.com/appengine/docs/java/datastore/creatinggettinganddeletingdata.html ):
public void updateEmployeeTitle(User user, String newTitle) {
PersistenceManager pm = PMF.get().getPersistenceManager();
try {
Employee e = pm.getObjectById(Employee.class, user.getEmail());
if (titleChangeIsAuthorized(e, newTitle) {
e.setTitle(newTitle);
} else {
throw new UnauthorizedTitleChangeException(e, newTitle);
}
} finally {
pm.close();
}
}
また:
public void updateEmployeeTitle(Employee e, String newTitle) {
if (titleChangeIsAuthorized(e, newTitle) {
e.setTitle(newTitle);
PersistenceManager pm = PMF.get().getPersistenceManager();
try {
pm.makePersistent(e);
} finally {
pm.close();
}
} else {
throw new UnauthorizedTitleChangeException(e, newTitle);
}
}