2/11/2010 12:07:42 PM

Вышли релиз-кандидаты Visual Studio 2010 и TFS 2010. Поставил, смотрю…

Про “студию” могу сказать пока только то, что работать стала существенно быстрее, чем прошлая Beta2. Предыдущая версия своей производительностью, признаться, наводила на мысль о том, не пошло ли использование WPF в ущерб. Однако, теперь этого ощущения нет. Работает не медленнее 2008-й – это точно.

А вот TFS стоит отметить отдельно. Устанавливать его теперь гораздо приятнее, чем его 2008-го предка, с которым ещё намучаешься устанавливая, да и немало (помню, как в Алкателе сидел до 10 вечера, выполняя установочные инструкции по шагам).
Установка же 2010-й версии проста и даже примитивна. Два этапа: собственно, установка, которая занимает всего пару минут, и конфигурация в визарде, что тоже занимает примерно столько же времени.
Сразу скажу, что я пробовал только Basic конфигурацию (там есть ещё пара вариантов), так как устанавливал на локальную машину. Приятно, что в Basic-варианте работает и билд сервис, и даже web access, и при этом ему не нужно ничего, что начинается с “Sharepoint”. И работает даже с SQL Server Express.
Поставил – и работай. Просто, удобно, как и хотелось.

Много всяких интересных штук там, в TFS 2010, но я хочу отметить две:

  1. Билд-сервисы:
    • На одной машине можно без хаков иметь несколько build-агентов, что не может не радовать;
    • Билд-скрипт, который был раньше MSBuild-проектом, теперь представляет собой Workflow. Редактировать его в Workflow-дизайнере, добавлять и даже создавать свои собственные задачи теперь гораздо удобнее и приятнее.
  2. Gated Check-Ins.
    Здесь я, признаться, в замешательстве. Поясню: Gated check-in – это когда разработчик делает check-in в репозиторий, при этом выполняется build, если хотите – прогон unit test’ов, и если что-то пошло не так, то внесённое изменение отвергается и не попадает в систему контроля версий. А если всё прошло как надо, то изменение попадает в соурс контрол, конечно. Соответственно, в соурс контроле мы имеем ветку, которая никогда не будет сломана и всегда будет как минимум компилироваться, а как максимум – зависит от вашего покрытия юнит тестами.

Так вот, Gated Check-Ins работают в TFS2010 замечательно, билд никогда не ломается и всё такое. Но есть у меня одно недопонимание, одна фича, которую, как я надеялся, исправят после Beta2, но она осталась…

Выглядит это так:

  1. Я изменяю код и делаю check-in.
  2. Поскольку мои изменения, пока не прошла валидация, де-факто не попадают в репозиторий, я всё ещё вижу изменённые файлы как checked-out.
  3. Билд и юнит-тесты проходят успешно, код попадает в репозиторий.
  4. Здесь я ожидаю, что файлы станут checked-in, если я их больше не трогал. Онако, этого не происходит.
  5. Я пытаюсь делать Get Latest Version или Check In снова, не важно что из этого.
  6. TFS, внимание, заявляет, что у меня локально присутствует версия файла #7, а в репозитории она уже #9 (очевидно, #8-это мой промежуточный check-in, #9 – финальный после валидации).
  7. Мне необходимо сделать merge версий 7 и 9! Хотя, ежу понятно, что они одинаковы, ибо это изменение делал я, только я и никто больше!
  8. Я признаю, что merge в VS2010 и в VS2008 – вещи очень и очень разные в пользу VS2010, там надо нажать ровно одну кнопку, даже никаких всплывающих окон, но сам факт… И так, делаю auto merge, всё проходит отлично, так как нет разницы, но файлы-то остаются в состоянии checked out!
  9. Я должен снова сделать check-in, и тогда оно мне скажет: изменений нет. И пометит файлы как checked in, чего мне и хотелось.

Звучит, возможно, сложнее, чем есть на деле, но вы попробуйте :)

Я понимаю, что это, наверное, достаточно сложно – отследить, что я действительно не менял файлов пока шла валидация, поэтому нельзя просто сделать их checked out когда она закончена.. Ну, вдруг я менял.
Я понимаю, что в общем случае при таком вот “отложенном” check-in может возникнуть automerge от нескольких разработчиков и в этом случае нужно просить делать merge…
Но я не понимаю, разве нельзя было ввести ещё один статус “в процессе валидации" для файлов в солюшн эксплорере? И “сбрасывать” его обратно в “checked out” если я меняю эти файлы? Пусть даже и на основе read-only атрибута в файловой системе?
Разве нельзя отследить, что во всём этом процессе вот для этого конкретного файла не было никакого автоматического merge’а сделано на уровне TFS? И не просить меня делать merge если файл не меняли ни локально, ни в TFS?
Во всех остальных случаях – спрашивайте, пожалуйста, я только рад буду и буду делать merge с удовольствием!

Короче, тут можно, конечно задать вопрос “Вам шашечки или ехать?”, и утверждать, что целостность билда важнее всех этих мелких неудобств.. Но ведь хочется, чтобы и их не было… :)

В общем, пока как-то так…

1/22/2010 1:39:36 PM

Большое количество проблем, связанных с производительностью SQL Server на больших данных так или иначе упираются в IO. В нашем случае, когда речь идёт о действительно больших объёмах, это ощущается по полной программе. Для примера, маленькая дивелоперская база данных, используемая локально на моей машине, занимает сейчас чуть менее 14 гигабайт. Это так, считай ничего, локально фичи писать/отлаживать.

На моей рабочей машине установлено “всего” 12 гигабайт памяти, из которых SQL Server’у разрешено использовать 6. Поэтому, понятное дело, о полном кешировании базы в памяти речь идти не может. Да и, думаю, редко где такая возможность существует.

И вот тут мы упираемся в IO. Особенно на scan’ах.

Решать эту проблему можно двумя путями:

