«… я раньше думал, что могу писать программы без ошибок»
Это вторая часть интервью. Первую часть можно прочитать здесь.
Адам: Привет и добро пожаловать в CoRecursive. Я Адам Гордон Белл. В каждом эпизоде CoRecursive кто-то делится увлекательной историей создания собственного программного обеспечения.
Что произойдет, если сайд-проект, который вы делали по фану, вдруг станет популярным во всём мире? Как вы будете монетизировать его? Будете ли вы уделять ему всё своё время? Будете ли вы париться на счёт его обслуживания или просто оставите сервера включёнными и соберёте для них шкаф? Мой специальный гость Ричард Хипп — разработчик SQLite — продолжает отвечать на эти и многие другие вопросы.
Во второй части интервью вы узнаете:
- почему Ричард считает себя причастным к провалу Motorola и Nokia;
- к каким проблемам привело масштабирование СУБД SQLite;
- сколько тест-кейсов нужно, чтобы обеспечить ей 100-процентное покрытие;
- о том, как Ричард добавил покрывающие индексы в SQLite;
- почему и как Ричард разрабатывает собственную систему контроля версий;
- какой совет он может дать младшим коллегам.
Google Android 2005: ранние восторги
Адам: К 2005 году в большинстве мобильных телефонов была установлена SQLite. На тот момент Ричард успел поработать с разными производителями устройств и видел не только их процесс разработки софта, но и, собственно, железа. Разработка гаджетов, и в частности смартфонов, часто движется медленно, с длинными итерациями. Часто приходится долго ждать, пока какое-то изменение или дополнение будет перенесено на новые прототипы. А программисты работают с макетами, которые обычно не похожи на готовые продукты. На одном из ранних этапов работы над смартфоном с Ричардом связалась компания Google. Тогда она была полным аутсайдером на рынке мобильных телефонов и встраиваемых систем, но сумела заинтересовать его.
Ричард: Это было примерно в 2005 году, задолго до того, как Google анонсировала Android. Тогда ещё не было и айфонов. И вот так я оказался в Маунтейн Вью, где я начал работать в паре с ещё одним инженером. У них был прототип телефона с Android OS. В то время у телефонов была полная QWERTY-клавиатура внизу и более мелкий экран — как у Blackberry. Мы отлаживали на нём работу SQLite: подключались к телефону и запускали отладчик на компе. На тот момент это было в новинку и выглядело впечатляюще. В какой-то момент телефон зазвонил, и тот парень произнёс: «О, это моя жена. Мне нужно ответить на этот звонок, извините».
Я вышел из комнаты, чтобы он мог поговорить. Внешне я оставался спокойным, но в моей голове как будто что-то взорвалось. Мы были здесь, мы отлаживали приложение в GNU Debugger c настоящим телефоном, на который уже можно было звонить. И это было крышесносно. Ни у кого в Motorola, и ни у кого в Nokia не было похожего инструментария. В тот момент я осознал, что Android взлетит высоко.
Минутка самобичевания: причастен к провалу Motorola и Nokia
Адам: А как тогда обстояли дела с разработкой телефонов, например, в Nokia? Полагаю, всё было гораздо медленнее?
Ричард: У них между обновлениями железа и программного обеспечения был гораздо больший цикл. Сотрудники, работавшие с Android, собирали новую операционную систему и загружали её на рабочие телефоны, подключённые к общедоступной телефонной сети по несколько раз в день. Для всех остальных компаний это был 30-дневный цикл.
Правда, корпус Android-телефонов выглядел так, как будто его напечатали на 3D-принтере. Это не похоже на готовый телефон, но он был портативным. Инженеры других компаний имели большие прототипы, большую полноразмерную макетную плату, но такой девайс не был подключён к телефонной сети, поэтому они не могли использовать его как телефон.
К сожалению, я был связан по рукам и ногам соглашением NDA.
Я не мог никому сказать. Не мог зайти в Motorola или Nokia и сказать: «Ребята, это важно. Вам нужно это исправить!». Я не мог им сказать! Ох, как это было важно… Боюсь, это их и погубило.
Тестирование MC/DC: «Хвастались, что в SQLite нет багов»
Адам: К этому моменту база данных Ричарда действительно набрала обороты. Он талантливый парень, но его команда из нескольких человек, занималась доработкой и поддержкой SQLite для всех крупнейших в мире мобильных вендоров — Android, Motorola и Nokia. А требования к стабильной работе баз данных, как известно, довольно жёсткие.
Ричард: Мы наивно хвастались всем, что в SQLite нет багов (или серьёзных багов), но Android определённо доказал, что мы ошибались. Представляешь, я раньше думал, что могу писать программы без ошибок. Когда ваше программное обеспечение начинает работать на миллионах устройств, ошибки выявляются очень быстро.
Адам: Ещё бы.
Ричард: Это действительно потрясающе. У них постоянно случались сбои. А примерно в то же время или незадолго до них я выполнял некоторую работу для Rockwell Collins, производителя авионики, и они познакомили меня с концепцией DO-178B. Это стандарт качества для авиационной продукции с повышенными требованиями к безопасности. И, в отличие от многих стандартов качества, он достаточно прозрачен и понятен. Вы можете купить его копию. Вы должны её купить, если хотите его применять. Это пара сотен долларов. Книжечка небольшая, и при достаточном изучении вы сможете достаточно быстро понять, о чём они говорят. Я начал следить за развитием стандарта. Одна из ключевых целей, которым они придают большое значение, — это 100-процентное тестовое покрытие по методу MCDC.
Для уменьшения количества проверок при тестировании логических условий фирмой Boeing был разработан модифицированный метод покрытия по условиям/веткам (Modified Condition/Decision Coverage или MC/DC). Для обеспечения полного покрытия по этому методу необходимо выполнение следующих требований:
- каждое логическое условие должно принимать все возможные значения;
- каждая компонента логического условия должна хотя бы один раз принимать все возможные значения;
- должно быть показано независимое влияние каждой из компонент на значение логического условия, то есть влияние при фиксированных значениях остальных компонент.
Покрытие по этой метрике требует достаточно большого количества тестов для того, чтобы проверить каждое условие, которое может повлиять на результат выражения, однако это количество значительно меньше, чем для прежнего метода «покрытия по всем условиям».
Адам: О, значит, это работает на уровне машинного кода…
Ричард: Ага. Но не будем останавливаться на деталях. В общем, тогда я решил написать тесты для SQLite, чтобы обеспечить 100-процентное покрытие по MCDC. Я работал год, примерно по 60 часов в неделю. Это была тяжёлая, очень тяжёлая работа. Я работал 5/2 по 12 часов в день.
Тогда я очень устал от этого. Помнишь старую шутку о том, что вы получаете 95% функциональности с первыми 95% вашего бюджета, а последние 5% — со вторыми 95% вашего бюджета? Ну вот то же произошло с моими трудозатратами. Довольно легко было получить покрытие от 90% до 95%. А вот получить последние 5% действительно было очень сложно, и мне потребовалось около года, чтобы добиться этого. Но как только мы дошли до этого момента, мы перестали получать от Android отчёты об ошибках.
Адам: Ого.
Ричард: Вот так.
Миллиарды тестов: теперь точно нет багов
Адам: Сколько же тест-кейсов вам пришлось подготовить? Примерно?
Ричард: Первые тесты были написаны на TCL. Это были обычные тесты, которые пишут разработчики.
Адам: Хорошо. И, значит, после этого были написаны тесты MCDC?
Ричард: Да, но мы по-прежнему поддерживаем и тесты TCL. Они по-прежнему остаются на виду у публики и являются частью дерева исходного кода. Кто угодно может загрузить исходный код и запустить эти тесты. Они не обеспечивают 100% тестового покрытия, но они очень тщательно тестируют все функции. А тесты MCDC, которые у нас называются TH3 мы сделали проприетарными. У меня была идея, что мы будем продавать эти тесты производителям авионики и зарабатывать на этом деньги. Мы продали ровно ноль копий, так что это не сработало. Это действительно хорошо сработало для нас, так как наш продукт стал действительно надёжным и позволяет нам очень быстро внедрять новые функции и исправлять ошибки.
Итак, как мы считаем тесты...
У нас, например, есть параметризованный тест-кейс. Он может запускать 100, 1000, 100 000 тестов — просто работая в цикле и меняя один из параметров. Для типичного релиза мы запускаем миллиарды тестов, но у нас, я думаю, порядка 100 000 основных тест-кейсов.
Адам: Ого.
Ричард: У нас есть контрольный набор тест-кейсов, и мы проводим тестирование релиза как минимум за три дня до выхода новой версии.
Адам: Это на одной машине или…
Ричард: Нет, мы намеренно тестируем на разных операционных системах и платформах. У меня есть старые машины, потому что в наши дни все процессоры Intel с прямым порядком байтов в основном продают под заказ. Хотя не так давно было много таких процессоров. У меня есть PowerPC iBook, и мы проводим тесты, в том числе, на нём. Мы следим за тем, чтобы SQLite корректно работал на процессорах с прямым порядком байтов. Также тестируем на 32-битных платформах, тестируем на ARM. Тестируем на чипах Intel. Тестируем на 64-битных платформах. Мы тестируем на Windows, Linux, Mac и OpenBSD. Короче, много всего.
Мы также чередуем разные наборы тестов. Помимо TCL и TH3, у нас есть нестандартные фаззеры, которые работают постоянно. У нас также есть такая вещь, как SQL Logic Test.
Шейн Харрельсон сделал её для нас около 10 лет назад. Он подготовил огромный набор SQL-выражений и запускал их с каждым ядром базы данных, которое попадалось ему в руки. Вот такой интересный набор тестов.
У нас есть ещё много более мелких. Всё вместе это большой объём кода, написанного специально для тестирования, и его выполнение занимает много времени.
Не теоретики, а практики: чужой опыт — да, но без Кнута — никуда!
Ричард: Изначально, когда я создавал SQLite, я искал справочник о том, как написать движок базы данных SQL. Я ничего не нашёл и только поэтому мне просто пришлось изобретать велосипед. Раньше было три сильных теоретических школы — Массачусетский технологический институт, Гарвард в Кембридже и Беркли. Если вы не проходили ни одну из этих трёх школ, вся теория шла мимо вас, а вы шли лесом. Таковы были реалии в моё время.
Однако любопытно вот что: если вы посмотрите на Vulcano-модель, используемую в Postgres, и на модель байт-кода, которая изначально была реализована в SQLite, вы обнаружите много общего. Это своего рода экспериментальная проверка теории. Два независимых направления развития дали один и тот же результат.
Я никогда не слышал, например, о покрывающем индексе. Меня однажды пригласили в Германию на конференцию по PHP. Разработчики языка PHP в то время как раз реализовали поддержку SQLite. Они хотели, чтобы я выступил там, и я приехал. Там же был и Дэвид Аксмарк, один из создателей MySQL.
Дэвид выступил с докладом и объяснил, как работают покрывающие индексы в MySQL. Я подумал: «Ого, это действительно умная идея». Покрывающий индекс — это такой индекс, в котором есть несколько столбцов, и вы выполняете запрос только по первой паре столбцов в индексе, а ответ, который вы хотите, находится в оставшихся столбцах индекса. В таком случае ядро базы данных может использовать только индекс для выполнения этого запроса. Ему не нужно ссылаться на исходную таблицу, и это ускоряет работу.
Адам: Это как хранилище типа «ключ-значение»…
Ричард: Да, похоже. Ну и вот. И на обратном пути, на рейсе Delta Airlines было много свободных мест. В моём распоряжении был целый ряд. Я достал свои вещи, открыл ноутбук и реализовал покрывающие индексы в SQLite.
Адам: Это круто.
Ричард: Ты просто перенимаешь чужой опыт. Люди приходили к нам и говорили: «Эй, почему вы не делаете эту оптимизацию», а я отвечал: «Мне никогда в голову не приходило». «Ну, ты сможешь это сделать?». И я отвечал: «Давай посмотрим, что мы можем сделать». А потом я действительно добавлял это в мой проект. Хотя мне до многого приходилось доходить самому.
Например, я нигде не изучал B-деревья. Я когда-то слышал об этом, не более того. Когда я пошёл писать свое собственное B-дерево, на книжной полке позади меня лежала книга Дона Кнута «Искусство программирования». Поэтому я просто вытащил её, пролистал главу о поиске и просмотрел алгоритм.
Забавно, что Дон подробно описывает алгоритм поиска в B-дереве и вставки в B-дерево, но не даёт алгоритм удаления узла. Он оставил это читателю в качестве упражнения в конце главы. Поэтому, прежде чем я написал свое собственное B-дерево, мне пришлось решить его. Спасибо, Дон. Я очень ценю это.
Адам: Ты что-нибудь ещё почерпнул из этой книги?
Ричард: Это потрясающая книга. Все программисты моего поколения должны были прочитать или хотя бы бегло просмотреть её. Год или два назад мне нужно было написать генератор псевдослучайных чисел, и я подумал: «Посмотрим, что порекомендует Дон». В итоге я утащил его алгоритм к себе.
Git не подходит для разработки SQLite: поэтому появился Fossil
Адам: Да уж, создание собственного генератора псевдослучайных чисел, отталкиваясь от описания алгоритма в бумажной книге, лежащей на полке — это не то, к чему я привык. Работая над SQLite, Ричард в основном создавал всё «с нуля». СУБД не имеет внешних зависимостей, за исключением компилятора C и пары вещей из libc. Более того, Ричард создал свою собственную систему управления версиями и баг-трекер.
Ричард: Я посмотрел на Git, посмотрел на Mercurial, посмотрел на свои требования и подумал: «Знаете что? Я просто напишу свою собственную систему управления версиями». Теперь Fossil является отдельным проектом. И я считаю, это стоило затраченных усилий. Вот, например, Линус Торвальдс написал Git для поддержки ядра Linux. И он идеально подходит для удовлетворения потребностей сообщества разработчиков именно ядра Linux. Для них Git — идеальная система контроля версий.
Но это ужасная система контроля версий с точки зрения разработчика SQLite. Зато Fossil — вот идеальная система контроля версий для работы в SQLite. Я написал её специально для этого, она в точности соответствует моим потребностям. Поэтому, делая всё самостоятельно, вы сами управляете своей судьбой, у вас больше свободы, вы не зависите от третьих лиц.
Время тонких аналогий: что общего между почтовым сервером и газонокосилкой?
Ричард: Сейчас у меня проблемы с Gmail. Когда мы назначали эту встречу, ты заметил, что многие попытки связаться со мной через Gmail оказывались неудачными. И я изо всех сил пытался понять, как решить эту проблему, и к чему же я пришёл?
Я собираюсь написать свой собственный почтовый сервер. Я делал заметки об этом, даже когда мы настраивали эту конференцию. Это большая проблема, и написать его, по крайней мере, так же сложно (если не сложнее), чем написать ядро базы данных. Но я не хочу быть обязанным Gmail.
Я не хочу, чтобы они контролировали запись всех моих разговоров. Я хочу контролировать это сам, и поэтому мне придется пройти через много боли, много работы и много усилий, чтобы придумать какое-то решение. Я могу взять в аренду виртуальную машину в облаке и запустить её самостоятельно, не полагаясь на третью сторону.
Если вы хотите быть свободным, нужно делать что-то самостоятельно.
Адам: Но, слушай, Ричард, лично я с радостью многократно лишаюсь своей свободы, когда нанимаю людей косить мне траву на лужайке.
Ричард: На самом деле, я тоже (улыбается). С другой стороны, у меня есть газонокосилка в гараже, и поэтому, если по какой-то причине люди перестанут работать, я могу взять это на себя.
Адам: Если мои люди, работающие на лужайке, исчезнут, у меня будут проблемы, потому что моя газонокосилка сломана. Но это пустяки. А вот если исчезнет SQLite, начнутся настоящие проблемы. База данных Ричарда — самая используемая в мире. И, по некоторым подсчётам, это самый широко применяемый программный модуль. Если он исчезнет, ваш веб-браузер не будет работать, ваш смартфон, вероятно, не запустится, и, возможно, некоторые автомобили тоже никуда не поедут.
Его влияние на мир огромно. Но было много моментов, где он мог споткнуться, заблудиться. Консорциум мог задушить проект или у него могли кончиться деньги. А за год, который потребовался для устранения всех ошибок в версии для Android, Ричард мог выгореть. Но он справился.
И теперь у него есть прекрасная возможность дать совет менее опытным разработчикам, которые хотят создавать эффективное и востребованное программное обеспечение с открытым исходным кодом. Пожалуйста, Ричард!
Совет в конце
Ричард: У меня была безумная идея — создать движок базы данных, у которого нет сервера, который общается напрямую с диском и игнорирует типы данных. Если бы вы спросили кого-нибудь из экспертов того времени, они бы сказали: «Это невозможно. Это никогда не сработает. Это глупая идея». К счастью, у меня не было никаких экспертов, поэтому я это сделал. Поэтому не поддавайтесь чужому влиянию и делайте то, что имеет смысл. Решайте проблему, которая волнует лично вас.
Cloud VDS с быстрыми NVMе-дисками и посуточной оплатой у
Автор: S_ILya