Django request.user.is_authenticated провоцирует элемент последовательности обновления словаря #0 длиной X; 2 требуется

Каждый раз, когда мой код достигает определенной строки, он, кажется, вызывает ошибку dictionary update sequence element #0 has length X; 2 is required. Значение X может измениться, но при вызове этого почти всегда возникает ошибка:

if request.user.is_authenticated

Это python 3.6.7, django 2.1.7

Вот стек ошибок

    Traceback:

File "/path/to/venv/lib/python3.6/site-packages/django/core/handlers/exception.py" in inner
  34.             response = get_response(request)

File "/path/to/venv/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  126.                 response = self.process_exception_by_middleware(e, request)

File "/path/to/venv/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  124.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "./playerdata/api/tokens.py" in token_queries
  87.     if request.user.is_authenticated:

File "/path/to/venv/lib/python3.6/site-packages/django/utils/functional.py" in inner
  213.             self._setup()

File "/path/to/venv/lib/python3.6/site-packages/django/utils/functional.py" in _setup
  347.         self._wrapped = self._setupfunc()

File "/path/to/venv/lib/python3.6/site-packages/django/contrib/auth/middleware.py" in <lambda>
  24.         request.user = SimpleLazyObject(lambda: get_user(request))

File "/path/to/venv/lib/python3.6/site-packages/django/contrib/auth/middleware.py" in get_user
  12.         request._cached_user = auth.get_user(request)

File "/path/to/venv/lib/python3.6/site-packages/django/contrib/auth/__init__.py" in get_user
  189.             user = backend.get_user(user_id)

File "/path/to/venv/lib/python3.6/site-packages/django/contrib/auth/backends.py" in get_user
  98.             user = UserModel._default_manager.get(pk=user_id)

File "/path/to/venv/lib/python3.6/site-packages/django/db/models/manager.py" in manager_method
  82.                 return getattr(self.get_queryset(), name)(*args, **kwargs)

File "/path/to/venv/lib/python3.6/site-packages/django/db/models/query.py" in get
  390.         clone = self.filter(*args, **kwargs)

File "/path/to/venv/lib/python3.6/site-packages/django/db/models/query.py" in filter
  844.         return self._filter_or_exclude(False, *args, **kwargs)

File "/path/to/venv/lib/python3.6/site-packages/django/db/models/query.py" in _filter_or_exclude
  862.             clone.query.add_q(Q(*args, **kwargs))

File "/path/to/venv/lib/python3.6/site-packages/django/db/models/sql/query.py" in add_q
  1263.         clause, _ = self._add_q(q_object, self.used_aliases)

File "/path/to/venv/lib/python3.6/site-packages/django/db/models/sql/query.py" in _add_q
  1289.                 joinpromoter.add_votes(needed_inner)

File "/path/to/venv/lib/python3.6/site-packages/django/db/models/sql/query.py" in add_votes
  2171.         self.votes.update(votes)

File "/usr/lib/python3.6/collections/__init__.py" in update
  620.                     super(Counter, self).update(iterable) # fast path when counter is empty

Exception Type: ValueError
Exception Value: dictionary update sequence element #0 has length 9; 2 is required

Код вокруг request.user.is_authenticated:

def token_queries(request):    
    data = None
    if request.user.is_authenticated:
        data = login_required(request)
    if data is None:
        data = anonymous(request)
    return json_response(data)

Обновлено: Дополнительная информация

Я должен уточнить, что эта ошибка срабатывает один раз в день, ближе к пику моей активности на сайте. Как только он сработает, он будет продолжать спамить в течение нескольких часов, прежде чем остановится. Это также происходит не только в файле tokens,py, но и почти везде в коде, где пользователь пытается получить доступ.

ОБНОВЛЕНИЕ: что-то связанное с балансировкой нагрузки

Ошибка, похоже, связана с балансировки нагрузки. Когда ошибка начинает спамить, это происходит только на одном узле LB за раз. Я обнаружил, что удаление узла из моего балансировщика нагрузки предотвратит это. Это даже не обязательно должен быть узел, на котором происходит ошибка.! Если я снова включу оба узла, ошибка сразу же начнет срабатывать снова.

Например:

  • Узел 1 начинает рассылать спам об ошибке
  • Я беру узел 2 из LB
  • Ошибка останавливается
  • Поместите резервную копию узла 2, рассылка спама об ошибках перезапустится мгновенно

