среда, 23 ноября 2011 г.

Moles vs Moq

Moles

Какое-то время назад, при выборе Isolation фреймвёрка для написания тестов мой выбор пал на Moles. Выбрал его я, руководствуясь следующими фактами: большой общественный резонанс (на хабре статьи про него, как пирожки вылетают), громкое имя разработчика - Microsoft Research как ни как. Ну и, посмотрев пару замечательных примеров, начал использовать его сначала в своих домашних разработках, а потом и на основной работе в достаточно большом проекте.

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

Самый первый звоночек, говорящий, что здесь все не так радужно как кажется прозвенел при первых запусках тестов - достаточно долгое время запуска тестов. Таким образом, на подготовку тестов к запуску уходило секунд 20-30 и это не на самой плохой машине. Это время идёт на инициализацию платформы Moles, многие скажут - полминуты это не много, тем более только один раз при запуске набора тестов, но раздражает. Справедливости ради замечу, что такая долгая инициализация происходит, если использоваться host type для исполнения тестов - moles. Для тех, кто не знает, Moles могут работать в двух ипостасях: moles и stubs. Первые используют для исполнения тестов собственный хост, вторые исполняются в обычной среде.

Поначалу меня вполне устраивали эти небольшие полминутные задержки, дающие действительно уникальные возможности, например, подмена статического метода (поля) DateTime.Now. Собственно никакого другого применения для молей, кроме как подмены готовых статических или sealed членов я и не нашел. Дальше - больше, при использовании этого волшебного хоста, как оказалось ни решарпер ни TeamCity не могут правильно считать покрытие тестами. Наверно, это и стало главной проблемой, приведшей к отказу в проекте от moles хоста, а, следовательно, и молей как таковых. Благо проект только начинал писаться, и тестов работающих под этим хостов было не много, а развивающийся проект позволял изменить архитектуру таким образом, чтобы без таких тестов можно было обойтись.
Собственно после отказа от moles хоста пользование этого фреймвёрка стало приносить гораздо меньше хлопот. Практически не вызывает проблем и сейчас - фреймвёрк удобный.

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

  1. Для использования заглушек используются генерируемые самим фреймвёрком сборки, создающиеся на основе указанных разработчиком. Сам процесс создания таких сборок заглушек практические не причиняет неудобств - тыкнул раз на сборке и пользуйся. А вот тот факт, что для заглушки только что объявленного члена интерфейса требуется скомпилить сборку и только потом глушить в тестах - раздражает.
  2. Удаление однажды созданной сборки требует выполнения аж трёх действий: удаление самого референса, удаление конфигурационного файла (*.moles) и удаление кеша этих сборок из папки проекта.
  3. Если использовать стаб по умолчанию, то обращение к любому не заглушеному члену интерфейса приводит к генерации исключения в момент выполнения, поэтому, в большинстве случаев создание стаба выглядит так:
    var createCommand = new SIDbCommand
    {
      InstanceBehavior = BehavedBehaviors.DefaultValue
      ....
    };
    Это как минимум занимает много места, да и вообще много лишних букв.

Moq

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

  • Для использование moq не нужно генерировать никаких сборок - всё заменяет волшебных класс Mock.
  • Можно молить еще не скомпиленные интерфейсы, что позволяет писать тесты до начала любой реализации.
  • Явно не заглушенные члены интерфейса наконец не нужно никак выделять - они по умолчанию работают как надо. Таким образом, аналогичный выше приведенный код на Moq будет выглядить примерно так:
    var createCommand = new Mock<IDbCommand>().Object;
Также архитектурно заложены следующие недостатки, по сравнению с Moles:
  • Не возможно молить статически и sealed члены.
  • Очень слабый лямбда синтаксис. Да и Fluent тоже какой-то не такой. Короче синтаксис специфичный. Не скажу, что ужасный, но в Moles значительно читабельнее и понятнее, хотя опять же возможно дело привычки. В общем, сравнивайте сами
    createCommand.ExecuteScalar = () => 100500; // Moles
    createCommand.Setup(x => x.ExecuteScalar()).Returns(100500); // Moq

В некоторых моментах использование Moq является намного удобнее Moles. Например, чтобы убедиться, что был вызван метод в Moles приходилось делать так:

