Арестована банда байкеров, угнавших более 150 автомобилей Jeep Wrangler


Порт Onboard Diagnostics System в автомобиле Jeep Grand Cherokee

Агенты ФБР произвели задержание девяти членов байкерской банды, которая промышляла угоном автомобилей Jeep Wrangler, используя высокотехнологические методы взлома (видео угона). Судя по всему, методы взлома применялись примерно такие же, как у арестованных недавно мексиканцев, которые взломали и угнали более 100 автомобилей Jeep Grand Cherokee (видео).

В современных автомобилях чуть ли не половину стоимости составляет электроника и программное обеспечение. Соответственно, и методы угона для этих «компьютеров на колёсах» применяются технологичные.

Как объяснили представители властей, угоном занималась байкерская банда Dirty 30, подразделение более крупного бандформирования Hooligans Motorcycle Gang. У каждого из девяти членов группы была своя узкая специализация, а все угоны происходили по одинаковой схеме.

Схема следующая. Сначала угонщики узнавали идентификатор конкретного автомобиля — Vehicle Identification Number (VIN). Код передавали члену банды, который занимался изготовлением ключей. У того был доступ к проприетарной базе данных с кодами замены потерянных ключей для Jeep Wrangler. По указанному VIN специалист скачивал из базы два кода. С помощью первого кода он изготавливал физический ключ зажигания с чипом для Jeep Wrangler и вместе со вторым кодом передавал его членам банды, которые осуществляли непосредственно угон.

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

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

Власти сказали, что начали расследование деятельности байкеров, когда задержали троих членов банды в начале 2015 года. По данным следствия, с 2014 года высокотехнологичной банде удалось похитить более 150 джипов общей стоимостью более $4,5 млн.

Буквально две недели назад известные автомобильные хакеры Чарли Миллер (Charlie Miller) и Крис Валасек (Chris Valasek) выложили в открытый доступ свои старые документы — практически пошаговое руководство по взлому Jeep Cherokee, а также инструменты и документацию по взлому других автомобилей с шиной CAN. Эти двое специалистов уже несколько лет выступают с докладами на тему безопасности автомобилей. В 2013 году после демонстрации управления автомобилями 2010 Toyota Prius и 2010 Ford Escape с ноутбука и геймпада Nintendo они представили подробный доклад с описанием техники взлома и опубликовали программный код для эксплойта автомобильного компьютера (ECU) с помощью передачи пакетов по шине Controller Area Network (CAN). Результаты того эксперимента описаны в фундаментальной работе “Adventures in Automotive Networks and Control Units”. В 2015 году Миллер и Валасек наглядно продемонстрировали взлом Jeep Cherokee два года назад с дистанционным управлением некоторыми функциями автомобиля. После той презентации автопроизводителю пришлось отозвать почти 1,5 млн машин по всему миру для замены прошивки.

Один из основных справочников взломщика от Миллера и Валасека — руководство «Взлом автомобилей для бедных». В нём объясняется, как заставить работать ECU снаружи автомобиля и использовать инструменты, описанные в предыдущей работе, для изучения сообщений шины CAN и проведения атаки.

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

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

Ну а владельцам Jeep Wrangler власти рекомендуют поменять систему блокировки капота, чтобы она не открывалась снаружи автомобиля. Тогда хакеры не смогут отключить сигнализацию перед угоном.
ссылка на оригинал статьи https://geektimes.ru/post/289709/

Неравный бой: CRM против Excel

Знаете, как называется худшая в мире CRM-система? MS Excel. Эта шутка ходит среди западных и российских вендоров корпоративного ПО. Excel, легендарный продукт в хорошем смысле этого слова, окружён десятками мифов: Excel знают все, в нём можно сделать все, с ним не нужна CRM-система и т.д. От раза к разу на профессиональных форумах, от будущих клиентов, от компаний мы слышим упорное сопоставление CRM и Excel. Конечно, с точки зрения разработчика это неравный бой, но мы всё же решились на детальное сравнение.
 

Электронные таблицы vs СУБД

Что такое Excel? По своему классу это табличный процессор или, если проще, электронная таблица — прикладная программа для решения вычислительных задач. Электронные таблицы (и тут речь не только об Excel, но и множестве аналогов и конкурентов) были обречены на успех в бизнесе и вообще в любом делопроизводстве, ведь изначально большинство расчётов выполнялось в табличной форме. Буквально ещё в 90-х годах на столах можно было видеть типографские бухгалтерские главные книги, оборотно-сальдовые ведомости, различные табели, сметы, формы отчетности, калькуляции и т.д. Вся эта документация была ничем иным как таблицами.
 
Электронный таблицы в том виде, в каком мы их знаем сегодня, заменили собой довольно большой блок программирования — то, что раньше решалось на ЭВМ только с помощью кода, теперь можно реализовать с помощью различных формул, макросов, функций. Таблицы отражают взаимосвязи, позволяют сохранять и переиспользовать расчёты, строить диаграммы и т.д. Одно из основных преимуществ электронных таблиц в целом и Excel в частности — пересчёт формул «на лету» при любом изменении входных данных. Соответственно, это даёт возможности моделирования, прогнозирования, планирования и анализа. Причём все перечисленные возможности становятся доступными пользователям, далеким от информатики и математики.
 
Дополнительная ценность Excel — возможность создавать пользовательские функции и скрипты на языке Visual Basic for Application. Необходимый код пишется в стороннем редакторе и затем запускается в таблице, обрабатывая входные данные. Фактически таблица становится интерфейсом кода на VBA. Для создания подобных функций требуется навык программирования — рядовой пользователь не справится.
 
Но, как известно, дьявол в мелочах — и настоящий ад может случиться, если переоценить таблицы и начать их использовать как единственное хранилище информации, или, говоря иначе, как базу данных. На первый взгляд кажется, что всё просто отлично: данные можно записывать и перезаписывать, можно искать информацию по нужным условиям, сортировать, делать выборки с помощью фильтров. Однако по сравнению с реляционными СУБД, которые лежат в основе большинства CRM-систем, операции манипулирования данными в том же Excel кажутся незначительными. К тому же, таблицы не столь безопасны, как СУБД.
 
Кстати, об освоении таблиц стоит сказать отдельно. Безусловно, они здорово облегчают жизнь многим менеджерам, бухгалтерам и даже инженерам, но если кто-то вам скажет, что он «гуру Excel», то, скорее всего, человек заблуждается. Освоение Excel на уровне продвинутого пользователя едва ли проще, чем освоение нового языка программирования. Но такая уверенность в рядах компаний существует, отсюда и растут корни целой группы типичных ошибок работы с табличными процессорами.
 

История из жизни. В одной компании завёлся тот самый «гуру Excel». Он занимал руководящий пост, и в силу этой привилегии решил всех своих подчинённых перевести на рельсы автоматизации. Бюджет отдела, планы, рабочие отчёты и KPI стали считаться исключительно в Excel. Пока он собирал, а затем и агрегировал информацию сам, всё было относительно неплохо. Когда с книгами Excel в расшаренных на сервере папках стали работать все, появились необычные данные типа KPI +370% к заработной плате или шестизначного плана продаж. На чём «погорели»:
 

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

 В общем, завели CRM-систему, перенесли остатки «выживших» данных и стали работать спокойно, т.к. программа сама знала, какие формулы считать, какие данные принимать, как сортировать. К тому же, таблицы СУБД сломать и потерять оказалось гораздо сложнее, а с бекапами так вообще невозможно. Такой вот айтишный хэппи-энд.

CRM изнутри

Теперь о CRM-системе, которая представляет собой логику + СУБД + интерфейс. Реляционная база данных, которая используется в CRM, это множество связанных между собой таблиц. Каждая строка (она называется запись) описывает один объект (например, клиента), а каждый столбец содержит атрибуты этого объекта (например, контакты, счета, покупки, скидки и т.д.). Для идентификации записей (строк) используется первичный ключ — набор полей, сочетание которых однозначно определяет запись в таблице.
 
