Global Tag Network Программное обеспечение для приложений и периферии

Технологии Raima

Как мы тестируем Raima Database Manager

Как мы тестируем RDM

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

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

На этой странице будут кратко описаны некоторые процедуры и методы, которым следует Raima для создания надежного и хорошо протестированного продукта.

Инвариантное тестирование базы данных

C / C ++ QA Framework, разработанный в Raima, имеет встроенную поддержку инвариантного тестирования. Инвариантный тест — это особый тип теста, в котором определенное условие должно быть истинным в течение «некоторого времени». Это условие называется инвариантом. Что означает «некоторое время», будет объяснено позже. Для базы данных инвариант обычно о данных, хранящихся в базе данных. Инвариант может быть достаточно ослабленным или очень строгим. Примером ослабленного инварианта является «база данных существует». Пример более строгого инварианта: «вычисление какого-либо хеша из всех данных в базе данных должно дать заданное значение».

QA Frameworks имеют специальную поддержку для создания, запуска и уничтожения инварианта базы данных. Случай создания для инварианта базы данных создает базу данных с заданной схемой и обычно вставляет некоторые данные, чтобы установить инвариант базы данных. Этот особый случай всегда запускается перед обычным случаем или случаями выполнения. Случаи нормального выполнения написаны так, чтобы «поддерживать» инвариант. Что мы подразумеваем под «поддержанием», будет уточнено позже. После выполнения обычных случаев выполняется случай уничтожения, и инвариант больше не поддерживается. Время с момента возврата варианта создания и до вызова варианта уничтожения — это «некоторое время», в течение которого сохраняется инвариант.

Запуск по умолчанию

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

Многопоточность

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

Процессы

Другой сценарий — запустить тест один раз, указав в QA Framework не запускать в конце вариант уничтожения. При этом база данных останется неизменной. Затем мы можем запустить тест в нескольких процессах, где выполняются только обычные сценарии выполнения. Правильно написанный инвариантный тест и надежный продукт также должны справиться с этим.

Независимость от платформы

Другой сценарий — запустить тест один раз и проинструктировать QA Framework пропустить случай уничтожения в конце, скопировать образ базы данных в другую архитектуру или операционную систему и снова запустить обычные случаи. Правильно написанный инвариантный тест, надежный продукт и портативный продукт также должны быть в состоянии справиться с этой ситуацией.

Совместимость изображений между версиями RDM

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

Краш-тестирование

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

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

Тестирование поврежденного изображения

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

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

Удаленная TFS против локальной TFS

Тест с использованием C / C ++ QA Framework может подключаться к транзакционному файловому серверу (TFS) разными способами. Например, TFS может быть встроена в один запущенный инвариантный тест базы данных, и в то же время другой экземпляр того же инвариантного теста базы данных может быть удаленно подключен к первому экземпляру. Второй экземпляр может работать локально на той же машине, что и первый, или удаленно на второй машине. Вторая машина может иметь ту же архитектуру и / или операционную систему, что и первая, или может отличаться. Здесь доступно множество комбинаций.

Verify tests

У нас есть несколько тестов, которые не являются строго инвариантным тестом базы данных. К таким случаям относятся тесты, в которых нормальный кейс или кейсы можно запустить только один раз. Если мы снова запустим обычные случаи, QA Framework должна быть проинструктирована только о «проверке». «Verify» никоим образом не изменит базу данных ; она просто проверит инвариант базы данных. Если QA Framework проинструктирована для параллельного запуска обычных случаев для проверки проверки из нескольких потоков такая инструкция будет проигнорирована и всегда будет запускаться только из одного потока.

Sequence tests

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

Fuzzy testing

Мы запускаем ряд инвариантных тестов базы данных, как описано выше. Один из наших основных инвариантных тестов базы данных выполняет множество случайных операций. Каждый раз, когда он фиксирует сумму чего-либо, сумма поддерживается равной 0. Этот тест сочетает в себе различные типы вставки, обновления и удаления с частями проверки. В тесте используются вложенные start-update и start-read, где он случайным образом возвращается к предыдущему состоянию. Для проверки используется как начальное чтение, так и звездообразный снимок. Этот тест написан не для пропускной способности, а для того, чтобы нагрузить наш механизм транзакций и убедиться, что мы полностью ACID.

Тестирование больших объемов данных

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

