14 декабря 2010 г.



Перестраиваем фундамент приложения

Буквально на прошлой неделе закончил весьма нетривиальную и рисковую задачу по разработке. Вкратце - для решения проблем целостности данных пришлось вносить изменения в основную модель и, соответственно, преобразовать имеющиеся данные.

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

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

Результат, опять же вкратце, - сделано и работает! Все отлично, и за время разработки и внедрения решения никто не пострадал :-) А теперь добавлю немного подробностей и мыслей, которые успели возникнуть за время работы по этой задаче.


Проектные подробности

Основу всех данных составляет довольно большой и сложный каталог продукции, реализованный по спецификации BMEcat. Сама по себе эта спецификация очень мощная и гибкая и, при первом рассмотрении, не может спровоцировать проблем в разработке. Как оказалось, может. Хотя, в нашем случае это было связано с технологией хранилища данных.

Дело в том, что в объектной модели должно присутствовать довольно много обратных ссылок. Отношения объектов, связанных этими ссылками, можно представить как отношение «шаблон - реализация по этому шаблону». Объект-реализация без обратной ссылки на шаблон, как без рук. Общие для всех объектов данные хранятся в объекте-шаблоне. Именно с сохранностью этих обратных ссылок у нас и возникли проблемы из-за способа их обработки в хранилище.

Технические детали для «технарей»

Сейчас немного черной магии для интересующихся. Остальные могут смело пропускать эту врезку и читать дальше.

Технологии - Java. Постоянное хранилище объектов у нас реализовано с помощью ORM Datanucleus Access Platform (стандарт JDO). Поля объектов, которые нужно сделать персистентными, описываются специальными мета-данными с помощью аннотаций. Обратные ссылки, естественно, тоже описываются для последующего хранения. Но вот какая штука. Когда мы сохраняем объект, сохраняются и все объекты по ссылкам. А объекты по обратным ссылкам нам сохранять не только не хотелось, но и было вредно из-за вопросов синхронизации данных.

Для решения такой проблемы Datanucleus предоставляет свое собственное расширение, т.к. JDO ничего подобного не описывает. Это решение заключается в добавлении специальных мета-данных, которые контролируют процесс добавления (attach) в хранилище. Выставляем значение «never». Мол, ссылки храни, а вот объекты по ссылкам не пересохраняй. Не работает. Разбираемся - оказывается значение «never» все еще не поддерживается. После общения с разработчиками выход один - писать свой патч.

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

Инфраструктурная основа проекта - 4 сервера. Один используется на нашей стороне для  локального редактирования каталога продукции, который за три года стал довольно объемным. Один предназначен для работы с удаленными пользователями, с него же происходит синхронизация и обмен данными c инсталляциями этих пользователей - физически находится у провайдера, с которым работает наша компания. Еще два сервера находятся на стороне заказчика: один - для работы сотрудников центрального офиса заказчика, второй - для обеспечения производства на одной из фабрик заказчика. Все четыре сервера связаны друг с другом и между ними происходит синхронизация данных по определенным правилам.

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

Как это было

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

Так как моя первая идея заключалась в глобальном рефакторинге, то второе, что я сделал - все разломал и начал вносить изменения. Не прошло и двух дней, как я понял свою ошибку. Глобальный рефакторинг затронул почти ВЕСЬ код. А проблему так и не решил. Ведь сколько не придумывай способов для рефакторинга, а обмануть спецификацию BMEcat и устоявшуюся модель данных не получится. Куча времени ушло на колупание в коде, а результат - на помойку.

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

Второе решение било прямо в проблемное место, минимально затрагивая другие узлы системы. Вроде бы все хорошо. Но фиг там - и это решение не подошло из-за ограничений API, текущей структуры проекта и вопросов производительности. Что делать? Я был практически в тупике - любые попытки придумать новое решение в этом же ключе были безрезультатными. А ведь битье головой об стенку не самое мотивирующее занятие. Состояние было - хоть на работу не ходи, чтобы не встречаться лицом к лицу с этой проблемой.

Если вы зашли в полнейший тупик - переключитесь на время на другие задачи. Те, которые можно достаточно быстро и безболезненно решить и получить относительно быстрый результат. Поддержите свою мотивацию, пускай поработает подсознание. Именно это я и сделал - результат не заставил себя долго ждать. Уже на следующий день у меня было третье решение проблемы, и на этот раз удачное и во всем подходящее. Hooorray!

Итак, последнее решение реализовано. На тестирование время жалеть нельзя. Ну что же, мы и не будем жалеть. Оттестируем вдоль и поперек. И этот этап пройден.

Осталось внедрение на продакшн-сервера. Вот тут очень важно выйти из «интравертного» режима работы, в котором пребывают практически все программисты во время работы. Если до этого все усилия (и советы) были сконцентрированы вокруг собственной персоны, то теперь пора подумать об остальных. Конечно же я имею ввиду заказчика и всех ваших миллиард пользователей. Поэтому - при внедрении сконцентрируйтесь на эффективной коммуникации.

Нужно согласовать удобное время для обновления, создать для себя план отступления (точку отката), убедить всех вокруг, что все будет хорошо и вперед. Не забыть договорится с командой, чтобы никто вам не мешал. Не забыть пообщаться со всеми, короче говоря. Ну и, конечно же, держать пальцы крестиком :-)

На этом все. Хоть кому-то перестройка удалась :-P

-



Понравилось сообщение - подпишитесь на блог Подписка на блогFollow grodnosoft on Twitter




Читайте также:


Комментов: 0

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