Например, вы пользуетесь CRM. Когда вы выгружаете отчёт о том, какие клиенты совершили покупку в текущем месяце, то внутри CRM происходит примерно следующее: таблица «Клиенты» по уникальному идентификатору (ключу) связывается с таблицей «Покупки», «Оплаты», «Номенклатура» и т.д., в зависимости от того, какую информацию вы хотите получить в итоговом отчёте. При этом СУБД в идеале должна работать максимально шустро, чтобы пользователь даже не заметил процесс. Именно благодаря такой архитектуре CRM умеет быстро и точно строить выборки в любом разрезе, позволяет мгновенно находить связанные с клиентом звонки, сделки, документы. Понятно, что подобную функциональность в Excel реализовать невозможно. Даже связка двух-трёх таблиц уже даёт ощутимые «тормоза» при работе с документами, а в онлайне зачастую просто не позволяет выполнить операцию.
 
Кроме того, реляционные базы данных при правильном подходе к управлению обеспечивают безопасность информации, защиту от несанкционированного доступа, целостность и согласованность данных. Они поддерживают многопользовательский режим работы без коллизий и конфликтов. Все действия логируются и почти всегда можно найти того, чьи шаги привели к ошибке или умышленному вреду.
 
Опять же, разделение прав доступа обеспечивает защиту от новичка, дурака и даже недобросовестных намерений. Так, например, сотруднику (простому менеджеру) могут быть даны права только на запись данных, а редактирование и удаление ему недоступны. Таким образом, максимальный вред от неопытного сотрудника — внесение ошибочной информации, но никак не удаление чужих важных сведений.

Эскизы на салфетках

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

  • Excel и прочие электронные редакторы. Создаются именованные строки и столбцы, вносятся данные, часть информации записывается комментариями к ячейке. Масштаб может быть любой — от банального списка клиентов и их контактов до огромного полотна управления отгрузками и продажами.
  • CRM-системы и иные учётные системы. Наиболее корректный и безопасный способ хранения и обработки данных, надёжный инструмент формирования клиентской базы и отслеживания взаимоотношений с клиентами.
  • MS Word и прочие текстовые редакторы. То же самое, что Excel, только в более олдскульной форме. Иногда внутри документа создаются таблицы. Самый шик — использовать программу блокнот и «расчерчивать» её при помощи пунктира.

    Примерно так — причём удобно двигать размеры (сарказм). Кстати, для создания таких таблиц нужно терпение и рабочее время.

  • Бумажные тетради и блокноты. Рудиментарная форма хранения записей, обусловленная, кстати, не технической отсталостью, а стремлением менеджеров не информировать руководителя о клиентском пуле и иметь только свою личную базу, которую в случае увольнения очень просто «вывести» — достаточно забрать блокнот или тетрадь.
  • Визитницы с визитками. Мы уже рассказывали о Rolodex как о предшественнике CRM-систем, но подобная система работы с клиентами жива до сих пор. Суть простая: собирать визитки с контактной информацией, хранить их и обращаться к ним при необходимости. Продвинутая форма — делать записи и заметки о клиенте на лицевой или обратной стороне карточки.
  • Стикеры. Вам может показаться, что мы шутим, но нет — у части менеджеров контакты и напоминания хранятся именно на стикерах, наклеенных на стол или рамку монитора. Понятно, что это самый ненадёжный способ организации информации.

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

Let’s неравный бой begin

Таблицы ограничивают доступ сотрудников и руководства к информации. И это значительный недостаток. Вы можете сказать, что существуют табличные редакторы с возможностями совместной работы онлайн, однако, во-первых, даже у Microsoft такие версии слабее по возможностям, во-вторых, доступ можно закрыть в любой момент, всего лишь изменив ссылку.
 
CRM-системы позволяют тонко настраивать права доступа на уровне групп и отдельных пользователей. В то же время руководитель может беспрепятственно просматривать профиль работы с каждым клиентом, а также ставить задачи, формировать отчёты и т.д.
 
В таблицах практически невозможно отслеживать полный путь взаимоотношений с клиентами. Нельзя добавить шаги взаимодействия (у менеджеров самый популярный способ — добавлять служебные заметки примечаниями), записи разговоров, историю переписки. Если какие-то вопросы решались посредством почты или переговоров, информация по ним может быть внесена в примечаниях или не внесена вовсе.
 
CRM-система отслеживает путь клиента от холодного лида до допродажи, не упуская ни малейшего взаимодействия. Например, в RegionSoft CRM все звонки с записями разговоров, вся переписка с клиентом и все действия менеджеров сохраняются в карточке клиента и к этой информации можно получить практически мгновенный доступ. По итогам разработки клиента можно сформировать и распечатать отчёт по динамике взаимоотношений, в котором будут отображены все воздействия.
 
В Excel и прочих табличных редакторах нельзя отслеживать работу менеджеров и адекватно реагировать на кризисные ситуации. Матрицы KPI, конечно, существуют в таблицах, однако о гибкой настройке думать не приходится — всё ограничивается несколькими прописанными формулами. В CRM динамика выполнения задач менеджерами доступна линейному и высшему руководству, недочёты и провалы видны сразу, на них можно оперативно реагировать, не дожидаясь конца месяца и заполнения «таблички ключевых коэффициентов».
 

— Эд, согласно моей таблице, ты выполнил ужасную работу.
— Потому что ваши таблицы плохо продуманы и не имеют ничего общего со сложным реальным миром. И да, позвольте напомнить, что формула указывает на неверную ячейку.
— Цифры не врут.

 
В таблицах сложно оценить потенциальные сделки, застойные сделки, стадии работы с клиентом. В основе взаимоотношения с клиентами с точки зрения коммерческого интереса лежит воронка продаж. Её можно отрисовать в Excel, в Интернете описаны десятки шаблонов для этого, однако анализ воронки в различных разрезах (по менеджеру, датам, компаниям, номенклатуре и т.д.) возможен исключительно в CRM-системах. Самая большая сложность работы со сделками в Excel — это необходимость вносить все данные вручную, от стоимости заказа до статуса и этапа разработки клиента. Естественно, что рано или поздно менеджер прекращает эту работу или забывает о ней. В CRM же после правильной первичной настройки статусы и этапы меняются автоматически, а стоимости «подтягиваются» при создании и проведении счёта.
 
Excel даёт довольно слабые возможности отчётности, много времени уходит на обработку данных, построение выборок и анализ. Тут стоит описать реальную ситуацию. Клиент отправлял отчет по реализации за период в головной офис. Объем файла был около 50 000 строк. В выгрузку попадали нерелевантные и ненужные данные. Сортировка и очистка данных у менеджера занимала ровно один рабочий день, и это при наличии специальных формул и макросов. CRM позволяет делать подобные выборки с помощью гибко настраиваемых фильтров с множеством критериев, кроме того пользовательские фильтры можно сохранять и использовать по необходимости (во всяком случае, именно так эта функция реализована у нас в RegionSoft CRM).
 
В CRM можно настроить несколько уровней доступа к данным клиентов, в Excel это сделать невозможно. Ведение базы клиентов в электронных таблицах чревато для бизнеса проблемами с безопасностью: данные могут потеряться как по техническим причинам, так и при увольнении сотрудника. Чего у Excel не отнять, так это его легкой «транспортировки» за пределы бизнеса, в том числе в руки конкурентов.
 
Чтобы забрать данные из десктопной CRM, нужно постараться. Вся оперативная информация о клиентах, контактах, планах, проектах, задачах должна быть в CRM-системе, а не «в голове сотрудника». Эта CRM-система должна быть защищена, т.е. иметь технические возможности, при которых сотрудник не сможет унести из нее сведения (если, конечно, ему не дали прав администратора сервера данных). Например, RegionSoft CRM имеет глубокую проработку безопасности, начиная с интеграции с ActiveDirectory и заканчивая контролем IP-адресов, MAC-адресов, с которых для пользователя разрешено подключаться к системе, подключения к корпоративной базе через alias (когда пользователь не знает реального расположения базы) и др. Совокупность этих мер и дает безопасное окружение для бизнеса. Но всегда есть человеческий фактор, от которого никуда не денешься. Сегодня человек лоялен, а завтра ему бес в ребро — и пошло поехало — твоим клиентам уже продают твои конкуренты. Поэтому должна быть именно совокупность мер, чтобы даже при возникновении желания слить данные, максимально затруднить это технически.
 
