com.google.appengine.api.datastore.DatastoreTimeoutException: datastore timeout: operation took too long.
Mój kod, który powodował problem wyglądał mniej więcej tak:
PersistenceManager pm = PMF.get().getPersistenceManager();
String query = "select from TABLE order by date";
List list = (List) pm.newQuery(query).execute();
for (Record record:list) {
// do smth with record
}
Wyjątek wylatywał z "podświetlonej" linii.
Kod w tej linii wykonywał się do 15 sekund po czym był brutalnie przerywany przez serwer... związane to było z tym, że w bazie znajdowało się wtedy grubo ponad 20 tysięcy rekordów... a Google App Engine jest na tyle miłe, że dane z bazy pobierane są dopiero wtedy gdy są naprawdę konieczne :-) [z jednym "ale"]
Linia:
for (Record record:list) {
jest równoważna takiemu kodowi:
for(Iterator iterator = list.iterator(); iterator.hasNext();) {
Record record = (Record)iterator.next();
I wygląda na to, że w trakcie tworzenia iteratora Google App Engine pobiera dane z bazy.... a zebranie 1000 rekordów zajmuje zbyt wiele czasu i stąd wyjątek.
Jak z nim walczyć?
Prosto :-)
Po pierwsze zamiast pętli for-each użyć należy zwykłej pętli, ale jako warunku stopu nie powinniśmy używać warunku
index<list.size()
, bo rozmiar danych poznamy dopiero po pobraniu wszystkich danych z bazy.Ja użyłem wartości 1000 jako maksymalnej ilości iteracji [ponieważ z każdego query możemy dostać maksymalnie do 1000 rekordów], zabezpieczyłem się też w bardzo brzydki sposób przed
IndexOutOfBoundsException
po prostu go łapiąc ;-)Po drugie trzeba w pętli pobierającej dane z bazy wstawić watchdoga, który w razie potrzeby wyjdzie z niej.
long start = System.currentTimeMillis();
PersistenceManager pm = PMF.get().getPersistenceManager();
String query = "select from TABLE order by date";
List list = (List) pm.newQuery(query).execute();
for (int idx=0; idx<1000; idx++) {
Record record = null;
try {
record = list.get(idx);
} catch (IndexOutOfBoundsException ioobe) {
// ugly
break;
}
// do smth with record
if ((System.currentTimeMillis()-start)>=2000) break;
}
I to działa :-)
Dodatkowo dodałem jeszcze ograniczenie do operowania na maksymalnie 100 rekordach ponieważ później je jeszcze kasuje i tworze nowe, ale to już w transakcjach :-)
Podobne postybeta
Jak z metody size() w List w Java'ie dostać ujemną liczbę? ;-)
Wymiana obiektów między PC a Androidem... - użyj serializacji Luke ;-)
Google App Engine - pierwsze wrażenia
Zaskoczenie
Amerykański zegarek jest trudny ;-)
Brak komentarzy:
Prześlij komentarz