Единственный способ остановить это — перезапустить машину, на которой размещен узел, на котором произошла ошибка.

Добавление липкости на 1 час к LB не решило проблему.

Я запускаю Django через Nginx на сервере AWS.

Покажите нам код def token_queries() в токены.py в строке 87.

dirkgroten 20.02.2019 12:00

@dirkgroten добавил код. Эта ошибка возникает почти во всех местах, где я пытаюсь получить пользователя.

Ryan Pergent 20.02.2019 13:54

Как определена ваша пользовательская модель? Каково значение AUTH_USER_MODEL в ваших настройках?

dirkgroten 20.02.2019 16:08

Я использую AUTH_USER_MODEL по умолчанию (никогда не менял значение)

Ryan Pergent 21.02.2019 10:46

Что вы используете для своих занятий? django.contrib.sessions.middleware.SessionMiddleware по умолчанию? И SESSION_BACKEND по умолчанию? Можете ли вы установить точку останова перед if request.user... и напечатать request.session['_auth_user_id']

dirkgroten 21.02.2019 11:39

Использование django.contrib.sessions.middleware.SessionMiddleware и никаких изменений не было сделано один SESSION_BACKEND. К сожалению, эта ошибка возникает только в рабочей среде, и я не могу воспроизвести ее локально, чтобы использовать точку останова. Он также будет нормально работать в течение нескольких дней подряд, но затем внезапно сработает и будет спамить ошибку в течение нескольких часов, прежде чем, наконец, остановится...

Ryan Pergent 22.02.2019 09:25

Глядя на код Django, я думаю, что эта ошибка может быть вызвана, если что-то не так в вашем сеансе. Возможно, поврежденная запись в таблице сеансов. Возможно, вы захотите очистить сеансы с истекшим сроком действия (если у вас нет задания cron, которое уже делает это регулярно), а затем непосредственно взглянуть на таблицу сеансов базы данных в рабочей среде, чтобы увидеть, сможете ли вы обнаружить поврежденную строку.

dirkgroten 22.02.2019 09:50

На самом деле вы должны добавить попытку, кроме как сейчас, вокруг вашего ошибочного оператора, чтобы напечатать дополнительную информацию в журналах при возникновении исключения. Например, зарегистрируйте файл cookie сеанса запроса, чтобы позже вы могли получить этот сеанс в рабочей оболочке.

dirkgroten 22.02.2019 09:54

Что заставляет вас думать, что это происходит из-за поврежденных записей сеансов? На самом деле, у меня раньше эта ошибка также вызывалась другой, говорящей 'SessionStore' object has no attribute '_session_cache', так что вы можете что-то понять.

Ryan Pergent 23.02.2019 23:35

@dirkgroten попробовал какое-то сложное решение и удалил все сеансы в django_session.... К сожалению, ошибка все еще рассылает спам.

Ryan Pergent 25.02.2019 09:43

Что заставило меня подумать, что это как-то связано с сеансами, так это то, что я думаю, что способ получить вашу ошибку, если user_id, который извлекается из сеанса, не является целым числом. Я думаю, что добавление некоторых конкретных журналов — единственный способ получить больше информации. Постарайтесь записать как можно больше информации при возникновении ошибки.

dirkgroten 25.02.2019 18:51

Это не проблема, связанная с вашей моделью пользователя, но я думаю, что в вашем urls.py. Покажите нам URL-адрес приложения

Wariored 25.02.2019 20:42

@Wariored Что ты имеешь в виду? Если возможно, я хотел бы показать больше кода, только если это действительно необходимо. Приложение будет работать нормально в 90% случаев, но примерно раз в 2 дня ошибка, о которой я говорю, начнет безостановочно рассылать спам в течение нескольких часов подряд.

Ryan Pergent 26.02.2019 10:06

Не могли бы вы поделиться url.py, который указывает на функцию token_queires?

sun_jara 26.02.2019 22:40

Какое промежуточное ПО сеанса и серверную часть аутентификации вы используете?

Devang Padhiyar 27.02.2019 14:51

@sun_jara, в файле URL нет ничего особенного. Какую проблему вы ищете?

Ryan Pergent 27.02.2019 20:55

@DevangPadhiyar 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware' и в настройках: SESSION_ENGINE = "django.contrib.sessions.backends.cached_db". Я также использую фреймворк django rest.

Ryan Pergent 27.02.2019 20:56