Таблицы дают лишь минимальный набор функций для сегментации клиентов и персонализации воздействий. Один из принципов успешного взаимодействия с клиентами — персонализация. Наверное, это даже можно назвать главным фактором выживания в нарастающем информационном шуме. Электронные таблицы позволяют создавать выборки и группировки, но проводить на их основе рассылки и иные коммуникации уже сложно. В CRM-системе вы просто делаете нужную выборку, сохраняете сегмент и запускаете рассылку именно по той группе, которую нужно проинформировать — из интерфейса системы (в RegionSoft CRM рассылку можно запускать с помощью собственного почтового клиента).
 
В Excel постоянно возникают проблемы унификации данных — установленные владельцем документа настройки форматов сбиваются, меняются, игнорируются. В итоге получаются коллизии, чаще всего связанные с денежным  и текстовым форматами, процентами. В CRM-системе разработчик предпринимает всё для максимальной унификации данных: так, при вводе можно выбирать значение из списка (чтобы была только «Москва», а не «масква», «мсква», «мск» и т.д.) или вводить значение, которое проверяется программой при вводе. В случае некорректного ввода CRM-система возвращает сообщение об ошибке.
 
Для Excel не существует адекватных интеграций с другими бизнес-системами и сервисами, например, планированием, бизнес-процессами, телефонией, 1С. Кто-то скажет, что в Excel, может и нет, а вот в таблицах Google Docs давно есть целый магазин приложений, надстроек и аддонов. Не будем спорить, Google предлагает неплохой инструмент, но для частного пользователя или очень маленькой начинающей команды. Во-первых, мало-мальски нагруженный файл подвешивает приложение (бюджет одной из компаний на 16 000 строк с формулами он не осилил). Во-вторых, опять же нет перечисленных интеграций. А причина упирается всё в тот же бэкенд: Spreadsheets всё те же таблицы-калькуляторы.
 
Интеграции CRM сильно отличаются от системы к системе и могут быть реализованы с помощью API, коннекторов, специального ПО, сторонних плагинов. Мы реализовали в своей CRM-системе интеграцию с сайтом, телефонией (в том числе виртуальной АТС Asterisk), Skype, 1С. Часть работы по интеграции ложится на RegionSoft Application Server, что обеспечивает более тонкие настройки и широкие возможности для автоматического обмена данными между CRM-системой и сторонними приложениями.
 
В электронных таблицах невозможно осуществлять грамотное персональное и коллективное планирование. Тут даже комментарии излишни — никакого планировщика, никаких напоминаний, никакой интерактивной работы с задачами от электронных таблиц ждать не стоит. В то время как, например, среди клиентов RegionSoft CRM планировщики и инструменты управления задач едва ли не самые популярные функции системы: ими все пользуются, все ценят возможности напоминаний и интерактивного управления календарём. Среди этих инструментов самый любимый — трёхнедельный планировщик, который совмещает управление задачами, рабочий календарь и в тоже время позволяет работать с горизонтом планирования.
 
В таблицах существуют проблемы с прикреплением файлов. Самая удобная и безопасная реализация этой функции для Excel и других электронных таблиц — прописывание ссылок на нужные документы. В CRM-системе все документы прикрепляются к карточке клиента, легко «поднимаются» и не теряются.
 
Быстрое масштабирование и рост бизнеса не выдержит ни одна таблица: во-первых, книги будут перегружены и вероятность ошибки возрастёт в разы, а во-вторых, добавление новых строк, столбцов, связей может просто повредить бережно накопленный файл. Прогрессивно расти нужно с сохранением производительности, и достичь этого можно только с использованием специальных средств автоматизации. Например, CRM-системы. Для программы любое расширение или сокращение количества пользователей, наименований, добавление подразделений и даже новая структура бизнеса проходят безболезненно.
 
В таблицах не предусмотрено автоматического заполнения информации, например, с сайта или для первичной документации. Хотя, есть библиотека PHPExcel, которая позволяет производить импорт и экспорт данных в Excel из информационных систем, написанных на PHP для любой платформы, но и это не делает Excel полноценной учётной системой или CRM.
 
Процесс обновления информации в таблицах необходимо контролировать, в то время как CRM — единый актуальный источник информации. Вообще, в управлении таблицами, при внесении и обработке данных слишком велик человеческий фактор. Считается, что Excel знают все менеджеры по продажам, бухгалтеры, маркетологи и прочие ребята из коммерческой службы. Это весьма иллюзорное представление: как мы уже говорили, освоение Excel «по-взрослому» требует огромного количества времени.
 
В таблицах не видны глубокие связи между данными, поскольку это не реляционная СУБД, а «плоское» представление данных (как бы один срез). Соответственно, отсутствие реляционных таблиц и ограничения по интерфейсу делают электронные таблицы всего лишь имитацией средства хранения и управления клиентской базой. CRM же представляет собой продуманный интерфейс поверх реляционной СУБД. С помощью интерфейса вносятся данные, управляются процессы, обслуживается телефония и т.д. Вся бизнес-информация надёжно хранится в базе данных, для которой, напомним лишний раз, стоит создавать резервные копии.
 
Знаете, чего от вас ждет клиент на этапе продажи и обслуживания? Он считает, что если обратился к одному специалисту из вашей компании, он обратился ко всем, и любой менеджер готов решить его проблему. И менеджер должен знать о коммуникациях по контакту. Значит, нужно иметь инструмент для мгновенного отображения требуемой информации — и CRM как раз такой инструмент. А вот Excel и другие электронные таблицы изначально не были созданы для управления взаимоотношениями с клиентами, они были созданы для вычислений и первичного анализа информации. Так может, не стоит ждать жалоб клиентов и накапливать риски, которые рано или поздно могут привести к коллапсу. Причём, по закону подлости, в самый неподходящий момент.
ссылка на оригинал статьи https://habrahabr.ru/post/329986/

Перевод статьи: Лучшая практика создания Git Commit’ов от OpenStack

Предлагаю читателям "Хабрахабра" перевод статьи "Хорошая практика в сообщении коммитов от OpenStack".

1 Git Commit Лучшая практика

Следующий документ основан на опыте разработки кода, устранении ошибок и просмотре кода в ряде проектов, использующих Git, включая libvirt, QEMU и OpenStack Nova. Рассмотрение других проектов с открытым исходным кодом, таких как Kernel, CoreUtils, GNULIB а также других, предполагает, что все они следуют достаточно распространенной практике. Это мотивировано желанием улучшить качество истории Git проекта Nova. Качество — это абстрактный термин для определения в разработке; когда для одного человека некий код «Красивый» (Thing of Beauty) — то для другого это «Костыль» (Evil Hack). Тем не менее мы можем сформулировать некоторые общие рекомендации о том, как и что делать, или, наоборот, чего не делать, когда отправляют Git коммиты для слияния с проектами в OpenStack.

Эта тема может быть разделена на две области:

  1. Порядок объединения или разбиения на несколько коммитов
  2. Информация в сообщениях коммитов

Содержание

1.1 Краткий обзор

Мысли и примеры, описанные в этом документе должны ясно продемонстрировать важность разбиения изменений в группу последовательных коммитов, а также важность написания хороших сообщений к ним. Если эти инструкции будут широко применяться, это значительно улучшит качество истории git’а OpenStack. Кнут и пряник необходимы для эффективного изменения истории git’a. Этот документ должен быть пряником, обращая внимание людей на выгоду и пользу, пока для других использование системы коллективного инспектирования кода Gerrit будет действовать как кнут. ;-P

Другими словами, при инспектировании в Gerrit’е, не просто нужно смотреть на корректность кода. При инспектировании сначала нужно изучить сообщение коммита и указать на улучшения самого сообщения. Приглядитесь к коммитам, которые объединяют множество логических изменений и потребуйте у отправителя разбить их на раздельные коммиты. Убедитесь что изменения с форматированием отступов не объединяются с функциональными изменениями. Также убедитесь, что рефакторинг кода зафиксирован отдельно от функциональных изменений, и так далее.

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

