我正在运行 Django 1.4.5、Celery 3.0.15、Django Celery 3.0.11、Johnny Cache 1.4。
celery 任务中对 ORM 的调用有时会失败,并出现奇怪的错误,例如invalid literal for int() with base 10: 'a'"
or <MaybeEncodingError: Error sending result: ''<ExceptionInfo: ObjectDoesNotExist()>''. Reason: ''PicklingError("Can\'t pickle <class \'scsite.models.DoesNotExist\'>: attribute lookup scsite.models.DoesNotExist failed",)''.>
:
这是一个示例堆栈跟踪:
Task scsite.tasks.send_signup_email[aecf0561-65af-4d11-a3a0-63d88b7e1a70] raised exception: ValueError("invalid literal for int() with base 10: 'd'",)
Task scsite.tasks.send_signup_email[aecf0561-65af-4d11-a3a0-63d88b7e1a70] raised exception: ValueError("invalid literal for int() with base 10: 'd'",)
Stacktrace (most recent call last):
File "celery/execute/trace.py", line 192, in trace_task
R = I.handle_error_state(task, eager=eager)
File "scsite/tasks.py", line 537, in send_signup_email
email_html = email_template.render(Context(data))
File "django/template/base.py", line 142, in render
context.render_context.pop()
File "django/template/base.py", line 134, in _render
return self.nodelist.render(context)
File "django/template/base.py", line 823, in render
bit = self.render_node(node, context)
File "django/template/base.py", line 837, in render_node
return node.render(context)
File "django/template/loader_tags.py", line 155, in render
return self.render_template(self.template, context)
File "django/template/loader_tags.py", line 137, in render_template
output = template.render(context)
File "django/template/base.py", line 142, in render
context.render_context.pop()
File "django/template/base.py", line 134, in _render
return self.nodelist.render(context)
File "django/template/base.py", line 823, in render
bit = self.render_node(node, context)
File "django/template/base.py", line 837, in render_node
return node.render(context)
File "django/template/defaulttags.py", line 192, in render
nodelist.append(node.render(context))
File "django/template/defaulttags.py", line 474, in render
self.extra_context.iteritems()])
File "django/template/base.py", line 584, in resolve
obj = settings.TEMPLATE_STRING_IF_INVALID
File "django/template/base.py", line 721, in resolve
value = self._resolve_lookup(context)
File "django/template/base.py", line 781, in _resolve_lookup
raise
File "django/db/models/manager.py", line 119, in count
return self.get_query_set().count()
File "django/db/models/fields/related.py", line 461, in get_query_set
return super(RelatedManager, self).get_query_set().using(db).filter(**self.core_filters)
File "django/db/models/query.py", line 624, in filter
return self._filter_or_exclude(False, *args, **kwargs)
File "django/db/models/query.py", line 642, in _filter_or_exclude
clone.query.add_q(Q(*args, **kwargs))
File "django/db/models/sql/query.py", line 1250, in add_q
can_reuse=used_aliases, force_having=force_having)
File "django/db/models/sql/query.py", line 1185, in add_filter
connector)
File "django/db/models/sql/where.py", line 69, in add
value = obj.prepare(lookup_type, value)
File "django/db/models/sql/where.py", line 320, in prepare
return self.field.get_prep_lookup(lookup_type, value)
File "django/db/models/fields/__init__.py", line 310, in get_prep_lookup
return self.get_prep_value(value)
File "django/db/models/fields/__init__.py", line 537, in get_prep_value
return int(value)
Task scsite.tasks.update_various_denorm_fields[edddb260-041d-4195-83d7-f2e731e5d1a5] raised exception: ValueError("invalid literal for int() with base 10: 'a'",)
Stacktrace (most recent call last):
File "celery/task/trace.py", line 242, in trace_task
R = I.handle_error_state(task, eager=eager)
File "celery/task/trace.py", line 415, in __protected_call__
return self.run(*args, **kwargs)
File "scsite/tasks.py", line 470, in update_various_denorm_fields
Person.objects.update_question_counts()
File "scsite/managers.py", line 715, in update_question_counts
person.update_question_count_denorm()
File "scsite/models.py", line 2712, in update_question_count_denorm
self.question_count_denorm = self.questions.count()
File "django/db/models/manager.py", line 119, in count
return self.get_query_set().count()
File "django/db/models/fields/related.py", line 567, in get_query_set
return super(ManyRelatedManager, self).get_query_set().using(db)._next_is_sticky().filter(**self.core_filters)
File "django/db/models/query.py", line 624, in filter
return self._filter_or_exclude(False, *args, **kwargs)
File "django/db/models/query.py", line 642, in _filter_or_exclude
clone.query.add_q(Q(*args, **kwargs))
File "django/db/models/sql/query.py", line 1250, in add_q
can_reuse=used_aliases, force_having=force_having)
File "django/db/models/sql/query.py", line 1185, in add_filter
connector)
File "django/db/models/sql/where.py", line 69, in add
value = obj.prepare(lookup_type, value)
File "django/db/models/sql/where.py", line 320, in prepare
return self.field.get_prep_lookup(lookup_type, value)
File "django/db/models/fields/__init__.py", line 310, in get_prep_lookup
return self.get_prep_value(value)
File "django/db/models/fields/__init__.py", line 537, in get_prep_value
return int(value)
我相信错误不在我的代码中,看起来缓存由于某种原因损坏了,并且发生了以下情况:
- 检索 pk=123 的 CustomModel。
- 它被传递给 Celery 进行处理。
- 出于某种原因,Custom 模型的 pk 变成了 'a'、'd' 之类的字符,而不是 123。另一个调用是 with
MyModel.objects.get(pk='a')
并且显然失败了。
实际上,重新访问此代码,似乎我并没有像上面提到的那样将对象传递给 Celery,只是从一个简单的 celery 任务中运行以下管理器方法。
@task
def mytask():
Person.objects.update_question_count()
class PersonManager(models.Manager):
def update_question_counts(self):
persons = self.all() # 1
for person in persons:
person.update_question_count_denorm() #3
我的猜测如下:当第 1 行运行时,人员是从缓存中获取的,但在处理第 3 行时,第 1 行中获得的缓存已损坏。