Skip to Content

Как упала сегментация

Без двадцати двенадцать ночи позвонил этот придурок Рафи и сообщил, что EndOfDay накрылся пиздой на заре своей молодости. 
Ах, как нежно я люблю этот субботний EndOfDay
Система не дура, она, видно, красиво отдыхает по субботам, и вечером с бодуна ТАКОЕ вытворяет… То потеряет нахуй connection – то-то мы его бегом на динамический меняем, заебал, зараза, - то у пары десятков основных таблиц неожиданно упадут все индексы…
А в позапрошлом месяце был большой шухер, когда сервер вдруг решил жить по Москве, и вместе с русскими самовольно перевел часы на летнее время. Тут же потерял все свои файлы, и я провела веселую ночь.
Но и сегодня было нехило. EndOfDay ебнулся на этапе с многообещающим названием «Create Segmentation File»... 

При слове «сегментация» у всех тружеников нашего проекта начинается тремор и заикание. Это тема!!!

Когда-то мое начальство решило повоспитывать двух наших молодых девочек, и отдало тему им. В лапы нашей Рути – бабушке системного анализа. У Рути старческий маразм и фрагментарное восприятие. При этом девочкам она безбожно хамит, и вещает, как Кассандра, хотя скорость речи у нее заметно опережает скорость протекания простейших интеллектуальных процессов. Одна из девочек собиралась рожать, вторая, существо мягкое и бесконфликтное, просто до смерти боялась спорить. 


Когда-то в палеолите я написала сервер встречного процесса с жутким названием «синхрун». Мой тогдашний аналитик до сих пор содрогается, когда вспоминает, КАК я ебала ему мозги.

Я зануда. Ни один логический карман не избЁг дотошного обсосания. Я поставила вилки под любое сочетание условий, которое только могла вообразить, и отказалась работать дальше, пока не получу исчерпывающий ответ, что делать программе в каждом из вариантов. Крики «так не бывает никогда!!!!» не имели на меня никакого влияния. «Ну, а вдруг?!» - тоном кинопровокатора вопрошала я… И от винта.

После того, как сервер был закончен и встал в production, никто и никогда больше не влезал в его внутреннюю логику. Синхрун не ошибся ни разу, и ни разу не упал на обработке, хотя все до единого «крайние случаи», которые мой аналитик с юзерами считали невероятными, в тот или иной момент явили миру свой лик…

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

За два дня – не до тестов! – а до первого срока постановки в production!!!!! они еще спорили о бизнес-логике!!!!!
Проваливая тест за тестом, Рути находила все новые и новые карманы, каждый из которых, в силу фрагментарности мышления, казался ей уникальным сочетанием условий. Каждые три дня она догадывалась, что опять ни хуя не понимает, и мучительно изобретала совершенно новую концепцию процесса, а девочки, точнее, одна несчастная, не ушедшая к тому времени в декрет, девочка, начинала переписывать программу в очередной раз. Всего, как я поняла, сегментацию от начала до конца переделывали пять раз. 
После пятого раза вдруг обнаружилось, что обильные и разхуебразные проверки ввода в этой auto сегментации почему-то разительно отличаются от проверок ручной сегментации, форма которой уже давно стоит в системе. Это навело такую пиздоту в данных, что начальство призадумалось. Когда же Рути в очередной раз пропела: «Ой, а этот вариант мы не проверяли, я как-то о нем не подумала…» - начальство не выдержало и отложило upgrade.
И вызвало меня. И велело сделать дня эдак за три единый проверочный модуль для обоих процессов сегментации.
«Черт с тобой, - читалось у начальства в глазах, - Нуди, зуди, только наведи хоть какой-нибудь порядок в этом бардаке!»

Ознакомившись с хозяйством, я побежала за валерианкой. Здесь было все для человека, который решил бы написать учебник «как не нужно программировать». Гигантские процедуры «простынями», без логических отступов, когда невозможно отыскать ни условий, ни конца вилки. Дублирующиеся бессмысленные названия функций и переменных. GoTo до пизды и больше. Перекрывающие друг друга, или наоборот, оставляющие «непокрытые» сектора возможных данных проверки…

Я сказала начальству, что тут ебаться недели две.