Тестирование репликации

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

Заключение

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

Lock testing

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

В нашей модели вложенной блокировки нам нужно убедиться, что все обновления имеют ACID. Что касается блокировки, это имеет определенные последствия. Например, при обновлении в сочетании с чтением мы должны убедиться, что блокировка чтения сохраняется до тех пор, пока обновление не будет зафиксировано, даже если приложение освободило блокировку. Тест не может наблюдать это через стандартный API без использования второго подключения к базе данных. У нас есть однопоточные тесты, которые это подтверждают. Это упрощает воспроизведение и поиск ошибок.

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

Memcheck тестирование

Мы используем Valgrind со стандартным инструментом memcheck в Linux или Purify в Windows для поиска проблем с памятью программного обеспечения. Эти инструменты доказали свою ценность при обнаружении многих типов ошибок.

Однако стандартные распределители, используемые в RDM, не используют распределители C-библиотеки, такие как malloc, calloc, realloc и free. Вместо этого мы используем наши собственные распределители, в которых память извлекается из ОС большими фрагментами или предоставляется через общедоступный API RDM. Поэтому эти инструменты не очень полезны для поиска проблем с памятью при использовании с нашими стандартными распределителями памяти.

Чтобы решить эту проблему, у нас есть альтернативная реализация для наших стандартных распределителей памяти, которая использует функции C-библиотеки, упомянутые выше. Эта реализация в сочетании с Valgrind или Purify используется для поиска проблем с памятью программного обеспечения. Полный запуск нашего набора тестов по умолчанию для C и C ++ с использованием этого подхода занимает несколько дней.

Моделирование сбоя выделения памяти

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

Эта реализация также использует malloc и free, а также некоторые дополнительные данные заголовка для каждого выделения, чтобы помочь обнаружить некоторые неправильные использования нашего стандартного API-интерфейса распределителя памяти. С помощью некоторых инструментов из C / C ++ QA Framework он также может имитировать сбои выделения памяти. QA Framework отслеживает количество выделений памяти и может запустить это снова, если данное выделение памяти завершится ошибкой. Ожидается, что такие прогоны с eNOMEMORY завершатся ошибкой. Если он не сработает, как ожидалось, будет представлена ​​необходимая информация, чтобы проблему можно было легко воспроизвести.

Efence

Efence выполняет небольшую часть того, что может делать memcheck, но намного быстрее. Это было несколько полезно для тестирования RDM на переполнение буфера в тестах, которые выполняются дольше.

Регрессионное тестирование

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

Code Coverage

Мы компилируем наш исходный код с помощью gcc с параметрами для обеспечения покрытия кода. Затем мы используем gcov, lcov и genhtml для создания чего-то, что можно визуализировать.

 

Helgrind тестирование

Мы используем Valgrind с инструментом helgrind для проверки RDM на безопасность потоков. Библиотеки, которые по дизайну не реентерабельны, используют мьютексы для защиты общих структур данных. Helgrind может находить места, где структуры данных не защищены должным образом. Однако для этого требуются тесты, которые используют несколько потоков для некоторых общих структур данных.

Наши инвариантные тесты базы данных, описанные ранее, являются хорошими кандидатами для этого типа тестирования, поскольку QA Framework имеет возможность запускать несколько экземпляров теста параллельно. Другие типы тестов также могут выполняться параллельно; однако эти тесты будут использовать отдельные базы данных, таким образом проверяя структуры данных, совместно используемые базами данных, на безопасность потоков.

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

Reentrancy testing

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

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

Assert

Мы используем утверждения во всем исходном коде. Утверждение — это утверждение в определенной точке кода, которое всегда должно быть истинным. Правильное использование утверждений требует тщательного проектирования. Например, утверждения также должны выполняться в случае состояния ошибки.

Утверждения не предназначены для обработки битовых ошибок в кэше ЦП или ОЗУ. Однако при доступе к файлу следует предположить, что могут быть битовые ошибки. Фактически, любой тип повреждения диска может произойти в общем случае, и механизм должен быть достаточно надежным, чтобы справиться с этим. Любое решение, основанное на прочитанном контенте, должно быть подтверждено, или движок должен быть достаточно надежным, чтобы не давать сбоев или зацикливаться. Если ошибка произошла в пользовательских данных, ядро ​​базы данных может вернуть неполные или неправильные данные. С другой стороны, если в метаданных произошла ошибка, механизм может ее обнаружить, и в этом случае об ошибке будет сообщено пользователю.

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

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

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

