2012-06-16 6 views
15

Escribo un código para un blog/sitio de noticias. La página principal tiene 10 artículos más recientes y también hay una sección de archivo con todos los artículos ordenados por tiempo de modificación descendiente. En la sección de archivo, uso la paginación basada en los cursores y almaceno los resultados comenzando desde la segunda página, ya que las páginas se cambian solo cuando se publica un nuevo artículo o cuando pasa a borradores por algún motivo. Cada página tiene 10 artículos. Entonces, cuando un usuario golpea una página de archivo con algún número (no el primero), primero se verifica la memoria de Memcache para ese número de página. Si la página no está ahí, Memcache está marcada por el cursor para esa página y luego los resultados se obtienen de almacén de datos utilizando ese cursor:Retraso de lectura en el almacén de datos de App Engine después de poner()

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) 

Cada vez que se crea un nuevo artículo o el estado de un artículo existente se cambia (borrador/publicado) Actualizo el caché para los resultados y los cursores de las páginas de archivo. Lo hago después de guardar un artículo en el almacén de datos:

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 

Y aquí viene el problema. A veces (no hay ley, sucede al azar) después de actualizar la caché obtengo los mismos resultados y cursores para las páginas de archivo que antes de la actualización. Por ejemplo, agrego un nuevo artículo. Se guarda en el almacén de datos y aparece en la página principal y en la primera página del archivo (la primera página del archivo no está en la memoria caché). Pero otras páginas de archivo no están actualizadas. Probé mi función cacheArchivePages() y funciona como se esperaba. ¿Podría ser así que ha transcurrido muy poco tiempo después de que puse() una actualización en el almacén de datos y antes de que obtengaArticlesPage() en la función cacheArchivePages()? Tal vez la transacción de escritura aún no terminó y entonces obtengo viejos resultados. Intenté usar time.sleep() y esperar unos segundos antes de llamar a cacheArchivePages() y en ese caso no pude reproducir ese comportamiento, pero me parece que time.sleep() no es una buena idea. De todos modos, necesito saber la causa exacta de ese comportamiento y cómo enfrentarlo.

Respuesta

22

Lo más probable es que se vea afectado por "eventuales consultas consistentes". Cuando se utiliza el almacén de datos de RR. HH., Las consultas pueden utilizar datos ligeramente antiguos, y lleva un tiempo que los datos escritos por put() sean visibles para las consultas (no hay tal retraso para get() por clave o ID). El retraso generalmente se mide en segundos, pero no creo que garanticemos un límite superior: si te golpea una desafortunada partición de red, podría ser horas, me imagino.

Existen todo tipo de soluciones parciales, desde hacer trampa cuando el autor de las escrituras más recientes está viendo los resultados de la consulta hasta el uso de consultas ancestrales (que tienen sus propias limitaciones). Puede simplemente darle a su caché un tiempo de vida limitado y actualizarlo en lectura en lugar de escribir.

¡Buena suerte!

+0

Gracias Guido! Debería prestar más atención a la coherencia de los datos y tener en cuenta las características del almacén de datos de recursos humanos. En este caso particular, es fácil para mí comparar los resultados anteriores y los nuevos, y si son los mismos para reiniciar la consulta. – wombatonfire

+0

He votado a favor porque "usar consultas de antecesores" es exactamente lo que resolvió el problema de demora en mi aplicación, gracias Guido. – Deleplace

Cuestiones relacionadas