А теперь подробные принципы, а также примеры хорошей и плохой практики.

1.2 Структурное разделение изменений

Основным правилом для создания хороших коммитов является предоставление только одного «логического изменения» для каждого коммита. Есть множество причин, по которым это важное правило:

  • Чем меньше изменяется код, тем быстрее и проще его проверять и выявлять потенциальные недостатки.
  • Если будет обнаружено, что коммит некорректен или битый, может потребоваться откат плохого коммита. И это будет намного проще сделать, если нет других несвязанных изменений в коде, которые смешиваются с исходным коммитом.
  • При устранении проблем, например при использовании команды git bisect, небольшие, хорошо сформированные коммиты помогут точно определить, где и когда была внесена проблема в коде.
  • При просмотре истории git’а с помощи git annotate или git blame небольшие хорошо сформированные коммиты помогают найти и изолировать проблемную часть кода, а также понять откуда появился проблемный фрагмент.

1.2.1 Чего нужно избегать при создании коммитов

Необходимо знать и понимать часто встречающиеся примеры плохой практики, чтобы их избегать:

  • Смешение форматирования отступов и пробелов с функциональными изменениями кода.

Изменения форматирования будут скрывать важные функциональные изменения, что затруднит для рецензента точное определение правильности коммита. Решение: Создайте 2 коммита, один с изменениями форматирования, другой с функциональными изменениями. Обычно изменение отступов делают первым, но это не является строгим правилом.

  • Смешивание двух несвязанных функциональных изменений.

Опять же рецензенту будет труднее выявить недостатки, если смешать два несвязанных изменения. Если возникнет необходимость в более позднем откате сломанного коммита, необходимо будет разобраться и распутать два несвязанных изменения с дополнительным риском создания новых ошибок.

  • Отправка больших новых функций в одном гигантском коммите.

Вполне возможно, что код для новой функции полезен только тогда, когда присутствует вся реализация. Однако, это не означает, что вся функция должна предоставляться в одном коммите. Новые функции часто влекут за собой рефакторинг существующего кода. Весьма желательно, чтобы любой рефакторинг выполнялся в отдельных коммитах, а не в тех, в которых реализуются новая функция. Это поможет рецензентам и наборам тестов подтвердить, что рефакторинг не имеет непреднамеренных функциональных изменений. Даже недавно написанный код часто можно разделить на несколько частей, которые могут быть независимо рассмотрены. Например, изменения, которые добавляют новые внутренние API или классы, могут быть в отдельных коммитах. Опять же, это приводит к упрощению проверки кода. Это также позволяет другим разработчикам получать маленькие куски работы при помощи git cherry-pick, если новая функция не совсем готова к слиянию. Добавление новых публичных HTTP API или RPC-интерфейсов должно быть выполнено в коммитах, отдельных от фактической внутренней реализации. Это побудит автора и рецензентов подумать об общем дизайне API или RPC, а не просто выбрать дизайн, который проще для выбранной в настоящий момент внутренней реализации. Если патч влияет на публичный HTTP, используйте флаг APIImpact (см. Включение внешних ссылок).

Главное следовать правилу:

Если изменение кода можно разбить на последовательность патчей или коммитов, то оно должно быть разделено. Меньше - НЕ больше. Больше это больше.

1.2.2 Примеры плохой практики

Теперь немного проиллюстрируем примеры из истории Git Nova. Примечание: хоть хэши коммитов цитируются для ссылки, имена авторов удалены, поскольку мы не должны обвинить или оскорбить ни одного человека. Время от времени каждый из нас виноват в нарушении правил хорошего тона. Кроме того, люди, которые рецензировали и одобряли эти коммиты, так же виновны, как и тот, кто их написал и отправил;

1.2.2.1 Пример 1

Коммит: ae878fc8b9761d099a4145617e4a48cbeb390623 Автор: [удалено] Дата: Пт. 1 Июня 01:44:02 2012 г. +0000  Рефакторинг вызовов метода create libvirt   * Сводит к минимуму дублирование кода для create  * Заставляет срабатывать wait_for_destroy при выключении    вместо undefine  * Позволяет уничтожить экземпляр при выходе из домена  * Использует reset для жесткой перезагрузки вместо create / destroy  * Заставляет resume_host_state использовать новые методы    вместо hard_reboot  * Заставляет rescue/unrescue не использовать жесткую перезагрузку    для пересоздания домена  Change-Id: I2072f93ad6c889d534b04009671147af653048e7

В этом коммите выполнено как минимум два независимых изменения.

  1. Переход на использование нового reset API в методе «hard_reboot»
  2. Корректировка методов внутреннего драйвера, чтобы не использовать "hard_reboot"

В чем же тут проблема:

  • Во-первых, нет никаких веских причин, по которым эти изменения необходимо было вносить одновременно (в одном коммите). Первый коммит мог включать изменения для прекращения вызова в разных местах метода «hard_reboot». Второй коммит мог бы переписать реализацию «hard_reboot».
  • Во-вторых, переход на использование метода reset libvirt был запрятан в большом рефакторинге кода, и рецензенты это пропустили, а ведь это вводило зависимость от более новой версии API libvirt. Этот коммит был найден как проблемный достаточно быстро, но тривиальный откат невозможен из-за большого числа несвязанных изменений.

1.2.2.2 Пример 2

Коммит: e0540dfed1c1276106105aea8d5765356961ef3d Автор: [удалено] Дата: Cр. 16 Мая 15:17:53 2012 +0400  Документ lvm-disk-images  Добавлена возможность использования томов LVM для дисков VM.  Реализует поддержку LVM дисков для драйвера libvirt.  VM-диски будут храниться на томах LVM в группе томов  указанных параметром `libvirt_images_volume_group`.  Другой параметр `libvirt_local_images_type` указывает, какой тип  хранилища будет использоваться. Поддерживаются значения: `raw`,  `lvm`, `qcow2`, `default`. Если `libvirt_local_images_type` =  `default`, будет использоваться с флагом `use_cow_images`. Логический параметр `libvirt_sparse_logical_volumes` управляет тем,  какого типа логические тома будут создаваться (распределенные с  помощью virtualsize или обычные логические тома с полным  выделением пространства). Значение по умолчанию для этой  опции `False`. Коммит вводит три новых класса: `Raw`, `Qcow2` и `Lvm`. В них  содержатся логика создания образа, которая была сохранена в  методах `LibvirtConnection._cache_image` и` libvirt_info`  благодаря чему создаются правильные конфигурации `LibvirtGuestConfigDisk`  для libvirt. Класс `Backend` выбирает, какой тип образа будет  использоваться.  Change-id: I0d01cb7d2fd67de2565b8d45d34f7846ad4112c2

Это изменение вводит одну новую крупную функцию, поэтому на первый взгляд кажется разумным использовать одиночный коммит, но, глядя на патч, он явно сильно сбивает с толку, так как содержит существенный объем рефакторинга кода с новой функцией LVM. Этот факт затрудняет определение вероятных регрессий при поддержке образов QCow2 / Raw. Его следовало бы разделить на меньшие по размеру, как минимум на четыре раздельных коммита.

  • Заменен флаг конфигурации «use_cow_images» новым флагом «libvirt_local_images_type» с поддержкой обратной совместимости для устаревшего флага «use_cow_images».
  • Создание внутреннего класса «Image» и реализация подклассов Raw & QCow2.
  • Рефакторинг libvirt драйвера для замены кода управления образами raw / qcow2, при помощи вызова нового класса «Image».
  • Внедрение новой реализации класса образа «LVM».

1.2.3 Примеры хорошей практики

1.2.3.1 Пример 1

Коммит: 3114a97ba188895daff4a3d337b2c73855d4632d Автор: [удалено] Дата: Пн. 11 Июня 17:16:10 2012 +0100  Обновление политик по умолчанию для KVM гостевых VM таймеров PIT и RTC  Коммит: 573ada525b8a7384398a8d7d5f094f343555df56 Автор: [удалено] Дата: Вт. 1 Мая 17:09:32 2012 + 0100  Добавлена поддержка настройки часов и таймеров VM libvirt