1) Грамотным построением SAN’а (Storage Access Network, всё имеющееся дисковое пространство), умным разбиением оного на LUN’ы (Logical Unit Number, логическая группа физических дисков), объединение LUN’ов в соответствующие задачам RAID’ы, распределением по ним файлов базы данных и т.д, что позволяет добиться отличных результатов за счёт параллельной работы дискового массива;

2) Настройкой и распределением самой базы данных SQL Server.

Сразу скажу, что 1 и 2 не исключают друг друга, наоборот, гармонично дополняют и всячески сопутсвуют.
Далее я буду говорить только о втором пункте, потому как первое – это работа администратора, в этом я не силён. Кроме того, часто бывает так, что конфигурацию “железа” особо выбирать не приходится (либо уже настроено на максимум), а производительность повысить надо.
В любом случае, не пользоваться возможностями SQL Server’а по оптимизации смысла нет.

Для оченки производительности именно в разрезе IO я перед каждым запросом делаю DBCC DROPCLEANBUFFERS, чтобы очистить clean buffers, которые в SQL Server являются кешем прочитанных с диска страниц в памяти.

Для начала – краткое и простое (без углублений) пояснение того, откуда и как вообще берётся нагрузка на диски.

SQL Server читает с диска данные так называемыми “страницами” по 8 килобайт. На каждой такой странице находится какое-то количество строк. Столько, сколько может уместиться на 8-килобайтовой странице. Соответственно, даже если требуется “выдать” всего одну строку, SQL Server прочитает с диска минимум страницу.
С другой стороны, чем больше строк “умещается” на страницу, тем меньшее количество раз SQL Server’у нужно обратиться к диску для получения результата.
Здесь мы приходим к важному:

  • Размер имеет значение.

Часто работая с кодом, мы не придаём значения размеру типов переменных: long, int, byte – какая разница. В большинстве случаев это действительно так – разницы никакой. Но в случае с SQL Server всё иначе, здесь размер действительно имеет значение. Ибо, скажем, bigint занимает места в 2 раза больше, чем int, соответственно, данных с колонкой типа bigint на страницу поместится в два раза меньше, чем данных с колонкой типа int, что означает, что для чтения одного и того же набора данных SQL Server’у потребуется прочитать в 2 раза больше страниц. То есть – мы имеем в 2 раза большую нагрузку на диски (IO).
Так и с другими типами данных. Именно поэтому SQL Server и имеет всякие разные настройки точности типов, вроде datetime2(4) и т.д.
Это не значит, что надо бросаться переделывать все bigint на int, это значит, что в данном случае о размерности типов надо думать.

Говоря об IO, в общем-то, вся задача сводится к одному простому вопросу: как заставить SQL Server читать с диска меньше страниц (про “читать быстрее”, повторюсь, речь не идёт)?

Про “умещать на страницу больше данных” с помощью “правильного” выбора типов колонок я уже сказал, однако SQL Server (начиная с 2005 и в Enterprise версии, насколько я помню)  предлагает и ещё один вариант:

  • Компрессия данных.

Существует два вида компрессии: Page (на уровне страниц) и Row (на уровне строк). Они друг от друга сильно отличаются.

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

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

Забавно, но во всех случаях, которые я видел, Page-компрессия “сжимала” данные лучше, чем Row-компрессия. Хотя я, признаться, ожидал обратного. Коэфициент сжатия сильно зависит от самих данных, конечно. У меня получалось по-разному: бывало и 25-30%, а бывало и 70%…
Попробовать и оценить можно прямо из Management Studio – когда делаете компрессию там есть кнопочка “посчитать”. Или можно просто системной процедурой воспользоваться, названия не помню (дома я).

Уместить на страницу “да побольше, побольше” – это половина решения. Вторая половина заключается в том, что и как туда умещать.
Ну вот представьте, мы выбираем из таблицы Orders все записи, сделанные для компании VPupkin LTD. SQL Server читает, он читает постранично, он всегда так делает. На первой (подходящей, допустим сервер знает какие страницы надо читать и где есть интересующая нас информация) странице у нас одна нужная запись, на второй – ещё одна, на четвёртой – две и ещё одна на шестой. SQL Server вынужден прочитать с диска страницы 1,2,4 и 6. И совершенно не важно, что на каждой из этих страниц ещё по 196 записей, которые к нашему запросу отношения не имеют.
Отсюда вопрос: как бы сделать так, чтобы интересующие нас записи располагались “рядышком”, а не “расползались” по всему файлу данных?
И на это есть хороший ответ:

  • Создание разделов (партиций)

По-моему эта штука доступна тоже только в Enterprise-версии, но я не уверен.
Смысл её состоит в том, что для какой-либо таблицы мы можем задать несколько разделов (партиций, я буду писать “партиций”, не привык я к “разделам”) и указать критерий, по которому SQL Server будет решать, в какую именно партицию попадёт запись.

То есть, буквально, мы просто говорим SQL Server’у: все Orders для компаний с ID от 1 до 100 – в эту партицию, а от 100-200 – вон в ту. Таким образом мы создаём некую физическую группировку данных, то есть, при наличии, скажем, 10 партиций заказы одной компании будут находиться в одной пратиции => “раскиданы” по 1/10 таблицы, а не по всей, => в 10 раз увеличивается плотность заказов одной компании => меньшее количество страниц нужно будет считать с диска.

К тому же SQL Server достаточно умён для того, чтобы понять: раз все записи для указанной компании находятся на одной партиции, то во все другие вообще смотреть не надо, в запросе учавствует только кусок таблицы. В 10 раз меньше данных.
Ну и существует синтаксис для того, чтобы явно обратиться к какой-то одной партиции и сделать запрос только к её данным, существует возможность для определения, в какой именно партиции лежат интересующие данные, если мы хотим обратиться к ней явно и т.д. и т.п.

При всём вышесказанном, таблица продолжает оставаться целой таблицей во всех её проявлениях, “извне” ничего не меняется, что просто замечательно.

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

