App EngineのJDOドキュメントを可能な限り忠実に守っていますが、Boardオブジェクトに含まれている永続コレクションの読み込みに奇妙な問題と一貫性のない問題が発生しています。「結果整合性」を手動で存在しないものとして指定した後、ローカルの開発Webサーバーでも不整合が発生します。
作成した読み込みヘルパーメソッドを使用してオブジェクト/コレクションを読み込むと、問題なく読み込まれることがあります。空のコレクションが返される場合もあります(データがプロキシオブジェクトに遅延ロードされただけではないことを確認するために、getterメソッドでコレクションに「触れている」ことに 注意してください)。
当初、この問題は高レプリケーションストレージエンジンの「結果整合性」の欠点が原因であると考えていましたが、LocalServiceTestHelperで結果整合性が0%の独自のポリシーを作成した後は、そうではないと確信しています。
この問題を例示するJUnitテストを作成しました。基本的に、testInsertUser関数でダミーのUserオブジェクトとBoardオブジェクトを作成して保存しようとします。新しく作成したPlayedTileオブジェクトのArrayListをこのボードにアタッチしてから、Google App Engineの永続マネージャーを使用してユーザー(およびボードとPlayedTileコレクション)をデータストアに保存するDataMaster.saveUserヘルパーメソッドを実行します。次の方法では、そのユーザー(BoardとPlayedTileコレクションを含む)をロードして、保存された結果を表示しようとします。混沌が続く。
JUnitコードは次のとおりです。
package com.astar.wordswall.test.data;
import java.util.ArrayList;
import com.astar.wordswall.data.DataMaster;
import com.astar.wordswall.data.jdo.Board;
import com.astar.wordswall.data.jdo.User;
import com.astar.wordswall.data.jdo.PlayedTile;
import com.astar.wordswall.test.appengine.LocalCustomHighRepPolicy;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;
import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
// import com.google.gwt.user.client.Random;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class SaveUsersBoardWithTilesTest {
Key userKey;
private final LocalServiceTestHelper helper =
new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig()
.setAlternateHighRepJobPolicyClass(LocalCustomHighRepPolicy.class));
@Before
public void setUp() {
helper.setUp();
}
@After
public void tearDown() {
helper.tearDown();
}
@Test
public void testInsert1() {
testInsertUser();
testReadUser();
}
/**
* Creation and insertion of a user, board, and linked set of tiles into BigTable.
*/
private void testInsertUser() {
User u = new User("Simon");
Board b = new Board();
ArrayList<PlayedTile> tiles = new ArrayList<PlayedTile>(7);
u.setBoard(b);
b.setPlayedTiles(tiles);
for (int j = 0; j < 7; j++) tiles.add(new PlayedTile('T'));
DataMaster.saveUser(u);
// Retrieve the user's key so that we can read him from the database later
userKey = u.getUserKey();
// Display all of our saved tiles:
System.out.println("Saved tiles:");
// Note that "getTileString()" just iterates through each Played tile printing the letter
System.out.println("\t" + u.getBoard().getTileString());
}
/**
* A typical read of a user object from the Datastore.
*/
private void testReadUser() {
User u = DataMaster.getUserWithBoard(userKey);
// Display all of our saved tiles:
System.out.println("Loaded tiles:");
System.out.println("\t" + u.getBoard().getTileString());
}
}
そして、実際にJDOの読み込みを実行する関連するDataMaster.getUserWithBoard静的関数は次のとおりです。
/**
* Loads a uniquely specified User and their associated board from
* the Datastore. It also loads the board's complete list of PlayedTiles.
* @param userKey the unique key assigned to this user
*/
public static User getUserWithBoard(Key userKey){
User u = null;
PersistenceManager pm = PMF.get().getPersistenceManager();
try{
u = pm.getObjectById(User.class, userKey);
// In order for the board and tile collection to load, we must "touch" it while PM is active
if (u.getBoard().getPlayedTiles().size() != 0) u.getBoard().getPlayedTiles().get(0);
if (u.getBoard().getPlayedWords().size() != 0) u.getBoard().getPlayedWords().get(0);
} finally{
pm.close();
}
return u;
}
奇妙なことに、このコードSOMETIMESは期待どおりに機能します。つまり、testReadUser()でデータストアからタイルを読み込んだ後、保存したタイルのセットとまったく同じものを出力します。時々、空のコレクションをロードするだけですが、特に奇妙なのは、u.getBoard()。getPlayedWords()。get(0)呼び出しがnullポインター例外をスローしないことです。
出力はの間で振動します
正しい:
Saved tiles:
T T T T T T T
Loaded tiles:
T T T T T T T
そして間違っている:
Saved tiles:
T T T T T T T
Loaded tiles:
完全に無計画に。
誰かがこれに光を当てることができますか?それは私を完全に夢中にさせています。:)
編集:もう1つの奇妙な手がかり/ファクトイドは、testSaveUserメソッドとtestReadUserメソッドの両方の呼び出しをforループで囲むことによってテスト全体を繰り返すと、すべてのロード操作が正しく実行されるか、いずれも実行されないことです。これはローカルのGoogleAppEngineテスト環境のバグですか?