2012-06-16 12 views
15

Scrivo un codice per un blog/sito di notizie. La pagina principale contiene 10 articoli più recenti e inoltre c'è una sezione di archivio con tutti gli articoli ordinati per tempo di modifica decrescente. Nella sezione archivio utilizzo l'impaginazione basata sui cursori e memorizzo i risultati della cache a partire dalla seconda pagina in quanto le pagine vengono modificate solo quando viene pubblicato un nuovo articolo o se per alcune ragioni le bozze vengono applicate alle bozze. Ogni pagina ha 10 articoli. Pertanto, quando un utente raggiunge una pagina di archivio con un numero (non il primo) memcache viene controllato per ottenere prima i risultati del numero di pagina. Se la pagina non è lì, memcache viene controllato per il cursore per quella pagina e poi i risultati vengono recuperati dal datastore usando quel cursore:Lettura ritardo nel datastore dell'App Engine dopo put()

class archivePage: 
    def GET(self, page): 
     if not page: 
      articles = memcache.get('archivePage') 
      if not articles: 
       articles = fetchArticles() 
       memcache.set('archivePage', articles) 
     else: 
      if int(page) == 0 or int(page) == 1: 
       raise web.seeother('/archive') 
      articles = memcache.get('archivePage'+page) 
      if not articles: 
       pageCursor = memcache.get('ArchivePageMapping'+page) 
       if not pageCursor: 
        pageMapping = ArchivePageMapping.query(ArchivePageMapping.page == int(page)).get() 
        pageCursor = pageMapping.cursor 
        memcache.set('ArchivePageMapping'+page, pageCursor) 
       articles = fetchArticles(cursor=Cursor(urlsafe=pageCursor)) 
       memcache.set('archivePage'+page, articles) 

Ogni volta che viene creato un nuovo articolo o lo stato di un articolo esistente viene modificata (bozza/pubblicato) Rinnovo la cache per i risultati e i cursori delle pagine di archivio. Lo faccio dopo aver salvato un articolo sul datastore:

class addArticlePage:  
    def POST(self): 
     formData = web.input() 
     if formData.title and formData.content: 
      article = Article(title=formData.title, 
           content=formData.content, 
           status=int(formData.status)) 
      key = article.put() 
      if int(formData.status) == 1: 
       cacheArchivePages() 
      raise web.seeother('/article/%s' % key.id()) 

def cacheArchivePages(): 
    articles, cursor, moreArticles = fetchArticlesPage() 
    memcache.set('archivePage', articles) 
    pageNumber=2 
    while moreArticles: 
     pageMapping = ArchivePageMapping.query(ArchivePageMapping.page == pageNumber).get() 
     if pageMapping: 
      pageMapping.cursor = cursor.urlsafe() 
     else: 
      pageMapping = ArchivePageMapping(page=pageNumber, 
              cursor=cursor.urlsafe()) 
     pageMapping.put() 
     memcache.set('ArchivePageMapping'+str(pageNumber), cursor.urlsafe()) 
     articles, cursor, moreArticles = fetchArticlesPage(cursor=cursor) 
     memcache.set('archivePage'+str(pageNumber), articles) 
     pageNumber+=1 

E qui arriva il problema. A volte (non c'è nessuna legge, succede casualmente) dopo aver aggiornato la cache ottengo gli stessi risultati e cursori per le pagine di archivio come prima del refresh. Ad esempio aggiungo un nuovo articolo. Viene salvato nel datastore e viene visualizzato nella prima pagina e nella prima pagina nell'archivio (la prima pagina dell'archivio non viene memorizzata nella cache). Ma altre pagine di archivio non sono aggiornate. Ho testato la mia funzione cacheArchivePages() e funziona come previsto. Potrebbe essere passato così poco tempo dopo aver messo() un aggiornamento al datastore e prima di I fetchArticlesPage() nella funzione cacheArchivePages()? Forse la transazione scritta non è ancora finita e quindi ottengo risultati vecchi? Ho provato a utilizzare time.sleep() e attendere alcuni secondi prima di chiamare cacheArchivePages() e in quel caso non ero in grado di riprodurre questo comportamento, ma mi sembra che time.sleep() non sia una buona idea. Ad ogni modo ho bisogno di conoscere la causa esatta di quel comportamento e come affrontarlo.

risposta

22

Probabilmente sei colpito da "query alla fine coerenti". Quando si utilizza il datastore delle risorse umane, le query possono utilizzare dati leggermente vecchi, e ci vuole un po 'di tempo prima che i dati scritti da put() siano visibili alle query (non esiste un tale ritardo per get() per chiave o id). Il ritardo viene in genere misurato in secondi ma non penso che garantiamo un limite superiore: se sei colpito da una sfortunata partizione di rete potrebbe essere ore, immagino.

Ci sono tutti i tipi di soluzioni parziali, dal barare quando l'autore delle scritture più recenti sta visualizzando i risultati della query per l'utilizzo di query antenate (che hanno una propria quota di limitazioni). Potresti semplicemente dare alla tua cache una durata limitata e aggiornarla su read anziché su write.

Buona fortuna!

+0

Grazie mille Guido! Dovrei prestare maggiore attenzione alla coerenza dei dati e tenere a mente le caratteristiche del datastore delle risorse umane. In questo caso particolare è facile per me confrontare i vecchi risultati e quelli nuovi e se sono gli stessi per riavviare la query. – wombatonfire

+0

Ho upvoted perché "use ancestor queries" è esattamente ciò che ha risolto il problema di ritardo nella mia app, grazie Guido. – Deleplace

Problemi correlati