Вместе эти два изменения обеспечивают поддержку настройки таймеров гостевых KVM. Внедрение новых API для создания XML-конфигурации libvirt было четко отделено от изменения политики создания гостевых систем KVM, в которой используются новые API.

1.2.3.2 Пример 2

Коммит: 62bea64940cf629829e2945255cc34903f310115 Автор: [удалено] Дата: Пт. 1 Июня 14:49:42 2012 -0400  Добавлен комментарий к методу rpc.queue_get_for(). Change-Id: Ifa7d648e9b33ad2416236dc6966527c257baaf88  Коммит: cf2b87347cd801112f89552a78efabb92a63bac6 Автор: [удалено] Дата: Ср. 30 Мая 14:57:03 2012 -0400  Добавлены методы shared_storage_test для вычисления rpcapi. ...пропуск... Добавлен get_instance_disk_info в метод вычисления rpcapi. ...пропуск... Добавлен remove_volume_connection в метод вычисления rpcapi. ...пропуск... Добавлен compare_cpu в метод вычисления rpcapi. ...пропуск... Добавлен get_console_topic() в метод вычисления rpcapi. ...пропуск... Добавлен refresh_provider_fw_rules() в метод вычисления rpcapi. ... много других коммитов ...

Эта последовательность коммитов реорганизовала весь слой API RPC внутри OpenStack Compute (Nova), чтобы можно было использовать реализацию подключаемых систем сообщений. Для такого существенного изменения основной части функциональности, было ключевым моментом разделение работы на большую последовательность коммитов, позволяющим сделать осмысленную проверку кода, а также упростило отслеживание изменений и поиск возможных регрессий на каждом этапе процесса.

1.3 Информация в сообщениях коммитов

Текст сообщения коммита является таким же важным, как и само изменения кода. При создании сообщения есть некоторые важные вещи, которые нужно помнить:

  • Не предполагайте, что рецензент понимает, в чем изначально была проблема.

Часто не всегда бывает ясно в чем была проблема, даже если были прочитаны отчеты об ошибках (bug report), а также пару комментариев. Сообщение о коммите должно содержать четкое изложение исходной проблемы. Конкретный баг — это только лишь интересная историческая справка о том, как проблема была выявлена. Рецензенту должно быть понятно что же делает предложенный патч для правильности принятия решения, не изучая этот дефект в системе отслеживания ошибок.

  • Не предполагайте, что у рецензента есть доступ к внешним веб-сервисам или сайтам.

Через 6 месяцев, когда кто-то в поезде / самолете / автобусе / пляже / баре будет устранять проблему и просматривать историю git’a, нет гарантий, что у него будет доступ к онлайн-отчету об ошибках или к серверу с документацией. Распределенные системы управления исходным кодом (SCM) сделали большой шаг вперед в том, что вам больше не нужно быть «онлайн», чтобы иметь доступ ко всей информации в репозитории. Сообщение коммита должно быть полностью самодостаточным, чтобы продолжать извлекать пользу из git’a.

  • Не предполагайте, что код самоочевиден / самодокументирован.

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

  • Опишите, почему изменения были сделаны.

Частая ошибка заключается в том, чтобы просто описать, как код был написан, без описания почему разработчик решил сделать это именно таким образом. Самое главное объяснить намерение и мотивацию изменений, но так же не забудьте описать общую структуру кода.

  • Прочтите сообщение о коммите, чтобы понять, указывает ли оно на улучшение структуры кода.

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

  • Обеспечьте достаточной информацией для принятия правильного решения что именно проверять.

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

  • Первая строка коммита является наиболее важной.

В Git первая строка сообщения коммита имеет особое значение. Она используется в качестве темы электронного письма, в сообщениях команды git annotate, в программе gitk viewer, при слиянии ветки и многих других местах, где места для текста не так уж и много. Помимо краткого описания самого изменения, следует не забывать, какая часть кода затронута. Например, если это влияет на драйвер libvirt, укажите «libvirt» где-нибудь в первой строке.

  • Опишите любые ограничения текущего кода.

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

  • Не включайте комментарии, которые относятся исключительно к этому патчу.

Другими словами, если вы переделаете свой коммит, пожалуйста, не добавляйте «Патч №2: переделан» (Patch set 2: rebased) к вашему сообщению. Это не будет иметь никакого значения после слияния изменений. Однако напишите заметку в Gerrit как комментарий к вашему изменению. Это поможет рецензентам узнать, что изменилось между наборами патчей. Это также относится к комментариям, таким как «Добавлены юнит-тесты», «Исправленны проблемы локализации» или любые другие патчи, которые не влияют на общую цель вашего коммита.

Главное следовать правилу:

Сообщение коммита должно содержать всю информацию, которая необходима для полного понимания и проверки патча на корректность. Меньше - НЕ больше. Больше это больше.

1.3.1 Включение внешних ссылок

Хоть сообщение в основном и предназначено для интерпретации человеком, в нем всегда присутствуют метаданные, предусмотренные для машинной обработки. В OpenStack’а в них включают «Change-Id» (Идентификатор изменения), а также необязательные ссылки на идентификаторы «bug», ссылки на «blueprint» (схемы/документы), флаг DocImpact, флаг APIImpact и флаг SecurityImpact.

  • Строка «Change-Id» представляет собой уникальный хеш, описывающий изменение, которое генерируется перехватчиком Git’а (hook). Она не должна меняться после инспектирования при редактировании коммита командой rebase, так же оно используется системой Gerrit для отслеживания версий патча.
  • Строка ‘bug’ может ссылаться на ошибку несколькими способами. Gerrit создает ссылку на ошибку при просмотре патча на сайте review.openstack.org, чтобы рецензенты могли быстро получить доступ к ошибке в Launchpad.
    • Closes-Bug: #1234567 — используйте «Closes-Bug», если коммит предназначен для полного устранения и закрытия ошибки на которую ссылаетесь.
    • Partial-Bug: #1234567 — используйте ‘Partial-Bug’, если коммит — это только частичное исправление и требуется еще работа для устранения этого бага.
    • Related-Bug: #1234567 — используйте ‘Related-Bug’, если коммит просто связан с указанной ошибкой.
  • В строке ‘blueprint’ должно быть указано имя Launchpad blueprint (документация на сайте Launchpad), если коммит предназначен для реализации некой функции. Gerrit создает ссылку на blueprint при просмотре патча на сайте review.openstack.org, чтобы рецензенты могли быстро получить доступ к документу на Launchpad.
  • Строка DocImpact содержит строку DocImpact и комментарий о том, почему это изменение влияет на документацию. Поместите DocImpact в отдельную строку. Используйте этот флаг, чтобы указать, что документация содержится в патче, или изменение затрагивает документацию, чтобы рецензенту можно было понять о причинах изменений. Включите как можно больше информации. Когда этот флаг включен в сообщение о фиксации, Gerrit создает bug-report для проекта openstack-manuals (инструкции openstack) для отслеживания и очередности выполнения, или перемещает если необходимо, в openstack-api-site.
  • Строка APIImpact содержит строку APIImpact и комментарий о том, почему это изменение влияет на общедоступный HTTP API. Поместите APIImpact на отдельную строку. Используйте этот флаг, чтобы указать, что патч создает, обновляет или удаляет общедоступный HTTP API или изменяет поведение общедоступного API. Включите как можно больше информации. Когда этот флаг включен в сообщение фиксации, все связанные отзывы можно найти с помощью этой ссылки, также страница из документации API_Working_Group может помочь в поиске релевантных отзывов. Кроме того, с рабочей группой API (API Working Group) можно напрямую связаться по IRC в канале Freenode #openstack-sdks.
  • Строка SecurityImpact просто содержит строку SecurityImpact. Она используется, чтобы указать, что изменение имеет последствия для безопасности и должно быть пересмотрено группой OpenStack Security Group.
  • Строка UpgradeImpact содержит строку UpgradeImpact и комментарий о том, почему это изменение влияет на обновления. Она используется, чтобы указать, что изменение имеет последствия обновлений для тех, кто делает непрерывное развертывание (continuous deployment), или от N до N + 1 апдейтов. Также возможно указывает на обновления раздела «Upgrade Notes» (Примечания по обновлению) в примечаниях к выпуску для затронутого проекта.

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

