API Async Datastore (Python)

Observação: é altamente recomendável a desenvolvedores que criam novos aplicativos usar a biblioteca de cliente NDB, porque ela oferece diversos benefícios em comparação com esta biblioteca de cliente, como armazenamento em cache automático de entidades por meio da API Memcache. Se você estiver usando a antiga biblioteca de cliente DB, leia o Guia de migração de DB para NDB.

A API Async Datastore permite realizar chamadas paralelas e sem bloqueio ao armazenamento de dados e recuperar os resultados dessas chamadas em um ponto posterior no gerenciamento da solicitação. Esta documentação descreve os seguintes aspectos da API Async Datastore:

Como trabalhar com o serviço de armazenamento de dados assíncrono

Com a API Async Datastore, você faz chamadas ao armazenamento de dados usando métodos do formato *_async (como get_async() no pacote google.appengine.ext.db). O exemplo de código a seguir demonstra algumas operações simples do armazenamento de dados usando os métodos assíncronos:

from google.appengine.ext import db

get_future = db.get_async(key)
put_future = db.put_async(model)
delete_future = db.delete_async(key)
allocate_ids_future = db.allocate_ids_async(key, 10)

Essas funções realizam as mesmas operações que as versões síncronas, mas retornam imediatamente um objeto assíncrono que pode ser usado para conseguir o resultado real futuramente. O código de amostra abaixo demonstra como receber o resultado assíncrono usando get_result ():

entity = get_future.get_result()
key = put_future.get_result()
range = allocate_ids_future.get_result()

# Wait for the operation to complete without returning a value.
# Exceptions that occurred in the call are thrown here. Calling
# get_result() allows you to verify that the deletion succeeded.
delete_future.get_result()

Observação: as exceções não são geradas até que você chame get_result(). Chamar esse método permite verificar se a operação assíncrona foi concluída com êxito.

Como trabalhar com transações assíncronas

Chamadas da API assíncrona do armazenamento de dados podem participar de transações da mesma forma que chamadas síncronas. Esta é uma função que ajusta o salário de um Employee e grava uma outra entidade SalaryAdjustment no mesmo grupo de entidades que o Employee, tudo dentro de uma única transação.

def adjust_salary(employee_key, raise_ammount):
   def runner():
        # Async call to lookup the Employee entity
        employee_entity_future = db.get_async(employee_key)

        # Create and put a SalaryAdjustment entity in parallel with the lookup
        adjustment_entity = SalaryAdjustment(parent=employeeKey)
        adjustment_entity.adjustment = raise_amount
        db.put_async(adjustmentEntity)

        # Fetch the result of our lookup to make the salary adjustment
        employee_entity = employee_entity_future.get_result()
        employee_entity.salary += raise_amount

        # Re-put the Employee entity with the adjusted salary.
        db.put_async(employee_entity)
    db.run_in_transaction(runner)

O exemplo ilustra uma diferença importante entre chamadas assíncronas sem transações e chamadas assíncronas dentro de transações. Quando você não usa uma transação, a única maneira de garantir que uma chamada assíncrona foi concluída é buscar o resultado do objeto assíncrono usando uma transação. Confirmar essa transação bloqueia o resultado de todas as chamadas assíncronas feitas dentro de uma transação.

Então, no exemplo acima, mesmo que nossa chamada assíncrona para inserir a entidade SalaryAdjustment ainda possa estar pendente quando runner() acabar, a confirmação ocorre somente depois que a inserção for concluída.

Consultas assíncronas

Atualmente não oferecemos uma API explicitamente assíncrona para consultas. No entanto, ao chamar a Query.run(), a consulta retorna imediatamente e faz uma busca prévia de resultados de forma assíncrona. Isso permite que seu aplicativo realize trabalho paralelamente enquanto resultados de consulta são buscados.

# ...

q1 = Salesperson.all().filter('date_of_hire <', one_month_ago)

# Returns instantly, query is executing in the background.
recent_hires = q1.run()

q2 = Customer.all().filter('last_contact >', one_year_ago)

# Also returns instantly, query is executing in the background.
needs_followup = q2.run()

schedule_phone_call(recent_hires, needs_followUp)

Infelizmente, Query.fetch () não tem o mesmo comportamento.

Quando usar chamadas assíncronas do armazenamento de dados

Quando você chama um método google.ext.db síncrono, como db.get(), seu código bloqueia até que a chamada para o armazenamento de dados seja concluída. Se a única coisa que seu aplicativo precisa fazer é exibir o resultado do get() em HTML, o bloqueio até que a chamada seja concluída é uma medida perfeitamente razoável a se tomar. No entanto, caso seu aplicativo precise do resultado get() além do resultado de uma consulta para retornar a resposta, e se get() e a consulta não tiverem dependências de dados, será perda de tempo esperar até que o get() seja concluído para iniciar a consulta. Veja um exemplo de código que pode ser melhorado com o uso da Async API:

# Read employee data from the Datastore
employee = Employee.get_by_key_name('Max')  # Blocking for no good reason!

# Fetch payment history
query = PaymentHistory.all().ancestor(employee.key())
history = query.fetch(10)
render_html(employee, history)

Em vez de esperar que o get() seja concluído, use db.get_async() para executar a chamada de maneira assíncrona:

employee_key = db.Key.from_path(Employee.kind(), 'Max')

# Read employee data from the Datastore
employee_future = db.get_async(employee_key)  # Returns immediately!

# Fetch payment history
query = PaymentHistory.all().ancestor(employee_key)

# Implicitly performs query asynchronously
history_itr = query.run(config=datastore_query.QueryOptions(limit=10))
employee = employee_future.get_result()
render_html(employee, history_itr)

As versões síncrona e assíncrona desse código usam quantidades de CPU semelhantes (afinal, ambas realizam a mesma quantidade de trabalho). Porém, uma vez que a versão assíncrona permite que as duas operações do armazenamento de dados sejam executadas paralelamente, ela tem latência mais baixa. Em geral, quando é necessário executar diversas operações de armazenamento de dados sem quaisquer dependências de dados, a API assíncrona melhora a latência de forma significativa.