Async Datastore API(Python)

注: 新しいアプリケーションを作成する際は、NDB クライアント ライブラリを使用することを強くおすすめします。NDB クライアント ライブラリには、Memcache API によるエンティティの自動キャッシュをはじめ、このクライアント ライブラリにはないメリットがあります。古い DB クライアント ライブラリを現在使用している場合は、DB から NDB への移行ガイドをお読みください。

Async Datastore API を使用すると、データストアに対して並列の非ブロック化呼び出しを行って、後で要求の処理中にこれらの呼び出しの結果を取得できます。このドキュメントでは、Async Datastore API の次の点について説明します。

非同期データストア サービスの操作

Async Datastore API を使用する場合、*_async という形式のメソッド(google.appengine.ext.db パッケージの get_async() など)を使用してデータストア呼び出しを行います。次のコードサンプルでは、非同期のメソッドを使用した単純なデータストア オペレーションを示しています。

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)

これらの関数では、同期バージョンと同じオペレーションを行います。異なるのは、非同期オブジェクトをすぐに返し、これを使用して実際の結果を後から取得できることです。次のコードサンプルでは、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()

注: get_result() メソッドを呼び出すまで例外はスローされません。このメソッドを呼び出すと、非同期操作が成功したことを確認できます。

非同期トランザクションの操作

同期呼び出しと同様、Async Datastore API 呼び出しをトランザクションに含めることができます。次の関数では、Employee の給与を調整し、追加する SalaryAdjustment エンティティを Employee と同じエンティティ グループに書き込みます。これらをすべて、1 つのトランザクションで行います。

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)

この例は、トランザクションを使用しない非同期呼び出しと、トランザクション内での非同期呼び出しの重要な違いを示しています。トランザクションを使用しない場合、個々の非同期呼び出しが完了したことを確認するには、トランザクションを使用して非同期オブジェクトの結果を取得するしかありません。そのトランザクションは、トランザクション内で行われたすべての非同期呼び出しの結果でコミットがブロックされます。

したがって、上記の例では、SalaryAdjustment エンティティを挿入する非同期呼び出しは、runner() の完了時点でまだ未処理の状態になっている可能性がありますが、その挿入が完了するまでは commit は発生しません。

非同期クエリ

現在のところ、Google ではクエリの非同期 API を明示的には公開していません。ただし、Query.run() を呼び出すと、クエリはすぐに戻り、非同期的に結果をプリフェッチします。このため、アプリケーションは、クエリ結果を取得しながら、並行して処理を行うことができます。

# ...

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)

しかしながら、Query.fetch() は同じようには動作しません。

非同期データストア呼び出しを使用するタイミング

db.get() などの同期的な google.ext.db メソッドを呼び出すと、データストアの呼び出しが完了するまでコードはブロックされます。アプリケーションで、get() の結果を HTML で表示するだけでよい場合、呼び出しが完了するまでブロックするという処理は合理的です。しかし、レスポンスの表示に get() の結果といずれかの Query の結果が必要で、get() とその Query の間にデータの依存関係がない場合は、get() が完了するまで Query を開始しないのは時間の無駄になります。非同期 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)

get() の完了を待機するのではなく、db.get_async() を使用して非同期的に呼び出しを実行します。

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)

このコードの同期バージョンと非同期バージョンの CPU の使用量はそれほど変わりませんが(結局、作業量は両方とも同じです)、非同期バージョンでは 2 つのデータストア オペレーションを並列実行できるので、レイテンシが短くなります。一般に、複数のデータストア操作を実行する必要があり、データに依存関係がない場合は、非同期 API を使用するとレイテンシを大幅に改善できます。