Ещё одна замечательная открывающаяся возможность – это паралеллизм. SQL Server, выполняя запрос, в котором учавствует такая “разбитая” таблица, оптимизирует работу, выполняя запрос к каждой партиции параллельно. Это очень существенно уменьшает время выполнения запроса, особенно на scan’ах. Даже если файловые группы партиций находятся на одном физическом диске, видимо, за счёт “группировки” на страницах и этого самого паралеллизма.
В цифрах: у меня есть запрос, выполняющийся за 1 минуту 09 секунд. После разбивки таблицы (для теста, на глазок) всего на 5 партиций, находящихся на одном физическом диске (том же, другого у меня нет), время выполнения запроса сократилось до 40 секунд.

Максимальное число партиций – 1000 на одну таблицу. По словам человека из Microsoft – цифра взята “с потолка”, надо же было какое-то ограничение придумать :)

Теперь об “побочных эффектах”.

В смысле разбиения по партициям – никакого отрицательного побочного эффекта, в общем-то, и нет. Есть ещё несколько интересных возможностей, такие, как staging-таблицы, например, но я и не работал с этим, да и не по теме несколько.

В контексте компрессии overhead есть – это +3-4% CPU на упаковку/распаковку. Впрочем, как показывает опыт, SQL Server достаточно редко “ест” CPU так, что не найти “лишних” 4%, а уж если мы уткнулись в проблемы с IO – то уж CPU-то гарантировано свободен.
И ещё. К сожалению, ни один из видов компрессии не получится использовать на таблицах, в которых есть sparse columns.

1/7/2010 2:08:05 PM

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

Код выглядел примерно так:

//items is a List<MyItem>
//itemIds is a List<long>

var resultItems = items.Where(x => itemIds.Contains(x.Id));

Можно было делать join, но он в итоге всё равно в нечно подобное выражается, поэтому разницы нет.
Время выполнения этой штуки – 6.7 секунды. Да, там полно элементов, что-то десятки тысяч, но всё же – почти 7 секунд – это неприемлемо долго.

Оптимизировалось всё это дело введением одной строчки кода:

var hashedIds = new HashSet<long>(itemIds); //и дальше вместо itemIds используем hashedIds

В общем-то оно и понятно, что хэш в подобных вещах быстрее.

Время выполнения вот этой строчки (создание хэш-таблицы) – порядка 100 милисекунд.
Время выполнения выборки – порядка 40 милисекунд.

Итого – 150 милисекунд вместо 6.7 секунд. Неплохо :)

P.S. Поковыряйтесь в профайлером – много интересного найдёте. Я нашёл :)

11/18/2009 9:52:42 PM

Интересно здесь. Не то, чтобы совсем, но неожиданно.

Майкрософт объявили направление: веб-приложения, которые (одинаково) доступны на лаптопах, десктопах и мобильных устройствах.
Основной платформой (по крайней мере для UI) объявлен Сильверлайт.

И вот тут начинается интересное, с анонса Silverlight 4.
Silverlight 4 содержит очень много замечательных вещей, начиная с drag-n-drop и заканчивая тем, что теперь он может работать вне пределов браузера, как нормальное приложение.
При этом его можно назначить trusted – и он может иметь доступ к железу, файловой системе, другим веб-сайтам и сервисам, да мало ли.
Например, внутри сильверлайта можно запускать всякие другие вещи. В качестве примера – adobe flash. Ребята запускали youtube на демо.

В общем, похоже, сильверлайт придётся осваивать.

P.S.
Сижу сейчас на секции, посвящённой будущему программирования. Докладчики – их пятеро – рассуждают.. о чём бы вы думали? О распараллеливании, о том, что в функциональных языках этих проблем нет и о том, как с этим быть :) Проблема серьёзно назрела.

11/17/2009 9:41:00 AM

Сегодня весь день слушал workshop по паттернам параллельного программирования.
Мероприятие состояло из трёх частей.

В первой части нам подробно рассказали зачем именно и в каком виде нужно “параллелить”. Какие бывают виды распараллеливания и т.п.
Это была самая сложная часть. В том плане, что я за ночь спал часа три только (а нечего было в самолёте высыпаться), только к утру уснул. Так что жутко хотелось спать. А так как о проблемах параллельности, мультиядерности и т.д. я уже много раз думал, и даже в этом блоге писал три года назад практически то же самое, что они сегодня говорили, то нового для меня в этой части было крайне мало, приходилось буквально заставлять себя держать глаза открытыми и открывать их, если всё же закрылись.

Хотя и из этой “философской” части несколько вещей показалось интересными:

  1. И Майкрософт, и Интел признают, что “затык” и проблема производительности сейчас лежит не на уровне “железа”, а на уровне софта. Представитель Майкрософт сказал прямо со сцены: виноваты мы с вами (все разработчики): мы просто не используем оптимально тех возможностей, которые даёт нам “железо”. А оно без проблем (и даже с меньшими затратами) может дать больше.
  2. Количество транзисторов в каком-нибудь Итаниуме отличается от количества транзисторов в Пентиум-4 примерно в 100 раз.
    Это значит, что вместо того, чтобы выпускать всякие Итаниумы на 2-4 ядра, Интел с тем же успехом и на том же количестве транзисторов мог (и может) выпустить процессор содержащий 100 ядер Пентиума-4. Про 80-ядерный процессор от Интел, впрочем, я тогда же писал.
    Если отвлечься от существующих проблем с софтом и вообразить, что мы можем их равномерно нагрузить – отдача во многом была бы куда лучше, чем от того Итаниума. Но такой процессор не выпускают потому, что мы сейчас не знаем, что с ним делать. Потому, что мы писали и продолжаем писать “однопоточный” софт. Поэтому Интел вынужден делать достаточно малое количество ядер (несколько-то потоков мы всё же делаем, да и несколько приложений одновременно запускаем), продолжая изголяться с частотой и другими параметрами.
  3. Но подвижки всё-таки есть: гибридные ядра вот уже появляются (это когда из четырёх ядер два “нормальные”, а ещё два состоят из микроядер, соотношение может быть 1:3 или 3:1, а могут и все 4 микроядерными быть). А это в свою очередь означает уже совсем другое количество потоков (посчитайте, по два на [микро]ядро хотя бы). Ну и количество ядер растёт – 8 есть, 16 ожидается.
  4. Софт, который пишется сейчас, скорее всего расчитывает продержаться на рынке пару-тройку лет. Даже с такими темпами роста прикиньте, то, что Вы пишете сейчас, способно ли эффективно использовать возможности процессоров, способных исполнять 128-256 нитей в параллели?
    Сколько Вы используете в своём приложении? Максимум три? Вам не кажется это смешным? То, что Ваше приложение будет точно так же тормозить на любой машине? Может пора перестать “изголяться” и пора начать думать и писать “нормально”? :)
    А ведь всё равно каждый, кто прочитает, через 10 минут откроет Visual Studio и продолжит клепать однопоточный код, подумал я. Ибо так проще и думать не надо. И это проблема.
  5. В Майкрософт серьёзно занимаются этой проблемой. Из тех примеров, что я помню: Visual Studio 2010 (которая сама написана на .NET с WPF-интерфейсом) очень активно использует Task Parallel Library (TPL), включёную в .NET 4.0 теперь. Это далеко не единственный пример был, просто я не проспал и запомнил :)

