Я пытаюсь понять асинхронные операции, введенные с помощью NDB, я хотел бы использовать @ndb.tasklet
для async некоторых из моих работ.Как переопределить метод асинхронного NDB и написать свой собственный тасклет
Простой пример будет string_id поколения в перегруженной get_or_insert_async
Является ли это правильный путь к вещам? Что можно улучшить здесь?
@classmethod
@ndb.tasklet
def get_or_insert_async(cls, *args):
id = cls.make_string_id(*args)
model = yield super(MyModel, cls).get_or_insert_async(id)
raise ndb.Return(model)
Другой пример - это делать вещи в петле в виде вентилятора. Это верно?
@classmethod
@ndb.tasklet
def do_stuff(cls, some_collection):
@ndb.tasklet
def internal_tasklet(data):
do_some_long_taking_stuff(data)
id = make_stuff_needed_for_id(data)
model = yield cls.get_or_insert_async(id)
model.long_processing(data)
yield model.put_async()
raise ndb.Return(None)
for data in some_collection:
# will it parallelise internal_tasklet execution?
yield internal_tasklet(data)
raise ndb.Return(None)
РЕДАКТИРОВАТЬ:
Как понятно всю концепцию, yields
здесь, чтобы обеспечить Future
объекты, которые затем собирают в параллель (где это возможно) и выполняться асинхронно. Я прав?
После намека Ника (Это то, что вы имели в виду?):
@classmethod
@ndb.tasklet
def do_stuff(cls, some_collection):
@ndb.tasklet
def internal_tasklet(data):
do_some_long_taking_stuff(data)
id = make_stuff_needed_for_id(data)
model = yield cls.get_or_insert_async(id)
model.long_processing(data)
raise ndb.Return(model) # change here
models = []
for data in some_collection:
# will it parallelise internal_tasklet execution?
m = yield internal_tasklet(data) # change here
models.appedn(m) # change here
keys = yield ndb.put_multi_async(models) # change here
raise ndb.Return(keys) # change here
EDIT:
Новый пересмотренный вариант ...
@classmethod
@ndb.tasklet
def do_stuff(cls, some_collection):
@ndb.tasklet
def internal_tasklet(data):
do_some_long_taking_stuff(data)
id = make_stuff_needed_for_id(data)
model = yield cls.get_or_insert_async(id)
model.long_processing(data)
raise ndb.Return(model)
futures = []
for data in some_collection:
# tasklets won't run in parallel but while
# one is waiting on a yield (and RPC underneath)
# the other will advance it's execution
# up to a next yield or return
fut = internal_tasklet(data)) # change here
futures.append(fut) # change here
Future.wait_all(futures) # change here
models = [fut.get_result() for fut in futures]
keys = yield ndb.put_multi_async(models) # change here
raise ndb.Return(keys) # change here
Не может ли он сделать вызов 'make_string_id' в' get_or_insert_async' синхронным и только базовый вызов исходного 'get_or_insert_async' действительно асинхронным? –
Можете ли вы переписать второй пример в своем ответе? Я не уверен, что ведьма уйдет, поэтому цикл не будет ждать каждого элемента и всех (или большинства) 'inner_tasklet' исполнений, где они будут распараллеливаться. –
@WooYek Да, но make_string_id в любом случае синхронно в вашем фрагменте. И если они не делают RPC, нет смысла делать их асинхронно - таллеты помогают, когда время тратится на RPC. Что касается второго примера - проблема заключается в анти-шаблоне вызова в цикле. Выполнение этого ждет завершения каждой отдельной задачи, прежде чем перейти к следующему. Каждый раз, когда у вас есть цикл с доходностью в нем, вы должны вызывать функцию без урока, собирать список результатов и вызывать доход в этом списке. –