Страница 16 из 16
Но и этого инструмента инженерам оказалось недостаточно. Команды становились больше, проекты — сложнее, потери от поломанных сборок росли. Разработчики строили новые очереди отправок, чтобы защитить системы непрерывной сборки. В ранних реализациях все списки изменений действительно вставали в очередь: система тестировала и одобряла или отклоняла списки последовательно. Если нужно было провести много продолжительных тестов подряд, то между постановкой списка изменений в очередь и его фактической передачей в систему контроля версий могло пройти несколько часов. В следующих версиях уже реализовали параллельное выполнение ожидающих списков изменений, но они запускались изолированно друг от друга. Хотя это могло создавать проблемы нарушения последовательности потоков, такие случаи были редки, их оперативно обнаруживала система непрерывной сборки. Возможность заливки кода через несколько минут после отправки запроса экономила много времени. Это компенсировало затраты на исправление редких падений системы непрерывной сборки.
Так большинство крупных проектов Google перешло на использование очередей на отправку. Во многих командах даже выделяли специального человека на роль «смотрителя сборки», задача которого заключалась в том, чтобы быстро реагировать на любые проблемы, выявленные очередью проверки или системой непрерывной сборки.
Эти две системы, панель мониторинга юнит-тестов и система непрерывной сборки Криса и Джея, использовались в Google несколько лет. Они принесли огромную пользу командам, были несложны в настройке и неприхотливы в сопровождении. И вот встал вопрос о реализации этих систем в виде общей инфраструктуры для всех команд. Так появилась система Test Automation Program (TAP). Когда мы писали эту книгу, TAP уже заменила собой обе первоначальные системы. Ее используют почти все проекты Google, кроме Chromium и Android. Только проекты с открытым кодом используют отдельные деревья исходного кода и серверные среды сборки.
Плюсы того, что большинство сотрудников используют один набор инструментов и единую инфраструктуру, трудно переоценить. Одной простой командой инженер может собрать и исполнить все бинарники и тесты, которые связаны с его списком изменений, получить данные о покрытии кода, сохранить и проанализировать результаты в облаке, а потом посмотреть их в виде отчета на постоянной веб-странице. Результат выводится в терминал в виде сообщения «PASS» или «FAIL» со ссылками на подробную информацию. Когда разработчик выполняет тесты, их результаты и данные о покрытии кода сохраняются в облаке, и любой рецензент может посмотреть их через внутренний инструмент для код-ревью.
Пример работы разработчика в тестировании
Следующий пример объединяет все, о чем мы говорили выше. Предупреждаем, в этом разделе много технической информации с уймой низкоуровневых деталей. Если вам интересна только общая картина, смело переходите к следующему разделу.
Представьте простое веб-приложение, с помощью которого пользователи отправляют URL-адреса в Google для добавления в Google-индекс. Форма HTML содержит два поля — ULR-адрес и комментарий — и генерирует запрос HTTP GET к серверу Google в следующем формате:
GET /addurl?url=http://www.foo.com&comment=Foo+comment HTTP/1.1
На стороне сервера это веб-приложение делится на две части: AddUrlFrontend, который получает запрос HTTP, распознает и проверяет его, и бэкенд AddUrlService. Сервис бэкенда получает запросы от AddUrlFrontend, проверяет, нет ли в них ошибок, и дальше взаимодействует с такими хранилищами данных, как, например, Google Bigtable[22] или Google File System.[23]
Разработчик начинает работу с создания каталога для проекта:
$ mkdir depot/addurl/
Затем он определяет протокол AddUrlService с использованием языка Protocol Buffers:[24]
File: depot/addurl/addurl.proto
message AddUrlRequest {
required string url = 1; // The URL address entered by user.
optional string comment = 2; // Comments made by user.
}
message AddUrlReply {
// Error code if an error occured.
optional int32 error_code = 1;
// Error mtssage if an error occured.
optional string error_details = 2;
}
service AddUrlService {
// Accepts a URL for submission to the index.
rpc AddUrl(AddUrlRequest) returns (AddUrlReply) {
option deadline = 10.0;
}
}
В файле addurl.proto определены три важных элемента: сообщения AddUrlRequest и AddUrlReply и сервис удаленного вызова процедур (RPC, Remote Procedure) AddUrlService.
Посмотрев на определения сообщения AddUrlRequest, мы видим, что поле url должно быть задано вызывающей стороной, а поле comment не является обязательным.
Точно так же из определения сообщения AddUrlReply следует, что оба поля — error_code и error_details опционально могут быть переданы в ответах сервиса. Мы предполагаем, что в типичном случае, когда URL-адрес успешно принят, эти поля останутся пустыми, чтобы минимизировать объем передаваемых данных. Это одно из правил Google: типичный случай должен работать быстро.
Из определения AddUrlService видно, что сервис содержит единственный метод AddUrl, который принимает AddUrlRequest и возвращает AddUrlReply. По умолчанию вызов метода AddUrl прерывается по тайм-ауту через 10 секунд, если клиент не получил ответа за это время. Реализации интерфейса AddUrlService могут включать в себя сколько угодно систем хранения данных, но для клиентов интерфейса это несущественно, поэтому эти подробности не отражены в файле addurl.proto.
Обозначение '= 1' в полях сообщений не имеет никакого отношения к значениям этих полей. Оно существует для того, чтобы протокол можно было дорабатывать. Например, кто-то захочет добавить поле uri в сообщение AddUrlRequest к уже имеющимся полям. Для этого вносится следующее изменение:
message AddUrlRequest {
required string url = 1; // The URL entered by the user.
optional string comment = 2; // Comments made by the user.
optional string uri = 3; // The URI entered by the user.
}
Но это выглядит довольно глупо — скорее всего, потребуется просто переименовать поле url в uri. Если это число и тип останутся неизменными, сохранится совместимость между старой и новой версией:
message AddUrlRequest {
required string uri = 1; // The URI entered by user.
optional string comment = 2; // Comments made by the user.
}
Написав файл addurl.proto, разработчик переходит к созданию правила сборки proto_library, которое генерирует исходные файлы C++, определяющие сущности из addurl.proto, и компилирует их в статическую библиотеку addurl C++. С дополнительными параметрами можно сгенерировать исходный код для языков Java и Python.
File: depot/addurl/BUILD
proto_library(name="addurl",
srcs=["addurl.proto"])
Разработчик запускает систему сборки и исправляет все проблемы, обнаруженные ею в addurl.proto и в файле BUILD. Система сборки вызывает компилятор Protocol Buffers, генерирует исходные файлы addurl.pb.h и addurl.pb.cc и статическую библиотеку addurl, которую теперь можно подключить.
Пора писать AddUrlFrontend. Для этого мы объявляем класс AddUrlFrontend в новом файле addurl_frontend.h. Этот код в основном шаблонный.
File: depot/addurl/addurl_frontend.h
#ifndef ADDURL_ADDURL_FRONTEND_H_
22
http://labs.google.com/papers/bigtable.html
23
http://labs.google.com/papers/gfs.html
24
http://code.google.com/apis/protocolbuffers/docs/overview.html
Конец ознакомительного фрагмента. Полная версия книги есть на сайте ЛитРес.