如何确保使用GAE和延迟任务队列仅向所有用户发送一条每日消息

时间:2016-10-06 03:06:11

标签: google-app-engine google-cloud-platform google-cloud-datastore

我正在使用带有GAE的延迟任务队列库。每天我都需要向连接到我的应用中某个页面的所有用户发送一段文字。我的应用程序连接了多个页面,因此对于每个页面,我想查看所有用户,并向他们发送每日消息。我正在使用游标以800的批量迭代用户表。如果有超过800个用户,我想记住光标停止的位置,并与其他用户开始另一个任务。

我只想确保使用我的算法,我将向所有用户发送一条消息。我想确保我不会错过任何用户,并且没有用户会收到两次相同的消息。

这看起来像是处理我情况的正确算法吗?

def send_news(page_cursor=None, page_batch_size=1, 
              user_cursor=None, user_batch_size=800):

  p_query = PageProfile.query(PageProfile.subscribed==True)
  all_pages, next_page_cursor, page_more = p_query.fetch_page(page_batch_size, 
                                           start_cursor=page_cursor)
  for page in all_pages:
    if page.page_news_url and page.subscribed:
      query = User.query(User.subscribed==True, User.page_id == page.page_id)
      all_users, next_user_cursor, user_more = query.fetch_page(user_batch_size, start_cursor=user_cursor)

      for user in all_users:
        user.sendNews()

      # If there are more users on this page, remember the cursor
      # and get the next 800 users on this same page
      if user_more:
        deferred.defer(send_news, page_cursor=page_cursor, user_cursor=next_user_cursor)

  # If there are more pages left, use another deferred queue to
  # send the daily news to users in that page
  if page_more:
    deferred.defer(send_news, page_cursor=next_page_cursor)

  return "OK"

1 个答案:

答案 0 :(得分:1)

您可以将user.sendNews()包装到具有特定名称的另一个延期任务中,这将确保仅创建一次。

interval = int(time.time()) / (60 * 60 * 24)

args = ('positional_arguments_for_object')
kwargs = {'param': 'value'}

task_name = '_'.join([
  'user_name',
  'page_name'
  str(interval_num)
])
# with interval presented in the name we are sure that the task name for the same page and same user will stay same for 24 hours

try:
  deferred.defer(obj, _name=task_name, _queue='my-queue', _url='/_ah/queue/deferred', *args, **kwargs)
except (taskqueue.TaskAlreadyExistsError):
  pass
  # task with such name already exists, likely wasn't executed yet
except (taskqueue.TombstonedTaskError)
  pass
  # task with such name was created not long time ago and this name isn't available to use
  # this should reset once a week or so

请注意,据我所知,App Engine不保证任务只执行一次,在某些边缘情况下,它可以执行两次或更多次,理想情况下它们应该是幂等的。如果这样的边缘情况对您很重要 - 您可以在数据存储区中为每个任务以事务方式读/写一些标志,并在执行任务之前检查该实体是否在那里取消执行。