Почему разработчику middle и middle+ не стоит бояться длительной работы на одном проекте? Может ли саппорт помогать развиваться, находить нестандартные решения, видеть весь бизнес-процесс и конечных пользователей?  В прошлой статье мы рассказали об опыте нашего коллеги Алексея, который после 7 лет программирования на зарубежном проекте неожиданно для себя возглавил службу поддержки третьей линии. Практика показала, что было принято верное решение. Поскольку ему удалось не только выстроить работу службы, но и улучшить бизнес-процессы заказчика.  Когда разработчик понимает, что написанную фичу предстоит поддерживать ему самому 5 лет и более, как в нашем случае, появляется понимание бизнес-процессов, более полное представление о системе и ее конечных пользователях, а значит и качество кода будет расти. В этой статье мы расскажем про первые семь кейсов, которые были найдены и устранены только за последний год. Каждый из них привел к появлению запоминающейся “татуировки”, которые теперь всегда учитываются при постановке новых задач.  Материал может быть полезен как разработчикам уровня middle и выше, так аналитикам и QA. Возможно, они позволят по-новому взглянуть на бизнес-процессы и предотвратить некоторые баги.  Был отличный зимний день, ничего не предвещало беды. Внезапно данные от одного госпиталя перестали приходить. Прошло 5 минут, мы были спокойны – ранее такое случалось несколько раз по 10-15 минут из-за проблем в сети госпиталя.  10 минут. Начинаем испытывать легкое беспокойство, на всякий случай откладываем задачи, чтобы быть готовыми срочно переключиться на проблему. Госпиталь молчит. 15 минут – идем на RDP, на виртуальную машину, где установлено наше ПО, и видим, что все сервисы, которые общаются с внешними системами, не работают. Не могут подключиться к внешним адресам. Перепроверяем часть этих сервисов. Другие госпитали тоже с ними работают, но у них проблем нет. Копаем, где может быть проблема. Спустя час понимаем, проблема не на нашей стороне. Далее формируем запрос в IT-службу госпиталя, описываем проблему, уточняем, не было ли изменений на их стороне. Получаем ответ, что они изменили Firewall. Сделали этого для того, чтобы предотвратить проблемы с log4j, потому что она может быть использована для “взлома” госпиталя. “Мы просто закрыли все “внешнее”, - сказали они нам. В этот момент стало понятно, что произошло. Мы попросили у них открыть обратно нужные адреса, упомянули, что в документации к системе есть список адресов, с которыми мы общаемся, и их не надо закрывать. Добавили то, что наша система написана на .net и log4j там не используется, а в смежных системах (не наших) используется версия без этой уязвимости. Вывод: когда IT-службе страшно, они вначале рубят все, что можно, а только потом начинается исследование, что нужно или не нужно рубить. Но перед этим бывает очень полезно прочитать документацию к системе.    С одним госпиталем мы работаем уже давно, проблем особых никогда не было. Но неожиданно в саппорт стали приходить запросы, что система “тормозит” с утра. Этот госпиталь предоставляет доступ в виртуальную машину, где установлена наша система, по запросу. В нем должна быть указана причина доступа и т.д. После этого выдается одноразовый пароль, и мы можем начать исследование. Проблема в том, что этот процесс коммуникации занимает час-полтора. Не очень удобно, но политика безопасности госпиталя неизменна. Получили доступ, зашли, смотрим – все хорошо, никаких нагрузок на диск или CPU нет, память свободная есть, свободного места на диске много. Да и система не тормозит при проверке. Жалобы тоже прекратились. На часах 9 утра. Закрыли запрос по причине невоспроизведения проблемы. Удивлены, но в целом спокойны. Рабочий день завершился без проблем на стороне госпиталя, других жалоб не поступало. На следующий день – опять проблемы производительности, снова с утра. Получаем доступ к системе – опять все хорошо в 9 часов. На третий день снова ожидаем проблемы с утра, хотя и надеемся, что они не возникнут. А зря, возникли. А в 9 часов снова все хорошо. Поняли, что нам надо быть в системе в 8 часов, смогли получить доступ заранее – в виде исключения. Обычно они такого не делают – пароли работают в течение трех часов после их получения, но мы уговорили на 24 часа. 8 часов, мы на боевом сервере.  8:02  мы видим очень сильное увеличение нагрузки на диск. Посмотрели, оказалось что бэкап боевой БД перенесли с 4 утра на 8:02 – рабочее время, где он давал нагрузку на диск, и как следствие, возникали проблемы. Стало понятно, почему мы до этого не могли детектировать проблему: пока получали доступ к серверу, проходил примерно час, бэкап заканчивался и все становилось хорошо. Написали в госпиталь, уточнили, зачем они перенесли бэкап.  Нам ответили, что это не должно влиять на систему, поскольку бэкап – системная функция.  Удивились, когда мы приложили скрин, где было видно, что нагрузка на диск из-за бэкапа. И все равно продолжали утверждать, что она не должна влиять на систему. Договорились убрать бэкап на один день, чтобы проверить гипотезу. В итоге утром никаких проблем не возникло.  После этого уже госпиталь задумался и начал выяснять, кто и почему изменил время бэкапа. Оказалось, что наняли нового системного администратора и поручили отвечать за то, чтобы бэкапы делались без проблем и правильно. А как можно было за это отвечать? Конечно, смотреть, как он делается. Этот администратор и перенес создание бэкапа на рабочее время – приходит на работу к 8 часам, смотрит, как делается бэкап, видит, что все хорошо, и работает дальше. Вывод: люди бывают разные. Часто они не оповещают об изменениях, которые делают, и не задумываются, почему выбрано то или иное время для какой-либо активности. Также часто люди не признают, что сделали что-то не то.   Многие госпитали имеют свой SMTP-сервер для отправки писем. В саппорт периодически приходят запросы на создание какого-либо отчета для госпиталя (например, количество пациентов, которые обратились в госпиталь за день). При этом просят отправлять отчеты по почте через их SMTP-сервер. Сделали мы один такой отчет по количеству пациентов в отделении. Как оказалось позже, его надо было изменить, и специалист из IT-службы госпиталя его скорректировал. В хранимой процедуре на SQL использовал курсор по временной таблице, только забыл удалить из нее обработанные данные. В результате получился вечный цикл, и примерно 24 тысячи писем ушло одному доктору. Доктор был не рад… Да и SMTP-сервер перестал отзываться из-за такого количества писем. После этого во всех отчетах мы стали делать не прямую отсылку почты через стандартную функцию SQL, а через “обертку”. И внутри этой обертки сделали счетчик отправленных писем, который обнуляется каждый день. Если в день через эту оберточную функцию отправляется более 2 тысяч писем, все последующие письма она автоматически складывает в специальную таблицу, но не отправляет. А нам приходит уведомление о том, что неожиданно большое количество писем в очереди на отправку. Когда мы создавали эту оберточную функцию, мы делали её как улучшение из-за инцидента на “боевом” сервере. Хотя слабо верили, что это надо. Но буквально в конце прошлого года госпиталь решил делать миграцию одной внутренней интеграционной системы. При этом надо было обработать все работы с 2011 года. В целом это не проблема, поместили их в очередь на обработку, и она начала обрабатываться по бизнес-логике.  Однако, возникла проблема. В 2015 году была сделана интеграция с другой интеграционной системой с изменением бизнес-логики. И по ней “старые” работы стали некорректными, хотя они были завершены, и система неожиданно стала отправлять письма про эти старые работы. Как оказалось позже – более 100 тысяч писем. К счастью, “ушло” только 2000 писем из-за того самого лимита по количеству писем в день, и SMTP-сервер выдержал нагрузку и все прошло без серьезных проблем. Вывод: любое взаимодействие, при котором данные отправляются “наружу”, лучше обернуть своеобразным “предохранителем” по количеству отправок. Это может спасти от довольно больших проблем.   Один из унаследованных API, который мы предоставляем некоторым клиентам, отвечает за получение аудиофайлов и возврат их содержимого в виде текста после автоматической или ручной обработки в зависимости от договора с клиентом. Логика API проста: Мы получаем аудиофайл от клиента и возвращаем уникальный номер, который характеризует этот файл (одна из функций API, RecieveDictation). Далее выполняем обработку этого файла на нашей стороне и выставляем статус “готово” после окончания обработки. Клиент должен теоретически вызвать функцию API для получения уникальных номеров готовых работ 1 раз в 5-10 минут (функция API GetPendingJobs). И для каждой работы через другой метод API получить результаты обработки аудиофайла (функция GetTranscription). Также есть вспомогательные функции для передачи получения статуса работы по идентификатору и для корректировки, но они не обязательны в бизнес-процессе. Но когда мы стали статистически проверять количество вызовов функции, получили вот такой график.   Ожидания совпали только по функции для получения аудиофайла со стороны клиента. Мы удивились такому распределению и стали исследовать, почему система работает без замечаний. Как оказалось, в коде сервиса было условие – если аудиофайл не обработан, то в качестве результата вернуть пустую строку. Когда мы изменяли и улучшали сервис, к счастью, мы не удалили это условие – возможно, оно было излишним, но не было неправильным. И именно из-за этого условия система работала без замечаний. Получилось, что клиент просто раз в 5-10 минут пытался получить результат для всех аудиофайлов. И так каждый раз, пока реально вернется не пустая строка. Выводы: 1) Даже если будет документация для API, пример интеграции с API, который мы выдаем клиенту “бонусом”, все равно есть вероятность, что API будут использовать весьма необычным способом. 2) Необходимо добавлять контроль статистики количества вызовов и проверять эту статистику на неожиданные вещи. В описанном проекте наше решение помогает обрабатывать результаты визитов пациентов за нужный интервал времени. Классическим показателем для оценки качества решений является изменение количества жалоб пользователей и запросов в саппорт. После того, как саппорт-процессы были переделаны и проведены улучшения, мы захотели проверить, что все хорошо работает, опираясь не только на данные техподдержки. Для этого мы ввели контрольные точки проверки качества. Так например, обработка любого визита к врачу должна быть сделана за 2 недели. Кажется, что все довольно просто. Но после добавления этой метрики оказалось, что не все визиты пациентов в системе обрабатываются в эти сроки. Мы начали контактировать с госпиталями, пытаясь понять, что не так. Программных проблем при обработке “плохих” визитов не было, мы проверили все логи ошибок. После общения с госпиталями выявили две проблемы: Если врач уходил в отпуск, он иногда забывал “передавать” пациента другому врачу. И это была ошибка бизнес-процесса работы с пациентами. Иногда для установления диагноза нужны анализы. При этом требовалось записать пациента на них, сдать, обработать результаты, передать новые данные доктору, и только после этого – поставить диагноз. Сейчас, спустя некоторое время, все это кажется логичным. Но после обнаружения этих кейсов мы смогли более точно сформулировать SLA (service level agreement) и лучше сегментировать причины отклонений от целевых показателей. Госпиталь со своей стороны также добавил в регламент ухода докторов в отпуск новые пункты. Выводы: 1) Если в бизнес-процессе есть ошибка, программное решение не сможет её исправить. Ошибку или отклонение можно детектировать с помощью анализа статистики в хранилище данных. Чтобы её исправить, надо “надеть шляпу аналитика” и улучшить бизнес-процесс. 2) Не важно, насколько современные или несовременные технологии используются при реализации программы для поддержания бизнес-процесса, если бизнес-процесс имеет недочеты. 3) Даже очевидные требования к системе лучше материализовывать в виде контрольных точек, которые нужно регулярно проверять и анализировать отклонения. 4) Часто проблему вначале можно решить изменением регламентов и, если они работают, постараться автоматизировать это изменение. Однажды к нам поступил запрос из саппорта, что система в одном из госпиталей работает медленно. Там она была развернута на виртуальной машине, которая находилась на железе госпиталя. Мы начали исследовать. Выяснили, что все запросы выполнялись, проблем timeout не было, нагрузка на диск, которую мы видели в Performance Monitor, была в пределах нормы – до 1 мегабайта на чтение и запись, CPU тоже был в норме, памяти свободной было много. Но жалобы продолжали поступать. В тот момент у нас зародилось подозрение, что проблемы в системе создает что-то внешнее, но доступа вне виртуальной машины у нас не было, госпиталь не собирался его давать – такова была их политика безопасности. Встал вопрос о том, как найти доказательства, что проблема не в нашей системе. Подозрение пало, прежде всего, на диск. Именно он теоретически должен был разделяться между несколькими виртуальными машинами. Стали искать доказательства. Мы реализовали элементарную утилиту – она копировала файл в 100 мегабайт, потом его считывала, а мы получали время, которое реально необходимо для чтения и записи. Если бы проблемы были с диском, время было бы непостоянным. Через 2 дня у нас было доказательство того, что проблемы в нашей системе – следствие высокой нагрузки на диск из-за других виртуальных машин. Вот пример графика, который мы получаем по реальной производительности диска каждый день. Как видно из графика, с 15 по 17 декабря диск работал намного медленнее, чем обычно. Позже госпиталь признал, что они проводили “технические работы”. Вывод: виртуализация – это хорошо, но в конечном счете программа выполняется на конкретном железе, и между виртуальными машинами может быть разделяемый ресурс. Если у саппорта нет доступа до хост-сервера, реальное время выполнения простых операции лучше измерять каждый час и на основании этого предоставлять доказательства тому, кто отвечает за железо.   Эта татуировка появилась после двух случаев на саппорте. Первый: из одного госпиталя пришел запрос с просьбой исправить проблему: в форме поиска в поле “Автор” имя доктора удваивалось. Причем только по конкретному врачу. Мы начали исследовать данные. К счастью, имена докторов не являлись конфиденциальными данными и у нас к ним был доступ. Оказалось, что есть два врача с одинаковым именем, но это разные люди. При этом один из них не заходил в систему в течение трех лет.  В итоге мы создали архив пользователей и перенесли его в архив. Это исправило проблему. Но мы задумались, как можно снизить вероятность такого события в будущем. Поскольку полностью исключать вероятность того, что появятся доктора с одинаковой фамилией, нельзя. В этом конкретном госпитале был бизнес-процесс, который увеличивал описанную вероятность: каждое лето туда на практику приезжали студенты-медики, и им создавали логин в системе. По статистике 85% всех пользователей не заходило в систему в течение двух и более лет. Но при этом среди этих пользователей нельзя было отделить студентов от “постоянных” врачей. Мы попросили госпиталь блокировать информацию по таким студентам после окончания практики.  Второй случай: мы решили привести в порядок версии SQL на всех серверах, собрать статистику и настроить оповещения, если госпиталь обновляет SQL server и, как следствие, меняет версию SQL. Когда мы получили этот проект, SQL server 2012 был самой последней версией. Ожидаемо периодически госпитали делают обновление SQL-сервера. Это важно еще и потому, что некоторые функции последних версий SQL не поддерживаются в более ранних версиях. При разработке конкретного запроса на новую фичу это надо учитывать. После добавления функции сбора информации о версии SQL мы обнаружили, что в одной из подсистем, которая реализовали на “железе” госпиталя, был установлен SQL Express. 8 лет назад это казалось довольно логичным – он бесплатный даже для коммерческого использования, и госпиталь этим воспользовался. Тем более, что в той базе данных не выполнялось никаких тяжелых запросов. Все запросы информации о пациенте проходили по первичному ключу. За эти 8 лет накопились данные, логи сообщений и прочее, размер БД был уже 9.4 GB, а в той версии SQL Express было ограничение на максимальный размер файла БД – 10 GB. По нашим расчетам, через 3-5 месяцев этот лимит сработал бы, и система могла “лечь”, потому что не сумела бы вписать в БД новые данные.  Когда мы начали анализировать, оказалось, что там хранились логи с 2012 года и не было никакой политики хранения данных. Мы согласовали её, стали удалять все логи, которым больше трех лет. В итоге размер данных в той БД стал всего лишь 4.2 GB – абсолютно комфортный уровень. Выводы: 1) В любой таблице должно быть поле CreatedDateTime_ИмяТаблицы, чтобы во время исследований саппорта можно было понять, когда были созданы данные. 2) Любая таблица должна иметь политику хранения данных. Старые данные должны или удаляться или переноситься в архив. Если нет такой политики, через год или более могут возникнуть проблемы. Объем данных увеличивается, запросы будут выполняться медленнее и т.д. При этом деградация будет постепенной, сложно заметной. Но когда система дойдет до “критической” точки, исправить ситуацию будет тяжело. В следующей статье мы расскажем о других интересных кейсах и “татуировках”, которые мы отработали на саппорте в этом зарубежном проекте.  Продолжение следует….  Часть 1: https://habr.com/ru/company/simbirsoft/blog/656275/   Часть 3: https://habr.com/ru/company/simbirsoft/blog/658599/