Я посадила рядом с собой Рути, и мы поползли по данным и по коду.
Каждое слово этой престарелой дамы я, дабы она потом не смогла от него отказаться, заносила тут же у нее на глазах в excel-файл. 
Мы нашли четыре недостающие проверки и две излишние.
Потом мы поругались. Я сказала, что такие-то пять проверок суть одна и та же проверка, и их нужно собрать в одну процедуру. 
Рути долго вопила и обижалась. Она говорила, что вторая проверка и пятая уже написаны Леной в двух местах из шести. И работают. И ей непонятно, почему их нужно переписывать. Когда проще найти еще четыре места, там дописать все пять, а в двух уже существующих местах дописать еще три. Она жаловалась, что я не понимаю логики программы.
Я с ней соглашалась. Ни хуя я в ее программе не понимаю. Но так, как она хочет, не работают.
Призвали начальство. Я даже не стало его, толковое, посвящать в подробности. Я просто сказала, что дублировать шесть раз код у меня не встанет. Начальство не удивилось моей спорной половой самоидентификации, сделало круглые глаза на цифру шесть, инцидент был исперчен. 

Потом я написала модуль. Сегментация прошла второй тест через полчаса после начала проверок. Рути цвела. Я сказала ей: «Рути! Это еще не конец. Я поправила только самую больную часть. У тебя будут проблемы, и много, потому что твоя мудацкая программа не покрывает все логические карманы.» - «Нет, - сказала счастливая Рути, - я уверена, что теперь-то мы предусмотрели все…»

Сегментация встала в предыдущую версию. Прошла однажды гладко – файл сегментации приходит раз в месяц. И вот… Без двадцати двенадцать мне позвонил этот придурок Рафи…

Я общалась с Рафи полчасика, тщетно стараясь научить его посмотреть лог. Ситуевина усугублялась тем, что сервер запускался на одном компе, а активизирующий его триггер-екзешник, где стояла отличная моего изготовления ЛОГ-гляделка – почему-то на другом. И этот мудоеб Рафи не мог найти триггер! Он и слова-то такого выговорить не мог… Итого, я так и не поняла, какая же ошибка так жестоко выбивала сервер…


Ситуация была аварийная: хоть езжай. Упала вечерняя часть. Не утренние репорты (в субботу они тоже бегут вечером), а ежедневная обработка базы. Перейти этап и топать дальше, как я легко сделала бы с репортами? Хер его знает, на что повлияет этот недосозданный файл…

Я прикинула расклад. Ехать дико ломало… Ну, найду я хуйню какую… И как я чинить это буду без доступа? Там даже через жопу автогеном не доберешься. К ебеням такой энтузиазЬм.

Я шмыгнула носом и порешила разбудить начальство. Начальство посопело - оно тоже не знало, что отвалится от недосозданного файла, - и порешило позвонить Эсти. Эсти у нас на поддержке. Был шанс, что она помнит лучше. 

Через полчаса отзвонили и мне: отбой. Прикинули, попиздели, решили – лесом. Путь обходят. Не треснет родимый банк, переебется денечек без segmentation файла.
«Ну ты, Лара, давай завтра с утра погляди, хули оно там ебнулось.»
Ну это я так перевожу. Мое начальство на приличном иврите разговаривает. По смыслу.

Бля-адь! И как я теперь усну? 


Утром, больная вдребезги, вместо того, чтобы тащить такого же больного ребенка к врачу, я приперлась на работу. И сразу поскакала в логи.

«Invalid use of null».

Всего-то. Сообщение из кода. Это принялся рекордсет с NULLами, а проверка не стоит, и NULL значения сразу пытаются обрабатывать как обычные. Почему не стоит? Обычно мы ставим, жестко. Я вообще препочитаю перекрывать наллы на уровне базы, сразу в селекте. Народ же часто ленится, но вилки с «If IsNull()» ставит всегда. Странно. 

Иду в функцию. Функция простейшая, так как к процессу сегментации никакого отношения не имеет: просто получает его результаты и пишет их в файл. Рекордсет вызывается не посредством сложной процедуры, а через простой view. Я выдыхаю. Это счастье.

Проблема в том, что у нас в основной (production) базе банка нет разрешения на исполнение команд. Мы можем только видеть данные, но не менять, и автоматически считается, что в разряд «меняющих» данные относятся все встроенные процедуры и user-defined functions. Хотя на самом деле только меньшая часть их действительно меняют данные базы.

И вот если рекордсет возвращает процедура или view с внутренними функциями, получить простым запуском нужный набор данных уже невозможно.

Программисты, бывает, матюгаются от этого так, что трескается потолок. И бывает, если не удается переписать процедуру на прямой select, приходится копировать данные из production. А это то еще мероприятие. Потому что в базе двести таблиц, не считая истории. И все они так или иначе связаны между собой. От двух до ста полей размером. Самые ходовые - по два миллиона записей. Копирование части – нарушает целостность данных. При копировании же всех, не говоря уж о том, что это адская работа, – пропадают последние изменения… Да и наш development сервер плохо тянет большие объемы данных, у нас таблицы обычно раза в четыре меньше…


