2013-02-21 10 views
5

Mam moduł obsługi żądań, który aktualizuje encję, zapisuje ją w magazynie danych, a następnie musi wykonać dodatkowe prace przed powrotem (np. Kolejkowanie zadań w tle i json-serializacja niektórych wyników). Chcę zrównoleglić ten kod, aby dodatkowa praca była wykonywana podczas zapisywania jednostki.Jak zapobiec wywoływaniu przez ndb wywołania metody put_async() i natychmiastowemu wystawianiu wywołania RPC?

Oto co mój kod obsługi sprowadza się do:, Appstats pokazuje

class FooHandler(webapp2.RequestHandler): 
    @ndb.toplevel 
    def post(self): 
     foo = yield Foo.get_by_id_async(some_id) 

     # Do some work with foo 

     # Don't yield, as I want to perform the code that follows 
     # while foo is being saved to the datastore. 
     # I'm in a toplevel, so the handler will not exit as long as 
     # this async request is not finished. 
     foo.put_async() 

     taskqueue.add(...) 
     json_result = generate_result() 
     self.response.headers["Content-Type"] = "application/json; charset=UTF-8" 
     self.response.write(json_result) 

jednak, że datastore.Put RPC jest robione seryjnie po taskqueue.Add:

Appstats screenshot

Trochę kopania wokół w ndb.context.py pokazuje, że połączenie put_async() zostanie dodane do AutoBatcher zamiast natychmiastowego wydania RPC.

Zakładam, że kończy się przepłukiwaniem, gdy toplevel czeka na zakończenie wszystkich wywołań asynchronicznych.

Rozumiem, że dozowanie daje rzeczywiste korzyści w niektórych scenariuszach, ale w moim przypadku tutaj naprawdę chcę, aby wysłany RPC został wysłany natychmiast, więc mogę wykonywać inne prace, gdy jednostka jest zapisywana.

Jeśli robię yield foo.put_async(), a następnie uzyskać ten sam wodospad w Appstats, ale z datastore.Put zostało zrobione przed resztą:

2nd Appstats screenshot

to należy się spodziewać, jak yield sprawia, że ​​moja obsługi czekać na put_async() wywołanie zakończone przed wykonaniem reszty kodu.

ja też próbowałem dodając wywołanie ndb.get_context().flush() zaraz po foo.put_async(), ale datastore.Put i taskqueue.BulkAdd połączenia są nadal nie są wykonane równolegle według Appstats.

Moje pytanie brzmi: jak mogę wymusić połączenie z put_async(), aby ominąć automatyczny dozownik i natychmiast wysłać RPC?

+0

Czy jest produkowany czy lokalny? – Lipis

+0

Jest w trakcie produkcji. –

Odpowiedz

6

Nie ma obsługiwany sposób to zrobić. Może powinno być. Czy możesz spróbować, jeśli to działa?

loop - ndb.eventloop.get_event_loop() 
while loop.run_idle(): 
    pass 

Być może trzeba spojrzeć na kodzie źródłowym NDB/eventloop.py aby zobaczyć, co jeszcze można spróbować - w zasadzie chcesz spróbować większość co run0() robi oprócz czekania na RPC. W szczególności, jest możliwe, że trzeba by to zrobić:

while loop.current: 
    loop.run0() 
while loop.run_idle(): 
    pass 

(to jeszcze nie jest obsługiwana, ponieważ istnieją inne warunki może trzeba obsłużyć też, ale te nie wydają się występować w swojej przykład.)

+0

Mam go do pracy, wywołując 'ndb.get_context(). Flush()', a następnie 2 pętle zasugerował zaraz po moim wywołaniu 'foo.put_async()'. Uważam, że powinien istnieć oficjalnie wspierany sposób, aby to zrobić, ponieważ nie sądzę, że mój scenariusz użycia jest rzadki (zapisać obiekt, a następnie podsumować pozostałą pracę obsługi podczas zapisywania jednostki). Złożyłem wniosek dotyczący funkcji: http://code.google.com/p/googleappengine/issues/detail?id=8863 –

+0

Uważam, że tak naprawdę potrzebna jest taskqueue.add_async, aby uzyskać kolejkę rpc zadania w stanie bezczynności/pętle rpc w Eventloop. http://code.google.com/p/appengine-ndb-experiment/issues/detail?id=180 – tesdal

+0

Jaki dokładnie był kod, który działał dla Ciebie? flush() jest plikiem, więc musisz go wydać, prawdopodobnie powodując więcej opóźnień niż chcesz. W każdym razie, zgodziłem się, że będzie to przydatna funkcja. Być może zwróci to na to większą uwagę, jeśli umieścisz go w trackerze NDB? –

-2

Spróbuj tego, nie jestem 100% pewna, że ​​to pomoże:

foo = yield Foo.get_by_id_async(some_id) 
future = foo.put_async() 
future.done() 

NDB wnioski się umieścić w autobatcher, partia zostanie wysłana do RPC, kiedy trzeba rezultatu. Ponieważ nie potrzebujesz wyniku foo.put_async(), nie jest on wysyłany, dopóki nie wykonasz innego połączenia ndb (nie masz) lub dopóki nie zakończy się @ ndb.toplevel.

Wywołanie metody future.done() nie blokuje, ale domyślam się, że może wywołać żądanie.

Inną rzeczą, aby spróbować zmusić operacja jest:

ndb.get_context().flush() 
+0

Dzięki, ale nic nie robi. 'Future.done()' robi tylko 'return self._done' bez żadnego przetwarzania, a ja już wypróbowałem' Context.flush() '. –

Powiązane problemy