Да, про распараллеливание был ещё хороший пример приведён: докладчик показал последовательно три картинки. На первой был изображён молоток, на второй – воткнутый в доску гвоздь, на третьей – белка. И сказал замечательную фразу: “Когда у Вас в руках молоток, всё вокруг кажется гвоздём. Но не с точки зрения белки.” :)
Сказал он это к тому, что “мы дадим вам молоток, в виде TPL, OpenMP, F# с его immutable типами и т.д, но пользоваться им надо тогда, когда это имеет смысл и так, как это имеет смысл делать”.
По-русски это можно перевести как “дурная голова ногам покоя не даёт” и сказать, что броситься и заменить во всём приложении for на Parallel.For, а IEnumerable на IEnumerable.AsParallel() и “с чистой совестью” думать, что задачи распараллеливания выполнены – глупо.

Вторая часть была гораздо более интересной. Она была дивелоперской.
Человек, который непосредственно из команды разработки TPL, сделал отличный доклад, практически не имея слайдов презентации. Вместо этого он просто открыл Visual Studio и показывал, как делать нужно, как можно, как не нужно, что работает, что нет… Отлично объяснял почему именно и как оно работает, отвечал на вопросы, иллюстрируя ответы тут же кодом, запуская его и демонстрируя результат.
Мне понравилось.
Так, как тема была “паттерны параллельного программирования”, он объяснил, какие паттерны используются при разработке таких приложений, что эти паттерны из себя представляют, как они реализованы в TPL и как пользоваться этой реализацией.

Книжку по этим паттернам можно свободно скачать вот тут: http://www.microsoft.com/downloads/details.aspx?FamilyID=86b3d32b-ad26-4bb8-a3ae-c1637026c3ee&displaylang=en

Третья часть была посвящена Microsoft Windows HPC.
Мне она показалась малоинтересной, за исключением того, что я вообще узнал, что у Майкрософт есть такая версия системы, как HPC. Более того, ожидается вторая версия.
HPC – это аббревиатура от High Performance Computing, а операционная система расчитана на кластеры с большим количеством нод. Текущая версия поддерживает до 256 машин в кластере (я так понял, что не важно, сколько у каждой сокетов и ядер), вторая версия (R2 они теперь взяли моду всё называть) будет поддерживать толи тысячу, толи тысячи, я не понял. Но это всё равно много.
Оттуда можно WCF-сервисы “наружу” выставлять, обработка будет вестись на кластере (или куске кластера, как захочется). И они там тоже использовали те же паттерны параллельного программирования, хотя и без использования TPL, а гораздо на более низком уровне.

Это был просто workshop. Завтра начинается PDC.
Looking Forward.

P.S. Лос Анджелес удивил. Ладно мне коллега и босс говорили, что опасный город, я думал – австралы, не местные, побаиваются. Но когда сегодня обедал с одним местным, он открыл карту метро, провёл пальцем по половине города и сказал: “сюда вообще не надо ездить и выходить на этих станциях”… И потом когда тебе говорят “я в ЛА не использую общественный транспорт, у меня семья, дети, работа, я перемещаюсь или на машине или на такси” – это уже не просто настораживает ;)

11/14/2009 4:09:00 AM

Видимо, моё расписание на PDC будет примерно таким. График достаточно плотный, учитывая  то, что всё начинается в 8:30 утра с Keynotes, потом секции, а после них всякие мероприятия до 9 вечера. Кроме последнего дня, когда всё просто заканчивается в 4:00.

Вообще весь PDC, похоже, приурочен к выходу Windows Azure и большинство докладов связано с ним.
Но, поскольку Microsoft решили ограничить объем базы данных на Azure 10-ю гигабайтами, что для нашего проекта крайне мало… В общем, Azure пока не слишком актуальна и я решил посмотреть “более другие” вещи.

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

Ну, в общем, получается как-то вот так:

Monday
    10:00 AM - 5:45 PM
        Patterns of Parallel Programming: A Tutorial on Fundamental Patterns and Practices for Parallelism

Tuesday
    11:00 AM
        Software + Services Identity Roadmap Update
        OR
        Data Programming and Modeling for the Microsoft .NET Developer

    12:30 PM
        Concurrency Fuzzing & Data Races

    01:30 PM
        Agile – Tales of Triumph, Tribulation, Tools, and Teams
        OR
        Microsoft ASP.NET Futures

    03:00 PM
        Microsoft ASP.NET 4 Core Runtime for Web Developers
        OR
        Evolving ADO.NET Entity Framework in Microsoft .NET Framework 4 and Beyond

    04:30 PM
        Code Contracts and Pex: Power Charge Your Assertions and Unit Tests
        OR
        Behavior-Driven Development vs. Test-Driven Development: What’s What?