Портативный код

Одна важная часть разработки программного обеспечения — убедиться, что код переносим. Поэтому мы избегаем некоторых функций C, которые нельзя переносить. Мы также обеспечиваем переносимость используемых форматов файлов. Это обеспечивается проведением наших тестов на самых разных платформах, включая как реальные аппаратные платформы, так и моделируемые платформы. Мы используем платформы с разным порядком байтов, разным выравниванием, где char по умолчанию имеет значение unsigned и signed, и это лишь некоторые из них.

Статический анализ

Мы используем PC-Lint и FlexeLint для статического анализа. Эти инструменты предоставили ценную информацию для поиска некоторых типов ошибок. Однако мы должны быть осторожны, поскольку некоторые типы перезаписи исходного кода могут легко скрыть или внести ошибки. Поэтому мы используем эти инструменты, когда определенные предупреждения глобально игнорируются, а для других предупреждений мы украшаем исходный код, чтобы подавить их в определенных местах. См. Раздел о тестировании Memcheck.

Usability testing

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

API-интерфейсы, которые мы разработали для RDM, прошли тщательный процесс, чтобы гарантировать соответствие отраслевым стандартам. Публичные файлы заголовков также включают документацию DoxyGen. Мы считаем, что легче поддерживать документацию в актуальном состоянии, если она идет вместе с кодом.

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

Deadlock testing

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

 
 

Тестирование производительности

Производительность — один из важных аспектов любого компьютерного программного обеспечения, особенно для базы данных. Мы измеряем три основных показателя. Загрузка ЦП, дисковый ввод-вывод и использование памяти.

 

Нагрузочное тестирование ЦП

Для загрузки процессора у нас есть несколько тестов производительности. C / C ++ QA Framework поддерживает создание экземпляров секундомеров, которые можно удобно использовать для измерения производительности конкретных вызовов API. Используя их в существующих тестах, мы можем получить цифры, чтобы измерить, как мы делаем это по сравнению с предыдущими версиями нашего продукта.

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

Мы работаем над улучшениями в этой области.

Тестирование дискового ввода-вывода

В RDM 14.1 мы радикально изменили способ записи данных на диск. Работая в режиме, в котором записи на диск сведены к минимуму, RDM может выполнять вставку, обновление и удаление с минимальным дисковым вводом-выводом. Компромиссами здесь являются увеличенное время открытия базы данных и увеличенное время восстановления после сбоя.

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

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

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

Тестирование использования памяти

У нас есть тесты, которые вызывают общедоступный API определенными способами, которые не должны влиять на использование памяти. Например, многократный вызов определенной функции с одними и теми же аргументами не должен увеличивать использование памяти. Эти тесты не пройдут, если будет обнаружено увеличение использования памяти. Любой тест с использованием C / C ++ QA Framework сообщает об использовании памяти в продукте непосредственно перед завершением.

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

 

Фреймворки QA

У нас есть четыре различных QA Framework, разработанных внутри компании. Тот, который написан для C / C ++, является наиболее полным. Кроме того, есть программы, написанные на Perl, Bash и Java. Все они имеют некоторые общие черты, что позволяет нашей команде легко переключаться между ними.

Perl QA Framework

Это более позднее дополнение к нашему набору QA Frameworks. Его основная цель — протестировать инструменты командной строки RDM. Он поддерживает ввод данных в инструменты и может сравнивать вывод с ожидаемыми результатами или grep выводить для определенных шаблонов. Он работает как в Unix, так и в Windows. Многие тесты, которые ранее были частью Bash QA Framework, были переписаны для использования Perl QA Framework. Таким образом, мы можем запускать эти тесты как в Unix, так и в Windows. На этой платформе тестируется много SQL-PL.

C / C ++ QA Framework

Большинство наших тестов C / C ++ написано с использованием этого фреймворка. Обладает наиболее полным набором функций. Подробнее см. В разделе выше, посвященном инвариантному тестированию.

Непрерывная интеграция

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

Основные причины из-за которых компании переходят работать на RDM

Ответ в реальном времени

Повышенная безопасность
Автономные возможности

Высокая стоимость облака

Ограничения широкополосного доступа