#программирование

Жуткая история одного пользователя

На днях я столкнулся с весьма загадочным и мистическим явлением в лучших традициях сериала «Секретные материалы». Аж кровь в жилах застыла. В общем, удалили пользователя из системы, а душа его осталась плутать по закоулкам и пугать все входящие API запросы. Да так, что сторонние системы, которые эти запросы слали, в ужасе шарахались и бросали NullPointerException. Если не хотите спать сегодня ночью, то смело заглядывайте сюда и узрите сию жуткую историю.

Произошло это, как и куча всякого другого дерьма, в 2020 году.

У меня была интеграция, разработкой которой я занимался, и сторонняя система, API которой дергала моя интеграция. Чтобы не путаться, давайте назовем систему Системой, а интеграцию – ни много ни мало, Интеграцией. 

Интеграция моя отвечала за то, чтобы получать на вход email и создавать в Системе пользователей с этим email и другими заданными атрибутами. Если же пользователь уже существовал в Системе, Интеграция должна была получить его по айди и присвоить эти самые атрибуты. Проблема заключалась в том, что айди пользователя нам заранее неизвестен, и никаких способов получения пользователя по email Система не предоставляла. Поэтому мне приходилось итерироваться по всем пользователям в Системе (благо, их там довольно мало, и сильно много их стать не должно) и искать совпадающий email:

users.stream().filter(user -> user.getAccount().getEmail().equals(email)).findFirst();

Особых проблем это не доставляло, и Система возвращала ответ в следующем формате:

"Users": [{
  "id": 42,
  "displayName": "Artem Krutov",
  "account": {
    "id": 42,
    "username": "artem.krutov",
    "email": "user@email.com"
  }
},
{
  ...
}]

Как видно, возвращался нам массив пользователей, и email каждого из них был доступен во внутренней структуре account.

Тщательно протестировав Интеграцию и прогнав несколько реальных сценариев, я отправился на заслуженный отдых.

И тогда это началось…

Шутка, началось все тогда, когда я через интерфейс Системы удалил созданного тестового юзера, чтобы он не путался под ногами, да и в целом, чтобы его никто не видел. А вот когда я его удалил… душа его осталась бродить по длинным и темным коридорам системы.

До поры до времени все было хорошо. Но когда один из пользователей был удален в Системе, и его темная душа начала пугать мои запросы, моя Интеграция начала себя странно вести и бросаться ошибками. Проблема была все в той же строке с итерированием:

users.stream().filter(user -> user.getAccount().getEmail().equals(email)).findFirst();

А все потому, что душа удаленного пользователя все еще присутствовала в ответе Системы:

{
  "id": 42,
  "displayName": "Artem Krutov",
  "account": {
    "id": 42,
    "username": "deleted-42",
    "email": null
  }
}

Видите? Данные пользователя обнулились, но он все равно возвращается в ответе. И, так как Интеграция предполагает, что раз мы нашли пользователя, то должен быть и его уникальный идентификатор в виде email, а его не оказалось, падал NPE:

java.lang.NullPointerException: null
NullPointerException

Это выглядит довольно стремно и костыльно. Зачем возвращать мертвого обнуленного юзера, с которым никак нельзя работать? Но из всего этого можно вынести простую мораль:

КОГДА ВЫ РАБОТАЕТЕ СО СТОРОННИМИ СИСТЕМАМИ, ОЖИДАЙТЕ ОТ НИХ ПОДСТАВ

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

if (users != null) {
  return users.stream().filter(user -> {
    if (user != null && user.getAccount() != null && user.getAccount().getEmail() != null) {
      return user.getAccount().getEmail().equals(email);
    }
    return false;
  }).findFirst();
}
return Optional.empty();

Всегда относитесь к сторонним системам с подозрением, ибо они в любой момент могут начать слать что-то неожиданное. Проверяйте полученные данные и отвергайте невалидные ответы. Обезопасьте себя от плохих ответов заранее, а не тратьте время на анализ логов, как это сделал я!

Понравилось? Подписывайтесь на меня в соцсетях!

 
Twitter
VK
guest
19 Комментариев
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Татьяна Крутова
Татьяна Крутова
3 лет назад

Кошмар! Впервые вижу настолько костыльное решение для удаления) Системный аналитик во мне негодует, а воображение рисует все трудности отлавливания таких багов. Бррр

Руслан
Руслан
3 лет назад

При сравнении данных в Java всегда лучше вызывать equals от той переменой, от которой есть уверенность что она не null. По крайней мере, я выработал эту привычку у себя. Конечно, это не спасет если аккаунт например null, но зачастую в 90% случаев помогает.

trackback
24 дней назад

does augmentin cause yeast infections

does augmentin cause yeast infections

trackback
24 дней назад

is celebrex safe for long term use

is celebrex safe for long term use

trackback
7 дней назад

amitriptyline / chlordiazepoxide

amitriptyline / chlordiazepoxide

trackback
7 дней назад

acarbose metabolism

acarbose metabolism

trackback

ezetimibe and atorvastatin combination

ezetimibe and atorvastatin combination

trackback
7 дней назад

robaxin erowid

robaxin erowid

trackback
2 дней назад

abilify dosages for depression

abilify dosages for depression

trackback
2 дней назад

tizanidine moa

tizanidine moa

trackback
2 дней назад

protonix side effects weight gain

protonix side effects weight gain

trackback
2 дней назад

januvia e repaglinide

januvia e repaglinide

trackback
2 дней назад

synthroid skinny

synthroid skinny

trackback
2 дней назад

spironolactone other names

spironolactone other names

trackback

is voltaren 1 percent gel the same medication diclofenac gel

is voltaren 1 percent gel the same medication diclofenac gel

trackback
2 дней назад

remeron pills

remeron pills

trackback

rx sitagliptin phosphate & metformin hydrochloride tablets

rx sitagliptin phosphate & metformin hydrochloride tablets

trackback
2 дней назад

actos ciudadanos

actos ciudadanos

Social media & sharing icons powered by UltimatelySocial
19
0
Нравится? Оставьте комментарий!x
()
x