bool tableWasCreated = false;
var databaseStrategy = new SIDatabaseStrategy
{
  InstanceBehavior = BehavedBehaviors.DefaultValue,
  IsTableExistString = table => !table.Equals("dbullet"),
  CreateTableTable = table => { tableWasCreated = Equals(table.Name, "dbullet"); }
};
target.InitDatabase(GetType().Assembly);
Assert.IsTrue(tableWasCreated);
А в молях как-то так:
databaseStrategy.Setup(x => x.IsTableExist("dbullet")).Returns(false);
target.InitDatabase(GetType().Assembly);
databaseStrategy.Verify(x => x.CreateTable(It.Is<Table>(y => y.Name == "dbullet")), Times.Once());

Опять не получается написать все за один подход, поэтому продолжить постараюсь в следующий раз. Но предварительный вывод такой - если Вы начинаете писать новый проект или есть возможность изменять архитектуру старого, то стоит посмотреть на что-то более легковеснее чем Moles (читай Moq), если же требуется молить что-то не доступное для изменения (сторонние сборки, древний унаследованный код, плохо спроектированный код) - Moles Вас очень сильно выручит. Я же свои булеты перевел на Moq, хотя пока и не в основной ветке. Уже сейчас можно там посмотреть как выглядят аналогичные тесты на Moq и Moles, ну или можно дождаться следующей моей заметки.

To May be continued...

суббота, 8 октября 2011 г.

Mongo vs MsSql часть четвертая - select

Здравствуй дорогой читатель! Продолжаю серию экспериментов по сравнению производительности Mongo DB и MSSQL Server. На этот раз я решил сравнить производительность операций выборки, так как это, пожалуй, основная операция при взаимодействии с БД в большинстве приложений. На этот раз постараюсь провести несколько экспериментов в одной статье, по-этому будет минимум текста.

Выборка по ключу

В первом тесте я буду выбирать информацию из БД по первичному ключу. Для подготовки тестовых данных я воспользовался наработками предыдущего эксперимента по добавлению разнородных объектов. Выборки осуществляются по случайно сгенерированному ключу в цикле по десять тысяч итераций из наборов записей в количествах 1000, 10000, 50000, 100000 и 500000. Исходники этого теста на Гитхабе.

Как видно, на операциях выборки данных по первичному ключу обе СУБД показали примерно одинаковые результаты, причем скорость выборки практически не зависит от количества записей в базе.

Выборка набора записей

Во второй части я буду выбирать данные не по одной штуке, а по 10, 100 и 500, но тоже по первичному ключу. Прямая ссылка на исходники эксперимента.

Увы, в выборке больше одной записи Mongo не особо силен. Если на достаточно маленьком количестве записей в выборке, Mongo практически не уступает MSSQL, то уже на 100 записях выборки разница в быстродействии отличается в пять раз. И чем больше выборка, тем этот разрыв в производительности увеличивается.

Выборка по like

Последний, на этот раз, эксперимент - поиск по like. Искать буду по начальным четырем символам Guid, которыми заполняется поле Field1. Индекс по этому полю не строился. Исходники этого эксперимента.

И снова MSSQL выполнил операции поиска быстрее Mongo. Как и следовало ожидать, время поиска по не индексированному полю зависит от количества записей в базе.

Сегодняшние эксперименты показали преимущество MSSQL Server в выборке набора записей. Тем не менее, Mongo DB вполне может заменить MSSQL Server, если требуется выбирать небольшое количество записей.

четверг, 6 октября 2011 г.

Mongo vs MsSql часть третья - снова insert

Это третья по счету статья из серии серии, где я пытаюсь сравнить две СУБД Mongo DB и MS SQL. Вот предыдущие части: первая и вторая. Исходники по прежнему лежат на GitHub, а это прямая ссылка на тег с текущим экспериментом. Прошлый раз я забыл сказать, если кто захочет повторить эксперимент у себя, то нужно заменить строку подключения к базе сиквела в классе SqlServerBenchmark (да знаю для этого есть конфиги, но пофиг). База годится любая, но лучше завести чистую :) Все операции по подготовке базы (создание таблиц и их чистка) берет на себя dbullet. Так что для запуска достаточно прописать строку подключения.

