google web font

среда, 23 марта 2011 г.

varchar hell

Душераздирающая история про кодировки и размеры полей в БД, с героями, злодеями и поучительной развязкой.

Давным давно, кажется, в прошлую пятницу (на самом деле, уже пару лет прошло), прилетел к нам баг от крупного заказчика, суть такова. В некоторые поля базы данных, имеющие ограничение по длине, не влезает заявленное количество символов. Например, в некоторой таблице есть поле description, описанное как VARCHAR2(255). Так вот, если вписать туда 255 латинских символов, то всё норм, но если вписать "п. 13 ст.33 распоряжения бла-бла-бла", как это любит делать заказчик, то в интерфейсе будет опаньки.


Наши программисты повозились с переменными среды, с параметрами создания таблиц, с локализациями и прочими непонятными субстанциями. Вычислили, что есть такая херь, как NLS_LENGTH_SEMANTICS, которая определяет, что означает число в скобках после слова VARCHAR2 — байты или символы. По умолчанию это байты, а поскольку русские символы в юникоде — многобайтные, заказчик получает ошибки, при этом интерфейс вполне корректно со своей стороны проверяет длину введённой строки: если она больше 255 символов, то он нормально сообщает об этом оператору, но если строка 254 символа, то в интерфейс вываливается оракловый экзепшен.

Ещё выяснили много интересно, типа того, что вторая группа программистов, написавшая демона RADIUS, чтобы не пересчитывать длину из юникода и обратно, устанавливает у себя принудительно кодировку базы в koi8-r, поэтому у него в логах до сих пор кракозябры.

В общем, посмотрели, что да как, разобрались и всё поправили. В демоне поставили нормальную кодировку (чтоб бралась, как положено, откуда надо), а исходники базы тупо sed'ом поправили с VARCHAR2(255) на VARCHAR2(255 chars) — в принципе, всё по уму стало. Но у заказчика база уже создана и изменить в ней поля оказалось то ли невозможно, то ли чрезмерно геморно, в общем, там база уже есть как есть. Зато с новыми исправлениями демон тут же напоролся на юникодовую строку, которая не влезла к нему в буфер, поэтому изменение бодро откатили обратно (недолго мы наслаждались русскими пояснениями в логах).

В общем, исправления исходников базы данных оставили только для разрабатываемой ветки, а исправление демона вообще убрали на всякий случай. Заказчик забил на нечастые ошибки в интерфейсе и у него всё осталось, как было. Зато теперь програмеры в разрабатываемой ветке добавляют новые таблицы и поля в исходники с VARCHAR2(255) (за всеми не уследишь, ленивые, заразы), а те поля, которые уже были в исходниках в тот момент, когда все пофиксили SED'ом, остались в VARCHAR2(255 chars). Заказчику при этом в момент инсталляции БД никто не прописывает NLS_LENGTH_SEMANTICS (а по дефолту там bytes), поэтому новые инсталляции нашего продукта сопровождаются всё той же весёлой шнягой: часть длинных описаний нормально сохраняется, а часть вызывает ошибку.

Заодно теперь имеем ещё и проблему в документации: для всех столбцов типа varchar2 там указан размер и слово BYTES, хотя на самом деле теперь часть столбцов имеет размер в символах, а часть в байтах. То есть документацию по-хорошему тоже надо бы обновить, видимо, тоже sed'ом. Уже несколько лет прошло, а никто этого делать не собирается, потому что это авгиевы конюшни, засранные всего одним коммитом.

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

Первый вывод: половинчатые решения добавляют геморроя всем, даже тем, кто тут вообще не при чём (например, писателю).

Но есть ещё одна проблема, связанная с тем, что решение, казавшееся системным, тоже было кривым.  Нельзя забывать, что програмер — ленивая скотина, которая обязательно забудет написать после 255 слово chars — это же к гадалке не ходи: ни один из наших програмеров этого не сделал ни до, ни после описанных событий. Единственным правильным решением было бы вписать установку настройки NLS_LENGTH_SEMANTICS="CHARS" в скрипты, устанавливающие базу данных, но именно этого не было сделано.

Почему? Я не скажу, что не знаю, я знаю. Потому что "заказчик ждёт решения прямо сейчас". Потому что для того, чтобы придумать правильное решение нужно хотя бы на 15 минут запереться с коллегой в переговорке для мозгового штурма. Потому что когда решаешь проблему, нужно думать не только "как решить", но и "для чего". У нас две группы программистов, и я могу ежедневно наблюдать, как одна группа принимает непродуманные решения и потом тратит время на разгребание вызванных ими проблем, а другая группа пишет практически безбажный код.

Второй оргвывод: думай, прежде чем открывать редактор, а не после того, как закоммитил.

И, наконец, ещё одно замечание, самое смешное. Дело в том, что заказчик-то, возможно, и хочет решения проблемы как можно быстрее, но на самом деле спешку нагнетает менеджер, причём не представитель заказчика, а свой собственный, который с заказчиком работает. Менеджеру надо сохранить лицо фирмы, выбить большой заказ, не упустить клиента и так далее, а заказчик-то в конечном итоге нормально отнёсся к тому, что его проблема вообще осталась нерешённой!

В последнее время я всё чаще замечаю, что сжатые сроки, авралы, непродуманные решения и прочая лажа — это даже не заказчика вина. Заказчик удовлетворится любым ответом, ему главное — чтобы проблему решали. Ну, если действительно срочно — тогда да, возможны эксцессы, но в описанной ситуации оказалось не срочно.

Третий оргвывод: менеджеров надо держать в ежовых рукавицах.

А то будут рассказывать о том, как заказчик с них неустойку стребует, а страдать будут в итоге те самые програмеры, кто ж ещё. Менеджер в любом случае останется в плюсе. 

Комментариев нет:

Отправить комментарий