8

Esta es una excepción común que recibo diariamente en el registro de mi aplicación, generalmente 5/6 veces al día con un tráfico de 1K visitas/día :Tiempo de espera de Google App Engine: se agotó el tiempo de espera de la operación del almacén de datos o los datos no estaban disponibles temporalmente

db error trying to store stats 
Traceback (most recent call last): 
    File "/base/data/home/apps/stackprinter/1b.347728306076327132/app/utility/worker.py", line 36, in deferred_store_print_statistics 
    dbcounter.increment() 
    File "/base/data/home/apps/stackprinter/1b.347728306076327132/app/db/counter.py", line 28, in increment 
    db.run_in_transaction(txn) 
    File "/base/python_runtime/python_lib/versions/1/google/appengine/api/datastore.py", line 1981, in RunInTransaction 
    DEFAULT_TRANSACTION_RETRIES, function, *args, **kwargs) 
    File "/base/python_runtime/python_lib/versions/1/google/appengine/api/datastore.py", line 2067, in RunInTransactionCustomRetries 
    ok, result = _DoOneTry(new_connection, function, args, kwargs) 
    File "/base/python_runtime/python_lib/versions/1/google/appengine/api/datastore.py", line 2105, in _DoOneTry 
    if new_connection.commit(): 
    File "/base/python_runtime/python_lib/versions/1/google/appengine/datastore/datastore_rpc.py", line 1585, in commit 
    return rpc.get_result() 
    File "/base/python_runtime/python_lib/versions/1/google/appengine/api/apiproxy_stub_map.py", line 530, in get_result 
    return self.__get_result_hook(self) 
    File "/base/python_runtime/python_lib/versions/1/google/appengine/datastore/datastore_rpc.py", line 1613, in __commit_hook 
    raise _ToDatastoreError(err) 
Timeout: The datastore operation timed out, or the data was temporarily unavailable. 

la función que está levantando la excepción anterior es la siguiente:

def store_printed_question(question_id, service, title): 
    def _store_TX(): 
     entity = Question.get_by_key_name(key_names = '%s_%s' % \ 
             (question_id, service)) 
     if entity: 
      entity.counter = entity.counter + 1     
      entity.put() 
     else: 
      Question(key_name = '%s_%s' % (question_id, service),\ 
          question_id ,\ 
          service,\ 
          title,\ 
          counter = 1).put() 
    db.run_in_transaction(_store_TX) 

Básicamente, el cheque store_printed_question función de si una cuestión ha sido impreso previamente, incrementando en ese caso el contador relacionado en una sola transacción.
Esta función se agrega por un WebHandler a un trabajador deferred predefinido utilizando la cola de default que, como ustedes sabrán, tiene una tasa de rendimiento de cinco invocaciones de tareas por segundo.

En una entidad con seis atributos (dos índices) pensé que usar transactions regulado por un límite de tasa de tarea diferida me permitiría evitar los tiempos de espera del almacén de datos pero, mirando el registro, este error aún se muestra diariamente.

Este contador que estoy almacenando no es tan importante, así que no estoy preocupado por obtener estos tiempos de espera; de todos modos, tengo curiosidad por saber por qué Google App Engine no puede manejar esta tarea correctamente, incluso a una tasa baja, como 5 tareas por segundo, y si bajar la tasa podría ser una posible solución.
Un sharded counter en cada pregunta para evitar tiempos de espera sería una exageración para mí.

EDIT:
he puesto el límite de velocidad a 1 de tareas por segundo en la cola predeterminada; Todavía estoy recibiendo el mismo error.

+0

Las tareas diferidas no son "más ligeras" que las tareas normales en cualquier sentido, excepto que son más fáciles de escribir. Debajo del capó, están implementados con controladores regulares. En cualquier caso, eso no tendría ningún impacto en la sobrecarga de la transacción en sí. –

Respuesta

6

En general, un tiempo de espera como este suele ser debido a write contention. Si tiene activada una transacción y está escribiendo un montón de cosas al mismo grupo de entidades al mismo tiempo, se encontrará con problemas de contención de escritura (un efecto secundario de optimistic concurrency). En la mayoría de los casos, si hace que su entity group más pequeño, que generalmente minimizará este problema.

En su caso específico, basado en el código anterior, es muy probable que deba utilizar un sharded counter para evitar el apilamiento de escrituras serializadas.

Otra posibilidad mucho menos probable (mencionada aquí solo para completar) es que la tableta en la que se encuentran sus datos es being moved.

+1

No hay un grupo de entidades definido, estoy actualizando un solo modelo. La contención de escritura en el grupo de entidades generalmente genera otro tipo de excepción. Como escribí en mi pregunta, usar un contador afilado parece una exageración en este caso. – systempuntoout

+1

@systempuntoout Aún puede obtener contención de escritura en entidades individuales si está haciendo más de aproximadamente 1QPS de modificaciones en ellas. La contención _ causará una excepción de tiempo de espera. –

+0

@Nick Sí, la contención de escritura en el grupo de entidades provoca un error de "Colisión de transacción para el grupo de entidades con la clave ..". – systempuntoout

7

Una consulta solo puede vivir 30 segundos. Vea mi respuesta al this question para obtener un código de ejemplo para interrumpir una consulta usando los cursores.

+0

El tiempo de espera predeterminado para las llamadas de API es de 5 segundos. Puede cambiar esto configurando el contexto de esta manera: ctx: = appengine.Timeout (appengine.NewContext (req), 30 * time.Second) – nilsmagnus

+0

'appengine.Timeout' now' context.WithTimeout' – murrekatt

Cuestiones relacionadas