Wednesday
    11:30 AM
        Microsoft Perspectives on the Future of Programming
        OR
        Building Data-Driven Applications Using Microsoft Project Code Name "Quadrant" and Microsoft Project Code Name "M"

    01:00 PM
        Microsoft Project Code Name “M”: The Data and Modeling Language
        OR
        Making Microsoft SQL Server 2008 Fly

    02:00 PM
        ADO.NET Data Services: What’s New with the RESTful Data Services Framework
        OR
        Spice Up Your Applications with Windows Workflow Foundation 4

    03:15 PM
        Windows Workflow Foundation 4 from the Inside Out
    
    04:30 PM
        Exception Management – Handling and Reporting Exceptions Effectively

Thursday
    08:30 AM
        Building Hybrid Cloud Applications with Windows Azure and the Service Bus
        OR
        Patterns for Building Scalable and Reliable Applications with Windows Azure

    10:00 AM
        Axum: A .NET Language for Safe and Scalable Concurrency
        OR
        Microsoft Visual Studio Lab Management to the Build Setup Rescue

    11:30 AM
        Workflow Services and “Dublin”

    12:45 PM
        Microsoft Semantic Engine

    01:45 PM
        Application Server Extensibility with Microsoft Project Code Name “Dublin” and Microsoft .NET Framework 4

    03:00 PM
        Automating "Done Done" in the Team Workflows with Microsoft Visual Studio Ultimate and Team Foundation Server 2010

11/11/2009 1:48:00 PM

В нашем проекте мы используем Entity Framework. Разумеется в первой, существующей ипостаси. Штука эта интересная и мощная, но имеет несколько существенных недостатков и неудобностей, среди которых и необходимость наследования всех классов от базового класса EF, и невозможность избавиться от Navigational Properties, и полное отсутствие контроля над поведением (да и интерфейсом) сгенерированных классов, и невозможность юнит-тестирования…

И если с классами немного удалось сладить, написав собственную утилиту-генератор, то вот всё остальное – просто беда. Да, существует такая штука, как TypeMock для юнит-тестирования, но во-первых, она платная для коммерческого использования, а во-вторых, работает через profiling API… Но хоть что-то.

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

Очень приятным моментом для меня стало то, что Navigational Properties теперь из модели можно просто выкинуть.
Обратите внимание на скриншот: в классе Member нет свойства Member_Properties, в классе Member_Properties нет свойства Member. Это я их удалил :) При этом EF по-прежнему знает о связи этих двух сущностей и даже отображает её в дизайнере.

Navigational Properties можно удалить

Ещё одна приятная мелочь: “нормальные” ID-поля для ключей. Смотрите на Member_Addresses, свойства MemberId и AddressId - “обычные” int-свойства. Navigational Properties для Address и Member тоже есть, я их просто не удалял.

Вторым очень приятным моментом стала поддержка “обычных” классов. “POCO-классов”, как теперь модно говорить. POCO – это “Plain Old CLR Object”. То есть, в качестве entity теперь может выступать класс, не унаследованный от “специального” базового класса.
Это очень, очень, очень здорово!

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

Add Code Generation Item

Таких генераторов для модели можно иметь столько, сколько захочется и генерировать совершенно разные вещи.
Я в своём тестовом проекте просто удалил .designer.cs файл тестовой модели и отключил в свойствах .edmx-файла регенерацию. То есть, .edmx-файл теперь для меня просто описывает модель, которая может быть обновлена из БД, содержит все маппинги и т.д, но не генерирует сама по себе никаких .NET-классов.

Вместо этого я добавил Generation Item, который делает всю эту работу. Этот Generation Item представляет собой обычный .tt-файл, то есть, тот самый обычный темплейт, который существует в Visual Studio ещё с 2005-й версии, но о котором мало кто знает :) темплейт представляет собой простой текстовый файл, в котором на простом и понятном языке (например C#) просто и понятно описывается, как и что должно быть сгенерировано.
Вот, их даже два в моём проекте:

Templates Template file

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

В моём случае первый файл генерирует, собственно, контекст и всё, что с ним связано. Второй – сами entities.

Результат работы темплейтов Сгенерированный класс

При этом Entities, как я уже сказал, можно делать POCO-классами и, например, метить их DataContract/DataMember атрибутами, что позволяет использовать их в WCF и избежать часто ненужного маппинга между DTO и Entities. По сути мы имеем возможность сразу из БД получить нужные нам бизнес-объекты и DTO, что тоже очень здорово и очень облегчает жизнь.

Я уже сказал, что темплейты очень легко переделать на свой лад, чтобы они генерировали то, что нужно. Контроль над генерацией и возможность сгенерировать так, как нужно – важная вещь.
Генерируя те же entities можно ещё каких-то атрибутов навесить, или реализовать геттеры-сеттеры так, как хочется, или переопределить ToString(), или ещё методов добавить…

Я вот, задумавшишь о тестировании, добавил несколько строк в темплейт, чтобы он генерировал мне тот самый “TestModel.Context.Interfaces.cs”, который видно на картинке. Изначально его не было.
В этом файле я просто определяю интерфейс для контекста. Ну, ещё я поправил, чтобы класс контекста реализовал этот интерфейс, дописав “, ITestDbEntities” в сигнатуру класса :) 
Этот интерфейс будет используется при юнит-тестировании.

Интерфейс, он простой, смотреть сюда незачем

Предыдущая версия EF использовала в контексте некий класс EntitySet<>, который был абсолютно неудобен в случае юнит-тестирования, новая версия использует ObjectSet<>, который реализует интерфейс IObjectSet<>. А если есть интерфейс – значит можно делать мок! А раз можно делать мок – значит можно писать юнит-тесты!
То, что надо!

Да, нужно отметить, что каждый раз, когда меняется темплейт или меняется .edmx-файл модели, все относящиеся к ней темплейты перегенерируют то, что они там генерируют. То есть, следить за этим совершенно не надо.
Вы просто меняете то, что хотите поменять, остальное делается “само”: обновляются классы бизнес-модели, DTO, возможно классы менеджеров с примитивными методами типа LoadById, Save, какие-то рутинные классы преобразований и хелперов и т.д, и т.п.

