我目前有一个在appengine上运行的应用程序,我正在使用延迟库执行一些作业,其中一些任务每天运行,而其中一些每月执行一次。这些任务中的大多数查询数据存储区以检索文档,然后将实体存储在索引(Search API)中。其中一些表每月更换一次,我必须在所有实体(4~5M)上运行这些任务。
这样一项任务的一个例子是:
def addCompaniesToIndex(cursor=None, n_entities=0, mindate=None):
#get index
BATCH_SIZE = 200
cps, next_cursor, more = Company.query().\
fetch_page(BATCH_SIZE,
start_cursor=cursor)
doc_list = []
for i in range(0, len(cps)):
cp = cps[i]
#create a Index Document using the Datastore entity
#this document has only about 5 text fields and one date field
cp_doc = getCompanyDocument(cp)
doc_list.append(cp_doc)
index = search.Index(name='Company')
index.put(doc_list)
n_entities += len(doc_list)
if more:
logging.debug('Company: %d added to index', n_entities)
#to_put[:] = []
doc_list[:] = []
deferred.defer(addCompaniesToIndex,
cursor=next_cursor,
n_entities=n_entities,
mindate=mindate)
else:
logging.debug('Finished Company index creation (%d processed)', n_entities)
当我仅运行一个任务时,每个延期任务的执行大约需要4-5个,因此索引我的5M实体大约需要35个小时。
另一件事是,当我在同一队列上使用不同的延迟任务对另一个索引(例如,每日更新之一)运行更新时,两者的执行速度要慢得多。并且每次延期通话开始大约需要10-15秒,这是无法忍受的。
我的问题是:有没有办法更快地完成此操作并将推送队列扩展到每次运行多个作业?或者我应该使用不同的方法来解决这个问题吗?
提前致谢,
答案 0 :(得分:2)
通过将if more
语句放在addCompaniesToIndex()
函数的末尾,您实际上序列化了任务执行:在当前的延迟任务完成索引其共享之前,不会创建下一个延迟任务的文档。
您可以做的是在if more
调用之后立即移动Company.query().fetch_page()
语句,获取(大部分)下一个延迟任务执行所需的变量。
这样,下一个延迟任务将在当前完成之前创建并排队(长),因此它们的处理可能会重叠/交错。您还需要进行一些其他修改,例如处理n_entities
变量,该变量在更新的场景中失去其当前含义 - 但这或多或少具有美观/信息性,对实际的文档索引操作不是必不可少的
如果延期任务的数量非常高,则存在同时排队过多的风险,这可能会导致“爆炸”。在GAE将产生的实例数量中处理它们。在这种情况下是不可取的,你可以"油门"通过延迟执行一点来产生延期任务的速率,请参阅https://stackoverflow.com/a/38958475/4495081。
答案 1 :(得分:1)
我想我终于通过使用前一个答案提出的两个队列和想法来解决这个问题。
我尝试过只使用一个队列,但处理吞吐量不是很好。我相信这可能来自于我们在同一队列和调度程序might not work as well in this case上运行的慢速和快速任务。