Примечание: Хотя во многих проектах с открытым исходным кодом, использующих Git является популярной практикой включать тэг «Signed-off-by» (Подписан) (генерируемого при помощи команды «git commit -s»), для OpenStack это не обязательно. Прежде чем получить возможность отправить код в Gerrit, OpenStack требует, чтобы все участники подписали CLA (Contributor License Agreement, Лицензионное соглашение для участников), что служит для эквивалентной цели.

Мы поощряем использование Co-Authored-By: name name@example.com (Соавторство) в сообщениях коммита, чтобы указать людей, которые работали над определенным патчем. Это является соглашением о признании нескольких авторов, и наши проекты призывают инструменты сбора статистики следить за этими полями при их анализе.

1.3.2 Краткий обзор структуры сообщений коммитов

  • В первой строке представьте небольшое описание изменений.
  • Вставьте пустую строку после первой строки.
  • В следующих строках напишите подробное описание изменений, разбив на пункты там, где это необходимо.
  • Первая строка должна быть ограничена 50 символами и не должна заканчиваться точкой.
  • Последующие строки должны быть заключены в 72 символа.
    • vim (по умолчанию в переменной среды $EDITOR на большинстве дистрибутивов) может автоматически обернуть строки для вас. В большинстве случаев вам просто нужно скопировать файл примера vimrc (который можно найти где-нибудь, например /usr/share/vim/vim74/vimrc_example.vim) в ~/.vimrc, если у вас его нет.
    • После редактирования абзаца его можно обернуть, нажав клавишу escape, поместив курсор внутри абзаца и введя комбинацию gqip.
  • Поместите строки «Change-id», «Closes-Bug #NNNNN» и «blueprint NNNNNNNNNNNN» в самом конце если это необходимо.

Например:

Переключение метода libvirt get_cpu_info на использование конфигурации API  Метод get_cpu_info в драйвере libvirt в настоящее время использует запросы XPath для извлечения информации из возможностей XML документа. Переключение на использование нового класса конфигурации LibvirtConfigCaps. Также добавили тестовый случай для проверки возвращаемых данных.  DocImpact Closes-Bug: #1003373 Implements: blueprint libvirt-xml-cpu-model Change-Id: I4946a16d27f712ae2adf8441ce78e6c0bb0bb657

1.3.3 Некоторые примеры плохой практики

Посмотрим немного примеров из истории Git Nova, опять же с удаленными именами авторов, так как мы не хотим никого обвинить или оскорбить.

1.3.3.1 Пример 1

Коммит: 468e64d019f51d364afb30b0eed2ad09483e0b98 Автор: [удалено] Дата: Пн. 18 Июня 16:07:37 2012 -0400  Исправление отсутствующего импорта в compute/utils.py  Fixes bug #1014829

Проблема: тут не упоминается, что должно было импортироваться, где этого импорта не хватает, и почему он так необходим. Эта информация была включена в средство отслеживания ошибок и должна была быть скопирована в сообщение коммита, чтобы обеспечить самодостаточное описание. Например:

Добавить отсутствующий импорт "exception" в compute/utils.py  nova/compute/utils.py делает ссылку на класс исключения exception.NotFound, однако исключение не было импортировано.

1.3.3.2 Пример 2

Коммит: 2020fba6731634319a0d541168fbf45138825357 Автор: [удалено] Дата: Пт. 15 Июня 11:12:45 2012 -0600  Предоставляет правильный формат ec2id для томов и снимков  Fixes bug #1013765 * Добавлен аргумент шаблона в вызовы ec2utils.id_to_ec2_id()  Change-Id: I5e574f8e60d091ef8862ad814e2c8ab993daa366

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

Представляет правильный формат ec2id для томов и снимков  Во время миграции uuid'а тома, выполненной набором изменений XXXXXXX, форматы идентификаторов ec2 для томов и моментальных снимков были удалены и теперь используют по умолчанию формат экземпляра (i-xxxxx). Идентификаторы необходимо вернуть обратно к vol-xxx и snap-xxxx.  Добавляет аргумент шаблона для вызовов ec2utils.id_to_ec2_id ()  Fixes bug #1013765

1.3.3.3 Пример 3

Коммит: f28731c1941e57b776b519783b0337e52e1484ab Автор: [удалено] Дата: Ср. 13 Июня 10:11:04 2012 -0400  Добавлена проверка минимальной версии libvirt.  Fixes LP bug #1012689  Change-Id: I91c0b7c41804b2b25026cbe672b9210c305dc29b

Проблема: в этом коммите сообщение просто документирует, что было сделано, а не почему это было сделано. Нужно было упомянуть, что ранее набор изменений ввел новую минимальную версию libvirt. Также нужно было сообщить, какое текущее поведение, если проверка не проходит.
Например:

Добавлена проверка версии libvirt, минимум 0.9.7  В коммите XXXXXXXX введено использование API сброса 'reset' которое доступно только в libvirt 0.9.7 или новее. Добавлена проверка версии перед libvirt версии подключения, выполняющаяся при запуске вычислительного сервера. Если проверка версии завершилась неудачей, служба будет завершаться.  Fixes LP bug #1012689  Change-Id: I91c0b7c41804b2b25026cbe672b9210c305dc29b

1.3.4 Примеры хорошей практики

1.3.4.1 Пример 1

Коммит: 3114a97ba188895daff4a3d337b2c73855d4632d Автор: [удалено] Дата: Пн. 17 Июня 17:16:10 2012 +0100  Обновление политик по умолчанию для KVM гостевых VM таймеров PIT и RTC  Политики по умолчанию для таймеров PIT и RTC для гостевой системы KVM плохо поддерживают надежность времени (часов) в гостевых операционных системах. В частности, гостевые Windows 7 часто приводят к сбою политик таймера KVM по умолчанию, а старые гостевые Linux системы будет иметь сильное смещение времени.  Устанавливает PIT так, чтобы пропущенные тики были введены с нормальной скоростью, т.е. они задержались  Устанавливает RTC таким образом, чтобы пропущенные тики вводились с более высокой скоростью, для того чтобы 'догонять'  Это соответствует следующему libvirt XML  <clock offset='utc'>   <timer name='pit' tickpolicy='delay'/>   <timer name='rtc' tickpolicy='catchup'/> </clock>  И следующие KVM параметры запуска    -no-kvm-pit-reinjection   -rtc base=utc,driftfix=slew  Это должно обеспечить стандартную конфигурацию, которая работает приемлемо для большинства типов ОС. Скорее всего в будущем необходимо сделать настраиваемую опцию для каждого типа гостевой ОС  Closes-Bug: #1011848  Change-Id: Iafb0e2192b5f3c05b6395ffdfa14f86a98ce3d1f

Некоторые вещи, которые следует отметить в этом примере:

  • Описано, что из себя представляет исходная проблема (плохие настройки KVM по умолчанию)
  • Описываются функциональные изменения (новые политики PIT / RTC)
  • Описывается результат изменения (новый XML / QEMU args)
  • Также описывается область для будущего улучшения (возможная конфигурация типа каждой ОС)
  • Используется запись Closes-Bug

1.3.4.2 Пример 2

Коммит: 31336b35b4604f70150d0073d77dbf63b9bf7598 Автор: [удалено] Дата: Ср. 06 Июня 22:45:25 2012 -0400  Добавлена поддержка фильтрации планировщика архитектуры CPU  В смешанной среде, где работают с различными архитектурами процессора, не хотелось бы запускать экземпляр ARM архитектуры на хосте X86_64 и наоборот.  Новый параметр фильтра планировщика предотвратит запуск экземпляров на хосте, для которого он не предназначен.  Драйвер libvirt запрашивает гостевые возможности хоста и сохраняет архитектуры гостевых ОС в списке permitted_instances_types в словаре cpu_info хоста.  Для Xen позже будет сделан эквивалентный коммит.  Фильтр архитектуры будет сравнивать архитектуру экземпляра с списком permitted_instances_types хоста и отфильтровывать недействительные хосты.  Также добавляет ARM как действительную архитектуру к фильтру.  По умолчанию ArchFilter не включен.  Change-Id: I17bd103f00c25d6006a421252c9c8dcfd2d2c49b