Надо будет посмотреть на это дело поближе.

10/31/2009 10:46:00 AM

В Сидней приезжает Тори Амос (Tori Amos). Будет выступать в главном зале знаменитой сиднейской оперы.
Ещё давно, несколько месяцев, наверное, назад были успешно куплены билеты и оставалось только ждать этой даты – 17-го ноября.

Однако, совершенно неожиданно с этой датой совпало проведение PDC (Professional Developers Conference). Точнее, неожиданностью оказалась не дата проведения конференции, а тот факт, что я на неё попадаю. Тем не менее, завтра с утра у меня интервью в американском консульстве на предмет получения визы, и если всё сложится удачно, то на концерт Тори Амос я, к сожалению не попадаю.

На работе смеются – для австрала персональное интервью в консульстве перед поездкой куда-то выглядит крайне забавно и необычно. Интересуются “а зачем”, “а страшно ли”, “а что там спрашивают”, удивляются.

Ничего всё это я уже проходил при получении первой британской визы (вот как раз факт того, что я был в Британии я и забыл указать в анкете, надеюсь простят), на вопрос “let me see the conference program” ответить смогу.

А австралы, кстати, тоже регистрируются где-то по какой-то визовой программе, не просто так едут. Ходить им, конечно, никуда не надо, но, видимо, какая-то электронная регистрация у них всё же есть. Ну а отпечатки пальцев снимать и фотографировать лица будут у всех одинаково :) Ну, за исключением босса – он хотя и не гражданин Австралии оказался, но канадец, а посему въезд в США для него свободный.

Вот. В Лос Анджелесе я ещё не был. Надеюсь, у них там в далёкой Америке есть интернет, напишу, оттуда, что там и как :) Уже договорился, что на время поездки мне выдадут другой, более лёгкий ноутбук…

Но 13.5 часов в самолёте! Это, я вам скажу, похлеще, чем в Австралию из России лететь. Тут придётся честно все 13.5 часов отвисеть в воздухе, тогда как перелёт в Австралию хоть и занимает 17-19 часов, но с отдыхом посередине этого временного отрезка! В прошлый раз было так неплохо поспать ночь в Японии в хорошем таком отеле, который, кстати, предоставлялся бесплатно. А тут 13.5 часов в воздухе. Мрак. Хорошо хоть ночью летим, надо будет не выспаться наконуне.

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

В общем, каждый раз после проведения PDC я смотрел видео доклады в интернете (очень советую) и каждый раз думал, что обязательно надо собраться как-нибудь и съездить. Ибо ролики – это одно, а личное присутствие – совсем другое, что бы там ни говорили те, кто всегда смотрит и никогда не присутствовал, это я по TechEd и ReMix знаю :)

Вот, представился шанс…

10/13/2009 5:01:26 PM

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

Проблемы эти таковы:

  1. При изменении объекта базы данных (процедуры, функции, триггера и т.д.) разработчик создаёт скрипт и помещает его в репозиторий. Всё бы хорошо, но несколько разработчиков могут написать свои скрипты (пофиксить что-то в процедуре, скажем), положить в репозиторий независимо.. И “победит” последний. Причём узнаётся об этом обычно в самый ответственный момент: на этапе тестирования (если не позже). Проявляется баг, разработчик недоумевает “я же пофиксил его, вот и скрипт есть!”. А то, что одни-двумя изменениями дальше идёт совершенно иная копия – как же это проверишь.
  2. Вышеописанная проблема многократно усугубляется в случае использования веток (бранчей).
    Так, как в разных бранчах делаются разные куски функциональности, то, во-первых, достаточно сложно поддерживать “сквозную” систему нумерации. Либо при каждом слиянии бранчей нужно как-то “перенумеровывать” скрипты, но тоже никогда не скажешь, какой именно должен быть порядок в итоге.
    Кроме того – та же проблема: в одном из бранчей поменяли процедуру, в другом – тоже. Скрипты перенумеровали, залили в репозиторий – а не работает.
    При этом очень сложно сказать, какая же версия была “в оригинале” (базовая), какая являет собой изменение в одном бранче, какая – в другом. Это же всё разные файлы, да и их ещё поискать.

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

На замену пришёл новый Database Project для Visual Studio 2008: http://www.microsoft.com/downloads/details.aspx?FamilyID=bb3ad767-5f69-4db9-b1c9-8f55759846ed&displaylang=en

Его нужно скачать, установить – и вроде как все проблемы решены :)

Краткое описание того, что мы имеем, используя эту штуку.

Все объекты БД представлены в виде скриптов.

На этапе инициализации проекта ему нужно указать БД, из которой он “заскриптует” все элементы схемы: таблицы, представления, процедуры, очереди, индексы, ключи, роли.. всё-всё-всё, даже CLR-сборки, если таковые имеются.
В результате разработчик получает дерево, по своей структуре очень похожее на то, что мы привыкли видеть в Management Studio, но все элементы – в виде create-скриптов.

Проект “умеет” отслеживать ошибки на этапе “компиляции”.

Это очень удобно! Например, если мы попытаемся создать скрипт для индекса по несуществующей колонке, создать view для несуществущей таблицы или что-то ещё “этакое” (да просто допустим синтаксическую ошибку или опечатку), то мы увидим ошибку “компилятора” (так же, как мы видим в случае ошибок в коде). Понятное дело, что пока в проекте есть ошибки, изменения в БД применяться не будут.
Что характерно, проект разбирает скрипты и зависимости между ними самостоятельно, а не просто пытается выполнить SQL-код и перехватывает ошибки.
Так с предупрежденями.

Возможности рефакторинга.

Если честно – много не пробовал. Но оно там есть. Например, переименование таблицы или столбца. Просто кликаем правой кнопкой мыши на элементе в дереве, выбираем Refactor –> Rename – и готово. Будет переименовано везде.

Использование макро-переменных.

