Your SlideShare is downloading. ×
Aleksey Mashanov Rit
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Saving this for later?

Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime - even offline.

Text the download link to your phone

Standard text messaging rates apply

Aleksey Mashanov Rit

426
views

Published on


0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
426
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
0
Comments
0
Likes
0
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Поэтапный  рефакторинг:  success story Алексей Машанов
  • 2. Цели рефакторинга ● Упрощение добавления новых возможностей за счет возможности реиспользования кода. ● Упрощение сопровождения кода за счет приведения его в человекочитаемый вид, нормализации кода и структуры базы. ● Избавление от велосипедов и перенос тем самым головной боли по их развитию и поддержке на сообщество.
  • 3. Характеристики системы ● Perl + PostgreSQL ● ~ 1200 модулей и 400 скриптов ● ~ 300000 строк чистого кода ● ~ 450 таблиц в БД ● ~ 150 хранимых процедур и 140 триггеров
  • 4. Разбиение на этапы Этап рефакторинга —  коммит должен укладываться  в рамки одного релиза t Релиз (3­4 недели) Шаг рефакторинга —  изменение не сказывающееся  на работоспособности системы Рефакторинг выполняется в основной ветке разработки
  • 5. Test Driven Refactioring Для каждого вносимого в код изменения Написание автотеста Проверка автотеста путем поломки тестируемого кода Внесение модификации (рефакторинг) Проверка модифицированного кода автотестом
  • 6. Структура автотестов Test::Class My::Test rollback после  lib/ t/lib/ каждого теста Class1 Class1::Test Class2 Class2::Sub1 Class2::Test Class2::Sub1::Test Class3 Class2::Sub2 Class3::Test Class2::Sub2::Test
  • 7. I. Замена самописных ORM на DBIx::Class
  • 8. Структура до рефакторинга Ent Ent::Smth11 Entity Entity::Smth11 new() new() Ent::Smth12 Entity::Smth12 list() list() get() … get() … set() set() save() Ent::Smth1N save() Entity::Smth1N ● Два примерно одинаковых ORM ● Методы модификации и поиска объединены в одном классе ● Доступ к полям объекта как к элементам хэша
  • 9. Что хотим получить DBIx::Class::Row Schema::Result::Smth11 DBIx::Class::ResultSet Schema::ResultSet::Smth11 new() Schema::Result::Smth12 search() Schema::ResultSet::Smth12 get_column() … … set_column() Schema::Result::Smth1N Schema::ResultSet::Smth1N insert() update() Schema::Result::Smth21 Schema::ResultSet::Smth21 Schema::Result::Smth22 Schema::ResultSet::Smth22 … … Schema::Result::Smth2N Schema::ResultSet::Smth2N ● Один ORM ● Методы модификации и поиска в разных классах ● Доступ к полям объекта через акцессоры
  • 10. Зачем? ● До рефакторинга ● Два самописных ORM в одной системе это слишком много ● Оба из них не поддерживают отношений между таблицами, тем не менее они нам необходимы, что приводит к обилию в коде plain SQL запросов ● Вновьприбывшие разработчики вынуждены с ними разбираться и вникать в их отличия ● После рефакторинга ● Много новых хороших возможностей, которым мы все очень рады ● Мы не одни во вселенной: почти все что нам может понадобиться уже изобрели, реализовали, отладили и устранили почти все баги, а какие не устранили, устраняют довольно-таки быстро ● Опыт работы с DBIx::Class разработчику пригодится не только для работы над нашей системой, поэтому он с большей вероятностью потрудится разобраться в нем поглубже
  • 11. Создание схемы table table table схема DBIx::Class::Schema::Loader code style conventions simple perl script выстраиваем нужную  иерархию Schema::Result::* Используем статическую схему DBIx::Class::Schema
  • 12. Схема обертки tiehash Ent EntHash Schema::Result::SmthX new() FETCH() get_coumn() set() STORE() set_column() get() EXISTS() has_column_loaded() list() NEXTKEY() columns() save() _DBIC_ insert_or_update() _DBICRS_ Ent::SmthN Ent::Smth2 Schema::ResultSet::SmthX dbic_class() Ent::Smth1 dbic_class() search() dbic_class()
  • 13. Callback методы DBIx::Class::Core Ent::* save() DBIC::EntCallback delete() insert() update() delete() да нет Schema::Result::* caller() eq 'Ent'
  • 14. Неспешная миграция 1. Ent::XXX­>... Schema­>resultset('XXX')­>... 2. SELECT * FROM xxx Schema­>resultset('XXX')­>select() 3. Ent::XXX­>save() Schema::Result::XXX­>insert() Schema::Result::XXX­>update() Ent::XXX­>delete() Schema::Result::XXX­>delete()
  • 15. Завершение рефакторинга ● Удаление иерархии старых ORM
  • 16. Timeline рефакторинга Схема таблиц Schema::* Обертка Ent вокруг DBIx::Class 1 релиз Callback методов Перенос хуков в Избавление Замена Ent::* → Schema::Result::* от plain SQL N релизов Schema­>resultset('*') Удаление старого ORM 1 релиз t
  • 17. II. Единый механизм хранения сущностей
  • 18. Исходная структура Service связан с объектом одного из трех Lbill Client ● классов, а не с одним. ● User, Server, VDS имеют примерно одинаковый набор финансовых полей, но не используют наследование. ● Лишняя связь от User, Server, VDS к Client. User Server VDS ● Сложные запросы к базе со множественными LEFT JOIN. ● Добавление новой сущности приводит к созданию 1 класса, 3 связей и модификации Service. Service
  • 19. Желаемая структура ● Добавление новой сущности Client приводит к добавлению 1 класса и 1 связи. Lbill User ● Финансовые операции ограничены работой с Entity, а не с тремя User, Server, VDS. Entity Server ● При добавлении новой сущности большинство возможностей (кроме Service VDS технических) - «из коробки». ● Нет лишних связей (нормализация).
  • 20. Структура базы vz_vds vz_vds servers clients servers id id users id users id identity_id client_id id id entity_id client_id lbills технические  lbill_id entity_id client_id технические  lbill_id id поля lbill_id финансовые  технические  поля финансовые  client_id поля поля финансовые  поля технические  поля services services entities технические  поля технические  поля user_id entity_id id поля server_id lbill_id vz_vds_id финансовые  поля Было Стало
  • 21. Миграционные триггеры entities AFTER UPDATE Обновление соответствующих финансовых полей в  таблицах users, servers, vz_vds при их изменении BEFORE INSERT vz_vds 1.Проверка, что все синхронизируемые из entities поля IS  servers NULL — это означает, что не выполняется попытка  users установить их значение при INSERT 2.Автоматическое заполнение синхронизируемых полей  данными из соответствующей записи в entities AFTER INSERT OR UPDATE Проверка, что все значения полей соответствуют  значениям всех соответствующих полей в таблице entities
  • 22. Заполнение данными vz_vds id servers entity_id entities 24786 id 1 users entity_id 38798 2 1. id 24786 id 78969 38798 1 entity_id 3 1 24786 12 2 78969 38798 23 3 INSERT 78969 3 UPDATE SET entity_id 2. services id user_id server_id vds_id entity_id 724786 78969 3 338798 2786 26 978969 6783 365 UPDATE SET entity_id
  • 23. Обертка в ORM EntHash Schema::Result::User is_proxied() EntHash::Proxy Schema::Result::Entity client_id EntHash::ProxyAux Schema::Result::Lbill tiehash Ent EntHash::User is_proxied() Ent::User hash_class() Schema::ResultSet::User { prefetch => { entity =>'lbill' } } list() search() client_id lbill.client_id is_proxied($_) entity.$_
  • 24. Неспешная миграция 1. users.$fields servers.$fields entity.$field vz_vds.$field services.user_id services.server_id services.entity_id services.vds_id 2. Ent::XXX­>new() Schema­>resultset('XXX')­>new() 3. SELECT * FROM xxx Schema­>resultset('XXX')­>search()
  • 25. Завершение рефакторинга ● Удаление переехавших в entities полей из таблиц users, servers, vz_vds; полей user_id, server_id, vz_vds_id из таблицы services ● Удаление миграционных триггеров ● Удаление оберточных классов и прочих миграционных подпорок
  • 26. Timeline рефакторинга Создание таблиц Написание триггеров Заполнение данными Обертка ORM 1 релиз Замена plain SQL модификаций users.$field → Замена plain SQL entities.$field запросов N релизов Удаление ненужных полей и подпорок 1 релиз t
  • 27. Вопросы?