Некоторые вещи, которые следует отметить в этом примере:

  • В нем описывается, проблемный сценарий (развертывание смешанных архитектур)
  • Описываются намерения изменений (сделать фильтр планировщика по архитектуре)
  • Коммит описывает грубый алгоритм исправления (как libvirt возвращает архитектуру)
  • Также отмечает ограничения данного исправления (работы, необходимые для Xen)

ссылка на оригинал статьи https://habrahabr.ru/post/329992/

Лекции Технопарка. Базы данных (весна 2017)

Всем жаждущим знаний предлагаем ознакомиться с новыми лекциями Технопарка, посвящённым базам данных. Курс ведёт Артём Навроцкий, ведущий программист в Allods Team.

Список лекций:

  1. Введение
  2. Модификация и выборка данных
  3. Выборка данных (продолжение)
  4. Транзакции. Триггеры и хранимые процедуры
  5. Индексы и производительность
  6. Оптимизация запросов. Оптимизация структуры данных
  7. Репликация, полнотекстовый поиск, JSON
  8. Сохранность данных

Цель курса — это дать знания по:

  • Концепции и принципам реляционной модели данных.
  • Методам проектирования баз данных.
  • Подходам к профилированию производительности.
  • Основам архитектуры современных СУБД.
  • Основам и границам применимости реляционной модели.
  • Основным типам нереляционных баз данных, а также области их применимости при построении хранилищ данных.

Привить навыки:

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

Лекция 1. Введение

Из первой лекции вы узнаете, зачем нужны СУБД, какова краткая история развития баз данных, что такое реляционные БД и NoSQL. Познакомитесь с реляционной моделью данных и основными операциями в рамках БД. Также в ходе лекции обсуждаются первичный и суррогатный ключи, рассказывается о типах данных в PostgreSQL. Вы познакомитесь с примерами схем баз данных, версионированием схем. Обсуждается задача генерации БД на основе исходного кода, методы инкрементных и идемпотентных изменений.

Лекция 2. Модификация и выборка данных

Первая часть лекции посвящена введению в проектирование баз данных. Вторая часть посвящена основам SQL: основные команды, создание таблиц, выборка данных. Затрагивается тема JOIN’ов, проводится обзор INFORMATION_SCHEMA. И в завершении обсуждается, как можно хранить иерархические структуры в базах данных.

Лекция 3. Выборка данных (продолжение)

Начало третьей лекции посвящено COLLATION и регистронезависимому поиску. Затем рассказывается о задаче выборки данных (SELECT): формирование подзапросов, оконные функции, UNION, снова поднимается тема JOIN’ов, обсуждаются рекурсивные запросы. И в завершение вы узнаете про операцию VIEW.

Лекция 4. Транзакции. Триггеры и хранимые процедуры

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

Лекция 5. Индексы и производительность

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

Лекция 6. Оптимизация запросов. Оптимизация структуры данных

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

Лекция 7. Репликация, полнотекстовый поиск, JSON

Эта лекция состоит из четырёх частей. Первая посвящена репликации: рассказывается о физической, логической, синхронной и асинхронной репликациях, о балансировке и отказоустойчивости, а также о проблемах репликации. Затем обсуждается проблематика полнотекстового поиска. Далее затрагивается вопрос хранения в БД географических данных. И в завершение рассказывается о хранении слабоструктурированных данных (JSON).

Лекция 8. Сохранность данных

Последняя лекция посвящена различным аспектам обеспечения сохранности информации в базах данных. Сначала рассказывается о резервном копировании, о его отличии от репликации, о создании логических и физических резервных копий. Затем вы узнаете о том, как нужно конфигурировать базы данных: каковы общие принципы настройки, как настраивать память и ввод/вывод, что такое табличные пространства, как собирать информацию о текущем состоянии сервера. И завершающая часть лекции посвящена обеспечению безопасности: политикам прав доступа и защите от SQL-инъекций.


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

Другие курсы Технопарка на Хабре:

Информацию обо всех наших образовательных проектах вы можете найти в недавней статье.

ссылка на оригинал статьи https://habrahabr.ru/post/329928/

Новая архитектура Android-приложений — пробуем на практике

Всем привет. На прошедшем Google I/O нам наконец представили официальное видение компании Google на архитектуру Android-приложений, а также библиотеки для его реализации. Не прошло и десяти лет. Конечно мне сразу захотелось попробовать, что же там предлагается.

Осторожно: библиотеки находятся в альфа-версии, следовательно мы можем ожидать ломающих совместимость изменений.

Lifecycle

Основная идея новой архитектуры — максимальный вынос логики из активити и фрагментов. Компания утверждает, что мы должны считать эти компоненты принадлежащими системе и не относящимися к зоне ответственности разработчика. Идея сама по себе не нова, MVP/MVVP уже активно применяются в настоящее время. Однако взаимоувязка с жизненными циклами компонентов всегда оставалась на совести разработчиков.

Теперь это не так. Нам представлен новый пакет android.arch.lifecycle, в котором находятся классы Lifecycle, LifecycleActivity и LifecycleFragment. В недалеком будущем предполагается, что все компоненты системы, которые живут в некотором жизненном цикле, будут предоставлять Lifecycle через имплементацию интерфейса LifecycleOwner:

public interface LifecycleOwner {    Lifecycle getLifecycle(); }

Поскольку пакет еще в альфа-версии и его API нельзя смешивать со стабильным, были добавлены классы LifecycleActivity и LifecycleFragment. После перевода пакета в стабильное состояние LifecycleOwner будет реализован в Fragment и AppCompatActivity, а LifecycleActivity и LifecycleFragment будут удалены.

Lifecycle содержит в себе актуальное состояние жизненного цикла компонента и позволяет LifecycleObserver подписываться на события переходов по жизненному циклу. Хороший пример:

class MyLocationListener implements LifecycleObserver {     private boolean enabled = false;     private final Lifecycle lifecycle;     public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) {        this.lifecycle = lifecycle;        this.lifecycle.addObserver(this);        // Какой-то код     }      @OnLifecycleEvent(Lifecycle.Event.ON_START)     void start() {         if (enabled) {            // Подписываемся на изменение местоположения         }     }      public void enable() {         enabled = true;         if (lifecycle.getState().isAtLeast(STARTED)) {             // Подписываемся на изменение местоположения,             // если еще не подписались         }     }      @OnLifecycleEvent(Lifecycle.Event.ON_STOP)     void stop() {         // Отписываемся от изменения местоположения     } }

Теперь нам достаточно создать MyLocationListener и забыть о нем:

class MyActivity extends LifecycleActivity {      private MyLocationListener locationListener;      @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);          locationListener = new MyLocationListener(this, this.getLifecycle(), location -> {          // Обработка местоположения, например, вывод на экран         });         // Что-то выполняющееся долго и асинхронно         Util.checkUserStatus(result -> {             if (result) {                 locationListener.enable();             }         });     } }

LiveData

LiveData — это некий аналог Observable в rxJava, но знающий о существовании Lifecycle. LiveData содержит значение, каждое изменение которого приходит в обзерверы.

Три основных метода LiveData:

setValue() — изменить значение и уведомить об этом обзерверы;
onActive() — появился хотя бы один активный обзервер;
onInactive() — больше нет ни одного активного обзервера.

Следовательно, если у LiveData нет активных обзерверов, обновление данных можно остановить.

Активным обзервером считается тот, чей Lifecycle находится в состоянии STARTED или RESUMED. Если к LiveData присоединяется новый активный обзервер, он сразу получает текущее значение.

Это позволяет хранить экземпляр LiveData в статической переменной и подписываться на него из UI-компонентов:

public class LocationLiveData extends LiveData<Location> {     private LocationManager locationManager;      private SimpleLocationListener listener = new SimpleLocationListener() {         @Override         public void onLocationChanged(Location location) {             setValue(location);         }     };      public LocationLiveData(Context context) {         locationManager = (LocationManager) context.getSystemService(                 Context.LOCATION_SERVICE);     }      @Override     protected void onActive() {         locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, listener);     }      @Override     protected void onInactive() {         locationManager.removeUpdates(listener);     } }

Сделаем обычную статические переменную:

public final class App extends Application {      private static LiveData<Location> locationLiveData = new LocationLiveData();      public static LiveData<Location> getLocationLiveData() {         return locationLiveData;     } }

И подпишемся на изменение местоположения, например, в двух активити:

public class Activity1 extends LifecycleActivity {      @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity1);          getApplication().getLocationLiveData().observe(this, (location) -> {           // do something         })     } }  public class Activity2 extends LifecycleActivity {      @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity2);          getApplication().getLocationLiveData().observe(this, (location) -> {           // do something         })     } }

Обратите внимание, что метод observe принимает первым параметром LifecycleOwner, тем самым привязывая каждую подписку к жизненному циклу конкретной активити.

Как только жизненный цикл активити переходит в DESTROYED подписка уничтожается.

Плюсы данного подхода: нет спагетти из кода, нет утечек памяти и обработчик не вызовется на убитой активити.

ViewModel

ViewModel — хранилище данных для UI, способное пережить уничтожение компонента UI, например, смену конфигурации (да, MVVM теперь официально рекомендуемая парадигма). Свежесозданная активити переподключается к ранее созданной модели:

public class MyActivityViewModel extends ViewModel {      private final MutableLiveData<String> valueLiveData = new MutableLiveData<>();      public LiveData<String> getValueLiveData() {         return valueLiveData;     } }  public class MyActivity extends LifecycleActivity {      MyActivityViewModel viewModel;      @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity);          viewModel = ViewModelProviders.of(this).get(MyActivityViewModel.class);         viewModel.getValueLiveData().observe(this, (value) -> {             // Вывод значения на экран         });     } }

Параметр метода of определяет область применимости (scope) экземпляра модели. То есть если в of передано одинаковое значение, то вернется один и тот же экземпляр класса. Если экземпляра еще нет, он создастся.

В качестве scope можно передать не просто ссылку на себя, а что-нибудь похитрее. В настоящее время рекомендуется три подхода:

  1. активити передает себя;
  2. фрагмент передает себя;
  3. фрагмент передает свою активити.

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

После уничтожения всех компонентов, к которым привязан экземпляр модели, у нее вызывается событие onCleared и модель уничтожается.

Важный момент: поскольку ViewModel в общем случае не знает, какое количество компонентов использует один и тот же экземпляр модели, мы не в коем случае не должны хранить ссылку на компонент внутри модели.

Room Persistence Library

Наше счастье было бы неполным без возможности сохранить данные локально после безвременной кончины приложения. И тут на помощь спешит доступный «из коробки» SQLite. Однако API работы с базами данных довольно неудобное, главным образом тем, что не предоставляет способов проверки кода при компиляции. Про опечатки в SQL-выражениях мы узнаем уже при исполнении приложения и хорошо, если не у клиента.

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

Нам нужно реализовать минимум три компонента: Entity, DAO и Database.

Entity — это одна запись в таблице:

@Entity(tableName = «users») public class User() {      @PrimaryKey     public int userId;      public String userName; }

DAO (Data Access Object) — класс, инкапсулирующий работу с записями конкретного типа:

@Dao public interface UserDAO {      @Insert(onConflict = REPLACE)     public void insertUser(User user);      @Insert(onConflict = REPLACE)     public void insertUsers(User… users);      @Delete     public void deleteUsers(User… users);      @Query(«SELECT * FROM users»)     public LiveData<List<User>> getAllUsers();      @Query(«SELECT * FROM users WHERE userId = :userId LIMIT 1»)     LiveData<User> load(int userId);      @Query(«SELECT userName FROM users WHERE userId = :userId LIMIT 1»)     LiveData<String> loadUserName(int userId); }

Обратите внимание, DAO — интерфейс, а не класс. Его имплементация генерируется при компиляции.

Самое потрясающее, на мой взгляд, что компиляция падает, если в Query передали выражение, обращающееся к несуществующим таблицам и полям.

В качестве выражения в Query можно передавать, в том числе, объединения таблиц. Однако, сами Entity не могут содержать поля-ссылки на другие таблицы, это связано с тем, что ленивая (lazy) подгрузка данных при обращении к ним начнется в том же потоке и наверняка это окажется UI-поток. Поэтому Google приняла решение запретить полностью такую практику.

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

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

Наконец, нам надо задать саму базу данных:

@Database(entities = {User.class}, version = 1) public abstract class AppDatabase extends RoomDatabase {     public abstract UserDAO userDao(); }

Здесь также применяется кодогенерация, поэтому пишем интерфейс, а не класс.

Создаем в Application-классе или в Dagger-модуле синглтон базы:

AppDatabase database = Room.databaseBuilder(context, AppDatabase.class, "data").build();

Получаем из него DAO и можно работать:

database.userDao().insertUser(new User(…));

При первом обращении к методам DAO выполняется автоматическое создание/пересоздание таблиц или исполняются SQL-скрипты обновления схемы, если заданы. Скрипты обновления схемы задаются посредством объектов Migration:

AppDatabase database = Room.databaseBuilder(context, AppDatabase.class, "data")      .addMigration(MIGRATION_1_2)      .addMigration(MIGRATION_2_3)      .build();  static Migration MIGRATION_1_2 = new Migration(1, 2) {     @Override     public void migrate(SupportSQLDatabase database) {         database.execSQL(…);     } }  static Migration MIGRATION_2_3 = new Migration(2, 3) {    … }

Плюс не забудьте у AppDatabase указать актуальную версию схемы в аннотации.

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

По окончанию исполнения всех скриптов, выполняется автоматическая проверка соответствия базы и классов Entity, и вылетает Exception при несовпадении.

Осторожно: Если не удалось составить цепочку переходов с фактической версии на последнюю, база удаляется и создается заново.

На мой взгляд алгоритм обновления схемы обладает недостатками. Если у вас на устройстве есть устаревшая база, она обновится, все хорошо. Но если базы нет, а требуемая версия > 1 и задан некоторый набор Migration, база создастся на основе Entity и Migration выполнены не будут.
Нам как бы намекают, что в Migration могут быть только изменения структуры таблиц, но не заполнение их данными. Это прискорбно. Полагаю, мы можем ожидать доработок этого алгоритма.

Чистая архитектура

Все вышеперечисленные сущности являются кирпичиками предлагаемой новой архитектуры приложений. Надо отметить, Google нигде не пишет clean architecture, это некоторая вольность с моей стороны, однако идея схожа.
image
Ни одна сущность не знает ничего о сущностях, лежащих выше нее.

Model и Remote Data Source отвечают за хранение данных локально и запрос их по сети соответственно. Repository управляет кешированием и объединяет отдельные сущности в соответствие с бизнес задачами. Классы Repository — просто некая абстракция для разработчиков, никакого специального базового класса Repository не существует. Наконец, ViewModel объединяет разные Repository в виде, пригодном для конкретного UI.

Данные между слоями передаются через подписки на LiveData.

Пример

Я написал небольшое демонстрационное приложение. Оно показывает текущую погоду в ряде городов. Для простоты список городов задан заранее. В качестве поставщика данных используется сервис OpenWeatherMap.

У нас два фрагмента: со списком городов (CityListFragment) и с погодой в выбранном городе (CityFragment). Оба фрагмента находятся в MainActivity.

Активити и фрагменты пользуются одной и той же MainActivityViewModel.

MainActivityViewModel запрашивает данные у WeatherRepository.

WeatherRepository возвращает старые данные из базы данных и сразу инициирует запрос обновленных данных по сети. Если обновленные данные успешно пришли, они сохраняются в базу и обновляются у пользователя на экране.

Для корректной работы необходимо прописать API key в WeatherRepository. Ключ можно бесплатно взять после регистрации на OpenWeatherMap.

Репозиторий на GitHub.

Нововведения выглядит очень интересно, однако порыв все переделать пока стоит по-придержать. Не забываем, что это только альфа.

Замечания и предложения приветствуются. Ура!

ссылка на оригинал статьи https://habrahabr.ru/post/329990/