Скрипты – они, конечно, обычные SQL-скрипты, но в них можно (если захочется) использовать макро-переменные в виде $(DatabaseName). Значения этим переменным можно задать в конфигурации проекта, далее эти макросы будут автоматически подменены там, где они встречаются.
Зачем? Ну, я не знаю :) То же имя базы данных (или linked-сервера). Тот же логин пользователя. Словом, то, что может отличаться от среды к среде (у разработчиков – одно, в продакшн – другое).

Ещё много всякого…

…в виде определения изменений между проектом и базой данных, возможностями юнит-тестирования и т.д.

Как это работает.

Процесс примерно такой:

  1. Мы говорим проекту “Deploy”.
  2. Происходит Rebuild проекта. На этом этапе делаются все проверки на ошибки, предупреждения и т.д. В том случае, если ошибок нет, идём дальше.
  3. Происходит сравнение схемы базы данных, в которую мы делаем deploy cо схемой, которая присутствует в нашем проекте в виде кучи create-скриптов. По результатам этого сравнения генерируется дельта-скрипт, который содержит уже всевозможные alter, delete, create и т.д.
    Надо сказать, что дельта эта генерируется очень качественно. Мы тестировали на достаточно сложных комбинациях – всё проходило “на ура”. Проблем не было ни разу.
  4. Дельта-скрипт применяется в базу данных.

Кстати, применять дельта-скрипт вовсе не обязательно, можно “сказать” проекту, что этот скрипт нужно просто сгенерировать.
Вообще настроек у проекта достаточно много: ANSI-ключи, нужно ли удалять объект, если он пропал из схемы проекта, нужно ли делать бекап перед применением дельта-скрипта и т.д, проще посмотреть.

Да, это всё DDL.
Что касается DML: тут всё совсем примитивно просто: есть два раздела: PreDeployment Scripts и PostDeployment Scripts. Туда (обычно в Post-, разумеется) и предполагается складывать скрипты для DML. Единственное, за чем надо следить – так это за тем, чтобы скрипты проверяли, нужно ли делать то, что они хотят делать. Ибо все они будут исполняться каждый раз при обновлении БД из проекта. Но это, как и понятно, сделать очень и очень нетрудно и вообще best practices :)

Автоматическое развёртывание.

Поскольку всё делается через msbuild, все targets уже на месте, то нет проблем ни с каким билд-сервером, ни даже без него (msbuild можно и из командной строки запустить). Словом, всё точно так же, как и с любым “кодовым” проектом - “на ура” автоматизируется ночной билд-деплой, например.

Плюсы.

Для нас – очень существенные. Судите сами: каждый объект -  это просто текстовый sql-файл. Как и любой другой файл с кодом, он отлично “уживается” в системе контроля версий, если два или более разработчика правят один файл – они будут иметь нормальный “человеческий” конфликт при чекине (или при слиянии веток) на этом файле и будут иметь все возможности грамотно этот конфликт разрешить.

Кроме того, правка таблиц, например, становится гораздо более лёгким делом, так как не приходится задумываться об ALTER TABLE, проверке на наличие (дабы не свалиться с исключением при попытке добавить то, что почему-то уже существует) и т.д. Нужен новый столбец: просто дописал его в CREATE-скрипт для таблицы – и всё. Остальное проект сделает за тебя.

Минусы.

У нас база данных – большая. Поэтому сравнение идёт достаточно долго. Долго – это где-то от 5 до 10 минут.
Хотя тут тоже не всё понятно. Сравение идёт долго на UAT-сервере, а вот локально, на той же БД (backup/restore) – одна минута пятнадцать секунд на весь деплоймент. Так что тут нужно ещё смотреть, нет ли у нас каких-то инфраструктурных затыков.

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

Словом, будем теперь так жить.

8/4/2009 4:46:08 PM

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

Поэтому напишу то, что навеяло “Я встречал такую ситуацию, когда программисты не хотят чтобы их код тестировали!”. Последние четыре года с тестированием в тех местах, где я работал, всё, можно сказать, нормально, поэтому немного поудивляюсь и опишу процесс.

Удивляться я буду нескольким вещам. Во-первых, как так – программист не хочет, чтобы его код тестировали? Может он еще и код-ревью проходить не хочет?? (шутка).
На самом деле, конечно, программист в тестировании заинтересован. Ибо это – вариант спихнуть часть работы на кого-то. Ведь в противном случае код либо придётся тестировать самому, либо краснеть за глупые баги в релизе.
Тестировать самому для программиста – пытка. К тому же, программист, я считаю, просто неспособен нормально протестировать приложение. Во-первых, он уже просто подсознательно производит действия, которые являются правильными. Например, программисту не придёт в голову полезть в XML-файл с данными, руками поправить там что-то, а потом попытаться с ним работать. Или заполнить весь жёсткий диск и посмотреть, как будет вести себя приложение при попытке автоматического сохранения состояния. А тестеры это делают (реальные примеры, это нас в Алкателе тест-команда такими вещами удивляла).
А во-вторых, он в основном не является конечным пользователем продукта и поэтому не в состоянии оценить и отработать нормальное пользовательское поведение, с чем тоже выходят иногда курьёзы.
Ладно, расскажу один – и пойдём дальше :)