Короче говоря, тут мне повезло. Я запустила простой view. Данных было чуть ли не три тысячи записей по пятнадцати полям. Все так не просмотришь, да и ну его нахрен. Что там оно вопило про NULL?

Я посмотрела вниз, в тело функции. Номер клиента, его имя и флажок active (который наше не любящее BIT-тип начальство всегда делает smallint и забивает нулем или минус единицей) стояли без проверки на NULL. Ясно море, они никогда не бывают нулевыми.

Но разве это not null на уровне таблицы?

Неееее. Я структуру таблицы помню. Только номер, который есть primary key.

Ладно, давай, покажи, вдруг где есть имя клиента или active = NULL?

 
…Оба-на, какой пиздюнчик! Выскочила одна запись, где ВСЕ, кроме трех полей, оказалось NULL. В том числе и имя, и флажок. Ну и клиент, мать твою. Проебали, стало быть, сегментацию. Остается понять, как проебали. 
 
К ебеням этот view, покажи давай полную запись по этому клиенту в таблице запросов на сегментацию.
Когда тут у нас она стряслась?
По логу - одиннадцатого. …
Ой, бляяяя… А почему ж сразу два-то??????

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

 
Хуюшки! Не одинаковые. Один запрос – ручной. Второй - автоматический, из файла. Первый более ранний: от десятого числа. Второй от одиннадцатого, как и было сказано. Десятого какой был день? Четверг. Значит, выполнился запрос в утро пятницы, одиннадцатого. Вместе с подоспевшим файлом. В пятницу EndOfDay не бежит. Вот он и навернулся в субботу. Как только, так сразу…

Так. Ну, а как выполнялись запросы в пятницу утром?

Первым пошел ручной. Потом – автоматический. Тээкс. Тээээкс. А подать сюда процедуру сегментации… Сдается мине, что я уже знаю, что там написано…
Ахххххааааааа!!!!! Каааааазлыыыыы!!!!! То есть козы. Так я и знала.
После сегментации старый клиент затирается. Второй такой же запрос ищет его данные на перенос по старому коду. Находит, ебстественннннно, хуй. И этот хуй, то есть, этот расхуяченный на все поля null, шлепает в нового клиента. А ВНЕШНИЕ поля, которые, собственно, и меняет сегментация, и некоторые из которых как раз not null ключи – заполняет пришедшими из запроса данными, и база не орет.
Прэлестно – сказали мы с петром иванычем.
 
О великий Кришна, а подумать об этой примитивнейшей дупликации они не могли???
Впрочем, кому там думать, Рути, что ли? …

В сей секунд ворвалось начальство, и я стала ему и девочке-писальщице-всей-этой-ахинеи объяснять, что стряслось, от яиц. Девочка сразу не въехала, но начальство у меня соображает. Оно поджало рот и кликнуло Рути. На восьмом повторе до старушки стало доходить. Она тихо ойкнула и прикрыла ладошкой заалевшие щечки: «Ах-ах… Какой ужас… Мы об этом не подумали… Мы этого и не проверяли…» 

Ну, и на хуя козе банан? Мне нравится это МЫ. Системная онанистка, мать твою. Рути зарабатывает в час в три раза больше меня. Это так, для справки.

 
И вот пока у них разразилась дискуссия о том, как и что чинить, я сидела молча, очень тихо, и хлопала глазами на данные. Потому что вдруг осознала, что все не так просто. Что я по-прежнему не понимаю, ПОЧЕМУ ОНИ ОДИНАКОВЫЕ в таблице запросов.
Ну, создал он лишнего мастера, в нем лишнего клиента, и забил их наллами. Хоккей. Номер нового мастера дается по нарастающей: каждому новому мастеру на единицу больше. В таблице мастеров он, ебстественно, unique – не может повторяться, это первичный ключ.
Номер нового клиента конструируется специальной процедурой на основе данных самого клиента и других имеющихся в таблице клиентов того же бранча. И он, разумеется, тоже unique – блядь, даже представить страшно, что было бы, если бы нарушалось это правило! Большой медной пиздой накрылась бы не только наша система, но, пожалуй что, и mainframe… 