Так же немного поменялась конфигурация машины на которой будут проходить эксперименты, теперь это Core2Quad Q6600, 4GB RAM софтверные составляющие не поменялись, и это по прежнему Windows 7 x64, MS SQL Server 2008R2 develop и Mongo DB 2.0.

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

То есть, объект состоит из кучи полей, как это обычно бывает в реальных приложениях. Количество записей для вставки по прежнему 1000, 5000, 10000, 75000 и 100000, а для Mongo еще и 1000000 в нагрузочку. Также немного переработан механизм запуска, теперь все тесты выполняются за один запуск.

Как и следовало ожидать на подобных объектах Mongo опять в пух и прах разнес сиквел. А вот следующий эксперимент оказался совсем не таким однозначным, как показалось сначала. Суть такова: берем большие объекты такой структуры

При вставке каждое из текстовых полей забивается четырьмя тысячам символов, это где-то 4000*4*2=32000 байт и вставляется как и раньше. Исходники как всегда на Гитхабе - ссылка на тег эксперимета Сначала все происходило как и ожидалось, то есть Mongo обгонял как и раньше. Но вот на миллионе записей Mongo начал вести себя несколько странновато - во первых он съел всю память, которую мог съесть (больше 2ГБ, своп отключен). А дальше стало происходить вообще странное - он забил почти весь диск, на которой лежали файлы базы, что-то около 26ГБ. Работал он долго, но не упал - все перемолол. Ну на самом деле как то так и получается 32000*1000000/1024/1024/1024 = 29,8ГБ. И эти почти тридцать гигов он перелопатил за 12 минут, молодец в общем (винт SSD).

Ну и в виде графиков

Не вписывающиеся в общую картину выпады вниз на втором графике со стороны Mongo объясняются малым количеством установленной памяти (а Бил когда то про 640КБ втирал...). Т.е. эксперименты запускались по порядку и скул не успел еще отпустить занятые им 1.5ГБ, но думаю общую картину это не исказило. Так же нехваткой памяти объясняется и резкий скачок вверх на вставке миллиона записей.

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

Разумеется, это не последний эксперимент по сравнению этих двух замечательных СУБД, в следующий раз наверно сравню производительность обновления данных

Mongo vs MsSql часть вторая - insert


В этой части серии экспериментов по сравнению Mongo DB и MS SQL Server я буду всячески мучать этих, не самых последних, представителей двух различных классов СУБД - NoSql и реляционных соответственно, вставками данных.
Итак мы имеем MS SQL Server 2008R2 редакции develop и Mongo DB 2.0. Если кто хочет воспроизвести эксперимент у себя - Вам понадобятся соответствующие дистрибутивы, вместо develop редакции сиквела вполне сгодится бесплатный express, Mongo можно загрузить отсюда. Исходники программки можно найти на GitHub по этой ссылке, для компиляции понадобится dbullet, кто не использует git - качайте отдельно, для всех остальных:
$
git submodule init
$
git submodule update
Тестирование проходило на следующий машине с такой конфигурацией: Windows 7 64, Core2Duo E4500, 8GB RAM

Опыт первый

Вставка большого количества мелких объектов

Для проведения измерений был создан такой простой интерфейс, который будет дополняться с каждым новым тестом:

public interface IBenchmark
{
  void InsertSimpleSmallObject(SimpleSmallObject obj);
}

Реализация для MsSql:

void InsertSimpleSmallObject(SimpleSmallObject obj)
{
  command.Parameters[0].Value = obj.Value;
  command.Parameters[1].Value = obj.Id;
  command.ExecuteNonQuery();
}

Реализация для Mongo:

void InsertSimpleSmallObject(SimpleSmallObject obj)
{
  collection.Save(typeof(SimpleSmallObject), obj);
}

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

public class SimpleSmallObject
{
  public int Id { get; set; }
  public string Value { get; set; }
}

В качестве Value используется Guid.