На прошлой работе делали мы вторую версию некого существующего инструмента, в котором была интегрирована *nix-консоль. Консоль эта использовалась там для отображения прогресса того, что происходит на удалённой машине. Цветная вся такая.
И вот в один прекрасный день приходит нам толи от одного из пользователей, толи от тестеров пожелание: дескать, консоль эта работает нестабильно в такой-то ситуации, внимание, если вводить там команды. Типа, поправить бы в новой версии.
Упс, подумали мы и спросили тимлида. Он удивился и сказал: консоль точно не предназначалась для того, чтобы что-то в ней ещё вводить! Даже непонятно, как это можно сделать: она ж read only!. Решили узнать в другой команде, которая занималась написанием самой платформы. Тимлид ушёл, через пару минут вернулся с круглыми глазами. Рассказывает: подхожу к тамошнему инженеру, спрашиваю – команды вводишь в консоль? Ага, ввожу, говорит! Очень удобно – если что-то свалилось – тут же подправил, не надо логиниться, окружение поднимать! Только, говорит, я думал, что я один это знаю. И да, говорит, а можно подправить заодно, чтобы оно не падало? :)
Оказывается, в случае ошибки, когда вывод в консоль останавливался, она толи переключалась в том инструменте из режима read only, толи какую-то комбинацию клавиш нужно было нажать… Сейчас не помню, а исходников первой версии никто не видел, да и смотреть не хотел :) Но каким-то образом это сокральное знание расползлось по всему зданию (а может и дальше) и достаточно много чуваков пользовались такой фичей, считая, что они одни знают как это делать. И никто не жаловался на баги, потому, что знали, что это типа хак.
Вот так хак стал требованием :)

Помимо же явной заинтересованности я не вижу, как программист вообще может повлиять на процесс тестирования. Ну, разве что там существует какой-нибудь “руководитель”, с которым можно договориться, и который “волею, данною мне…” прикажет тестерам не тестировать определённый функционал ;) Другого пути я не вижу…

Расскажу про жизненный цикл разработки и место тестирования в нём на примере нашего веб-проекта.

Что мы имеем:

  1. Основную ветку (Trunk) в системе контроля версий (про ветки я уже писал: Про бранчи);
  2. Main Build с Main Build Agent – определение автоматического билда для интеграции и проверки кода;
  3. UAT Build с UAT Build Agent – определение автоматического билда для UAT;
  4. UAT Environment – неполный комплект серверов + база данных UAT (неполный потому, что всех типов серверов по одному);
  5. Staging Environment – полный комплект серверов + staging база данных (полный потому, что зеркалирует следующую конфигурацию);
  6. Production Environment – полный набор;

Что происходит:

  1. В соответствии с принятым в команде соглашением, любой новый функционал разработчики делают в отдельных ветках. Забавно: ветки обычно называют шуточным именем своей sub-команды или названием фичи, которую делают. Например, у нас была ветка “Team3G”, ветка “Old Skool” и ветки типа “FormPropsRefactoring”, etc.
  2. Когда новая функциональность готова, делается merge в Trunk.
  3. Каждый check-in в Trunk автоматически запускает Main Build, который забирает код из системы контроля версий, собирает его в релизной конфигурации и прогоняет юнит-тесты. Если что-то пошло не так, то он (Main Build) начинает паниковать и слать письма о том, что в результате такого-то check-in таким-то пользователем код перестал собираться и тесты не проходят.
  4. Ночью, а конкретно в 3:30AM, по расписанию срабатывает UAT Build. Этот зверь суровее: он тоже берёт последнюю версию, собирает и запускает юнит-тесты. Если всё прошло успешно, то он минифицирует и комбинирует javascript/css, автоматически заливает новую версию продукта на все UAT-серверы, синхронизирует базу данных (об этом тоже писал: Синхронизация баз данных), выполняет ряд служебных функций. После этого UAT готов к работе.
    В случае невозможности обновления UAT паники больше: письма идут и разработчикам, и тестерам, и менеджерам, которые используют UAT для “а! они это сделали! хочу посмотреть!”, и специалистам по требованиям (не буду называть их ПиЭмами, чтобы не было путаницы: они программистами не руководят).
    UAT Build может быть так же запущен руками любым членом команды в случае какого-то срочного багфикса или просто по договорённости с тестерами.
  5. У тестеров есть какая-то своя тулза или планировщик, который, насколько мне известно, запускает ночью автоматизированные веб-тесты. Они хвастались, что у них уже в районе 400 веб-тестов есть. Ну, это которые умеют работать с продуктом в браузере, извне. Логинятся, делают там чего-то.
  6. Утром приходят тестеры. Хитро ухмыляясь они смотрят на то, как прошли веб-тесты. Потирая ладошки они смотрят на ряд задач, которые были программистами помечены как законченные (Done). Закончен – значит ночью попал в UAT, думают они, щас мы его… И приступают к своей работе.
    Здесь я должен отметить: хочет программист, или не хочет программист, статус Closed задаче может поставить только тестер. И сделает он это только тогда, когда лично убедится, что штука работает. Ведь под статусом будет стоять его фамилия ;) Шучу. Но фамилия стоять будет. Если же штука не работает, то тестером в системе регистрируется баг, которому присваивается приоритет – и пошёл в работу. А задачу обратно в статус невыполненных.
  7. Когда тестеры довольны тем, что происходит на UAT и когда пришло время по мнению специалистов по требованиям, протестированный продукт заливается из UAT на Staging. Там – копия реальной конфигурации. Там работают маркетинговые ребята, там проводятся демонстрации. Там же работают бета-пользователи. Там же продукт тестируется снова для того, чтобы убедиться, что всё будет работать в production. Разработчики туда доступа уже не имеют.
  8. Когда все довольны тем, как работает staging, код из него переносится в production.

Вот и всё. В идеале перед пунктом 2) должен идти пункт 1.5), в котором будет сказано: “Когда разработчики считают, что задача выполнена, устраивается код-ревью изменений, внесённых в ветку”.

Отсюда видим:

  • Разработчик никак не может повлиять на то, кем, как и когда будет тестироваться его код. Лично я – не могу :)
  • Код обязательно будет протестирован, так как он выполнен в рамках какой-то задачи, а задача может быть закрыта только тестером.
  • В случае, если задача выполнена не полностью с точки зрения тестера, она будет возвращена команде разработчиков обратно, плюс может быть зарегистрирована дополнительная задача-баг, имеющая собственный приоритет (так, основная задача может иметь низкий приоритет, но если в результате её имплементации пострадало что-то другое, тестер может присвоить высокий или даже критический приоритет багу).

Powered by BlogEngine.NET 1.6.0.0

About the author

Alexey Raga Alexey Raga
.NET software developer.

E-mail me Send mail

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2010

Sign in