Мою задумчивость заметило начальство, побелело лицом и спросило, ЧТО ЕЩЕ. Мозги мои жужжали и постреливали, и не было свободных мощностей, чтобы разжевать для дебилов. Я просто показала пальцем в экран и сказала: «Я не понимаю, почему они одинаковые.»


Что тут началось!!! Рути впала в истерику.

«Какой ужас! – кричала она, - он создает одинаковых клиентов!! Как же так??? Как же так???»

- «Наверное, - храбро импровизировала девочка-писальщица, - это потому, что новый клиент-номер строится в таблице запросов, а не клиентов, и следующий такой же конструируется на основе таких же исходных данных!…»

Рути посмотрела на девочку с ужасом и надеждой, и они кинулись проверять свои писания. Мое начальство проводило их взором и шепнуло:

«Лара, это херня. Клиентский номер создается только непосредственно при записи в таблицу клиентов.»

– «А что ж мы видим тогда в запросах?»

- «Не знаю, - неуверенно сказало начальство, - Может быть, это update?»


....UPDATE!!!!! …


Тень какой-то смутной догадки зашевелилась у меня в голове, и я махнула начальству рукой проваливать.

«Лара, - дрожащим голосом спросило начальство, - что делать в данными в production? Мы сможем их сегодня как-то нормализовать? Или предупредить их, чтобы не работали? В каких формах? Что не запускать?»

- «…Через час, - сказала я. – Не надо никого предупреждать.»

- «Ты уверена?»

– «Через час. Или я съем свой тапок.»


Конечно же, херню они спороли. Ни одна из таблиц не могла принять дублирующийся номер. Я пошла в клиентский update log.

Почти одновременно четыре инсерта.

Два разных, отличающихся на единицу, новых мастера.

Два разных новых клиента.

Все нормально. Я пошла в таблицы.

Два разных мастера. Один с данными, но без контрольного номера, приходящего из файла. Этот был внесен из ручной сегментации. Второй – с контрольным номером, но без данных, сплошные nulls – повторный, из файла. Точно та же история в таблице клиентов, только там нет контрольного номера.

Однако записей с первыми номерами мастера и клиента в таблице запросов НЕТ ВООБЩЕ. Не было такой сегментации. Ни один отчет не найдет этого клиента. Зато записей со вторыми – целых две. 

Я уже поняла, в чем дело, и закипала, как большой монастырский самовар. Мало того, …. Так они еще…. Я пошла, полилась водичкой.

Как сказать «мудак женского полу?…» Вот и я не знаю.


Выдохнула, и открыла процедуру. Мать-мать-мать – привычно отозвалось эхо. Внимание всем. Записав нового мастера и нового клиента в таблицы, процедура, как и предположило мое начальство, делает update таблице запросов: заносит в выполненный запрос данные мастеру и клиенту номера.

Нужно выбрать именно ту запись, к которой относится изменение.

Как это делает процедура у наших козочек? Пра-авильно, на основе СТАРОГО НОМЕРА. А таких записей у нас ДВЕ. Вы не забыли? ДВЕ, блядь. Сначала делается сегментация первой. И первая пара номеров ЗАНОСИТСЯ В ОБЕ СТРОКИ. Потом – второй. И снова то же самое. Поверх! Первый вариант исчезает навсегда.


…Прошло полчаса примерно. В паузах между отругиванием от дилеров начальство нарезало круги вокруг моего рабочего места. Я помахала ей рукой, чтобы обнадежить. Потом позвонила вниз коллеге, уточнить, как правильно совместить данные двух мастеров.

Тут прискакала Рути. 

«Лара, - неспешно начала она, - я составила план, как нам исправить ситуацию с дублирующимися номерами клиентов…»

- «Пошла ты…. съешь булочку. Нету никаких дублирующихся номеров.»

Прибежала девочка-писальщица и попросила объяснить. Я обещала, но потом, после того, как подготовлю начальству скрипт: нужно было вернуть данные production в исходное – до дупликации – состояние. Причем помнить, что первой выполнилась ручная сегментация, а правильные внешние данные принесла, наоборот, автоматическая. Попросила девочку помочь открутить назад таблицу счетов.

«Почему только счетов?» - удивилась девочка, - «Там двадцать таблиц еще…»

- «Можешь проверить, - сказала ей я, - Если я все правильно понимаю, в ТЕХ таблицах второго клиента быть не может. Кстати, это хороший тест.»

Девочка проверила. Второго клиента не нашла. 


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

А я ему благоразумно не рассказываю, что вчера три часа на могла отловить свой собственный, примитивнейший баг: ДАТЫ в процедуре сравнивались, КАК СТРОКИ.

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