Одна тысяча объектов:
Inserting small objects (1000 objects) of FakeBenchmark
Time: 00:00:00.0020001
Inserting small objects (1000 objects) of SqlServerBenchmark
Time: 00:00:03.5131987
Inserting small objects (1000 objects) of MongoBenchmark
Time: 00:00:00.1380078
Пять тысяч объектов:
Inserting small objects (5000 objects) of FakeBenchmark
Time: 00:00:00.0050003
Inserting small objects (5000 objects) of SqlServerBenchmark
Time: 00:00:15.9469122
Inserting small objects (5000 objects) of MongoBenchmark
Time: 00:00:00.2690154
Десять тысяч объектов:
Inserting small objects (10000 objects) of FakeBenchmark
Time: 00:00:00.0110006
Inserting small objects (10000 objects) of SqlServerBenchmark
Time: 00:00:31.7717581
Inserting small objects (10000 objects) of MongoBenchmark
Time: 00:00:00.6260358
Семьдесятпять тысяч объектов:
Inserting small objects (75000 objects) of FakeBenchmark
Time: 00:00:00.0800001
Inserting small objects (75000 objects) of SqlServerBenchmark
Time: 00:04:06.5806614
Inserting small objects (75000 objects) of MongoBenchmark
Time: 00:00:03.7390576
Сто тысяч объектов:
Inserting small objects (100000 objects) of FakeBenchmark
Time: 00:00:00.1030037
Inserting small objects (100000 objects) of SqlServerBenchmark
Time: 00:05:33.3390749
Inserting small objects (100000 objects) of MongoBenchmark
Time: 00:00:05.6952817
На этом этапе я посчитал, что нужно присвоить сиквелу техническое поражение, но тем не менее хотелось хоть немного поднапрячь Mongo. Итак увеличиваем количество вставок до милиона.
Милион объектов:
Inserting small objects (1000000 objects) of FakeBenchmark
Time: 00:00:01.0090577
Inserting small objects (1000000 objects) of MongoBenchmark
Time: 00:01:06.2024621

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

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

среда, 5 октября 2011 г.

Mongo vs MsSql часть первая - установка


Не прошло и три года, как я снова решил попробовать вести бложик :) Тем более возникла такая ситуация, с которой я просто не могу не поделиться. Итак начнем.
В последнее время все чаще и чаще слышатся слова NoSql, Mongo, Cassandra и прочие непонятные термины и связанные с ними восторженные крики. Вот и решил я самолично разобраться что же это за зверь такой - NoSql и самое для меня главное, где же его можно применить и как быстро оно работает :) Так как ни с одной NoSql СУБД я до этого не был знаком, то решил взять первую попавшуюся, и это оказалась Mongo.
Первым делом удивил размер этой СУБД - чуть больше 20 метров (для сравнения тот же MS SQL 2008 R2 редакции develop занимает 5,6 ГБ, правда со всеми прилагающимися цыганами).
Второе, что еще сильнее, но уже даже не удивило, а просто поразило - простота установки, точнее полное её отсутствие, за необходимостью. Все что нужно - всего лишь распаковать архив, создать папку для хранения файлов базы и запустить сам сервер, который представляет собой обычное консольное приложение в простейшем случае (скорее всего существует возможность развернуть и в виде службы, но мне пока это не надо).
Как ни странно, но в эти 20 метров дистрибутива входит еще и среда исполнения запросов, а по совместительству и админка СУБД, так же выполненная в виде консольного приложения.
Итак, попробую составить некоторую таблицу сравнения Mongo с MSSQL:
ПоказательMS SQL Server 2008R2 developMongo DB 2.0
Дистрибутив
Размер дистрибутива5,6 ГБ20,2 МБ
Скорость установки20-30 минутмоментально
Утилита администрирования
Графический интерфейсЕстьНет
ИнтелисенсЕсть, но постоянно отваливается так, что скорее "нет", чем "есть"Есть, и не отваливается

Но на самом деле все, что выше перечислил не особо сильно характеризует СУБД, да размер отличается на несколько порядков, но кого он волнует в нынешнее время, ведь по большому счету, что 5ГБ, что 20 метров - качаешь то один раз на время установки. Скорость установки - да тоже, раз в жизни сервера. ГУИ админки в сиквеле графическое конечно, но такое убогое, что и консольная монга не особо то и сильно напрягает (кто начал плеваться и брызжать слюной - попробуйте PL SQL developer для оракла или хотя бы EMS Manager, а потом поговорим). Так что по результатам, первого раунда и конечно же, только с моей точки зрения - победила дружба :)

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