Любое пользовательское промежуточное ПО?

Trent 28.02.2019 05:15

@Trent нет пользовательских промежуточных программ :(

Ryan Pergent 28.02.2019 14:25

Судя по строке user = UserModel._default_manager.get(pk=user_id) в стеке ошибок, я думаю, что что-то не так с вашим ответом на запрос к базе данных. Это также объясняет, почему это происходит только на производстве. Какую базу данных SQL вы используете?

gdlmx 02.03.2019 02:55

Я сомневаюсь, что это как-то связано с сеансами, промежуточным программным обеспечением или базой данных. Проблема возникает во время построения запроса UserModel._default_manager.get(pk=user_id) при определении того, какой тип JOIN следует использовать.

Simon Charette 02.03.2019 07:08

@gdlmx Я использую MySQL с INNODB

Ryan Pergent 04.03.2019 10:21

@RyanPergent абсолютно одинаковы, давайте помогать друг другу и публиковать все версии программного обеспечения: nginx 1.19.2 gunicorn==20.0.4 django "==3.0.9 python3.6 pipenv, docker Gunicorn начинается со следующей строки (это может быть важно) : gunicorn conf.wsgi:application --bind 0.0.0.0:8000 --workers 5 --worker-connections=1000

Ivan Borshchov 31.08.2020 14:29

@IvanBorshchov Stack Overflow также требует, чтобы в вопросе было достаточно информации, чтобы можно было воспроизвести проблему. Наш формат не предназначен для того, чтобы просто гадать, почему что-то происходит. Судя по описанию проблемы, это также тесно связано с проблемой конфигурации производственной системы, а не с проблемой программирования, что делает ее неактуальной здесь, в Stack Overflow. Может быть программное решение проблемы, но если это так, то в вопросе необходимо указать минимальный воспроизводимый пример, который фактически разрешает дублирование проблемы.

Makyen 31.08.2020 22:26

Эта ошибка возникает довольно глубоко в ORM; логика add_votes определяет, следует ли использовать LOUTER JOIN вместо INNER. Не могли бы вы предоставить более подробную информацию о том, какой сервер WSGI вы используете. Это случайно не mod_wsgi Apache? Кроме того, у вас есть кастомный AUTH_USER_MODEL, который делает что-то странное с его базовым менеджером?

Simon Charette 02.03.2019 06:54

@Makyen Главный вопрос этого вопроса - КАК создать шаги для воспроизведения, это происходит очень часто. Такие проблемы очень редки, т.к. случаются после месяца производственной работы. И воспроизвести их абсолютно сложно. Жаль, что вопрос закрыли. Это могло бы спасти многих людей и развить отрасль в целом. Невозможность воспроизвести просто откладывает проблему и делает следующий фреймворк непригодным для использования

Ivan Borshchov 03.09.2020 10:08
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
26
293
2

Ответы 2

Возможно, проверьте, чтобы настройка СЕКРЕТНЫЙ КЛЮЧ была одинаковой на каждом из ваших узлов.

Просто догадка, основанная на вашей балансировке нагрузки и комментариях к сеансу.

Оба узла имеют одинаковый код (синхронизированный репозиторий). Если бы секретный ключ был неправильным, ошибка происходила бы постоянно, а не периодически, верно?

Ryan Pergent 04.03.2019 10:20

Согласитесь, выглядит довольно странно, у меня такая же проблема, секретный ключ стабилен

Ivan Borshchov 31.08.2020 14:26

Я могу определить ненормальную строку в вашей трассировке ошибок:

File "/usr/lib/python3.6/collections/__init__.py" in update
  620.                     super(Counter, self).update(iterable) # fast path when counter is empty 

В нормальных условиях эта линия никогда не должна быть достигнута. Если вы посмотрите на исходный код в "collections/__init__.py",

if isinstance(iterable, Mapping):
    ...
        super(Counter, self).update(iterable) # fast path when counter is empty

Переменная iterable была передана от переменной needed_inner в функции _add_q. Его тип всегда должен быть <class 'set'>. Поэтому isinstance(iterable, Mapping) никогда не должно быть True.

Как вы сказали, вы не использовали пользовательский AUTH_USER_MODEL. Таким образом, наиболее правдоподобным объяснением является скрытая ошибка в реализации Python Nginx WSGI (возможно, связанная с балансировкой нагрузки).

Другие вопросы по теме