7/2/2010 2:14:03 PM

Завтра работаю, финальная миграция с TFS 2008 в одном домене на TFS 2010 в другом домене. В последнем случае у меня будет ещё 4 физических сервера для build agents, думаю, что build controller будет жить на одном из них, чтобы не грузить саму TFS-машину.

На сегодняшний день (не мною) было отсмотрено и оттестировано достаточное количество process templates, включая MS SCRUM v1.0, MS Agile, MS CMMI/MFC, ScrumForTeamSystem v3.0

"Победил" последний - ScrumForTeamSystem v3.0, который, хотя поначалу и кажется "перенавороченым", но вот потыкавшись там, понимаешь – ба, тут того нет, тут – этого… Причём вещи-то полезные.

Словом, Царина (человек, которому с этим всем работать) смотрит видео с PDC (мы с ней, кстати, были на той сессии Winking smile) и прочих конференций, посвящённых SCRUM, а я завтра “нажму кнопку”.

Надеюсь успеть быстро.

Карма в действии. В Акателе я TFS мигрировал два раза, тут тоже…

Tags:

Programming | Other

6/7/2010 3:06:47 PM

По поводу предыдущей заметкипро FlowChart.

Как совершенно правильно заметил Паша в баззе, проблема всего этого великолепия с “долгоиграющими” workflow заключается в том, их нельзя менять.

При попытке что-то добавить или удалить запущеные и сохранённые в БД экземпляры просто не будут оттуда загружаться, а Workflow Runtime будет выдавать страшное сообщение о том, что “дерево Activities изменено, и поэтому состояние не может быть восстановлено”.

Как это решается? А никак. Просто никак. Официально предложен следующий механизм решения конфликта: името параллельно несколько версий и настроить роутинг WCF-сообщений. Чтобы сообщения, содержащие контракт “v1” доставлялись на сервис V1, а сообщения, содержащие контракт “v2” доставлялись на сервис V2.

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

Далее предлагается поступить так, как в КВН у команды Астаны (Казахстан) было: “Ну их!” :)
То есть, “старые” процессы дорабатывают по старой схеме, а те, которые начинаются новые – идут по схеме новой.

Иногда это не лишено смысла, допускаю. Однако, что делать, если нужно банально исправить ошибку? Особенно, если у нас имеются “бесконечные” workflow, как, например, документы в системе: они просто переходят из одного статуса в другой, туда, обратно, раз в два месяца…
Ответа на этот вопрос, видимо, не существует :)

Как вариант можно попробовать (только мысли, сам не ковырялся) написать свой вариант сохранения/загрузки workflow в/из БД.
Делается это, вроде бы, совершенно не сложно: надо просто создать своего наследниеп System.Runtime.DurableInstancing.InstanceStore. А вот насколько будет сложно там, внутри, разобраться, что данные у нас от V1 и построить на основе них V2 – непонятно, надо пробовать :) Наверное, однозначно ответить нельзя: когда-то это сделать легко, а когда-то и невозможно просто потому, что данных не хватит…

Пример того, как сделать собственную “сохранялку” в XML есть в библиотеке примеров для WF и WCF, здесь.

Паша ещё предложил как вариант делать stateless workflows. Чтож, вариант, хотя и очень-очень сильно компромисный и не всегда приемлемый. К тому же, в WF4 нет State Machines, и как “сказать” Workflow, что сейчас она находится не вот в этом состоянии, а вон в том – я не знаю. Допустим, она “висит” на Delay где-нибудь в середине. Или на ждёт, что кто-то “дёрнет” WCF-метод.

По поводу сохранения ещё, раз уж Паша эту тему тоже затронул.
В WF4 это тоже переделано относительно 3.5. Теперь Workflow Runtime не сохраняет сам Instance, а сохраняет только некие его состояния, переменные, в результате чего объёмы получаются гораздо меньше, нежели они были раньше. Что, в принципе, правильнее и логичнее. Но вот поднять именно код из сохранённого состояния больше не выйдет.

По поводу ошибок теперь.
В WF4 есть activities для транзакций и обработки ошибок (try..catch). То есть, всё можно делать непосредственно там – о отлов, и компенсацию, и обработку.
Если же всё-таки где-то недосмотрели и “выпали” в событие UnhandledException, то в этом случае мы “теряем” только то состояние, которое находилось на момент ошибки в памяти. То есть, в следующий раз workflow будет продолжена с момента последнего сохранения в БД.
Кстати, есть там ещё замечательная Persist Activity. Понятно, что она делает ;) Так вот, сначала Persist, потом делаем перевод денег :) Если что-то не получилось – в следующий раз выполнение продолжится с того же самого места, как и было указано в Пашином примере.

Но с версионностью – засада, конечно. Надо ещё будет попробовать, возможно ли её разрешать автоматически хоть в простых случаях… Тогда, может быть, комбинация stateless (or minimum state) + такая вот собственная сохранялка хоть как-то сможет помочь…

6/6/2010 2:39:02 PM

Два дня потратил на то, чтобы более-менее разобраться в Workflow Foundation 4-й версии.

Из новых замечательных возможностей – FlowChart диаграмма и возможность выставить содержащую её Workflow в сеть в виде “обычного” WCF-сервиса.

Сама по себе FlowChart диаграмма очень удобна, прежде всего своей наглядностью (сразу понятно, как протекает процесс), а так же тем, что практически в точности отражает те блок-схемы, которые вырисовываются на досках и бумажках при продумывании схемы работы. Наиболее счастливые разработчики сразу получают задание от специалиста по требованиям в виде примерно такой вот схемы с фразой “я хочу, чтобы было так!” в заголовке :)

Программисту остаётся только открыть Visual Studio, выбрать там проект, который называется “WCF Workflow Service Application”, и начать определять свой сервис в файле Service1.xamlx (лучше переименовать, или создать новый ;))

Я попробовал сделать небольшой сервис по обработке tickets (извините, я буду писать “тикетов”, так как не сумел придумать нормальный перевод).
Система примерно такая: тикет регистрируется в системе, после чего может изменяться до тех пор, пока не перейдёт в состояние Cancelled или Closed.
При этом он может попасть в состояние WaitingForCustomer. В этом состоянии он может ожидать неделю. Если клиент ничего не ответил, то тикет закрывается.

В результате получается что-то вроде вот такой диаграммы:

FlowChart 

Блок “Receive and Update Ticket” – это просто Activity, определённая в отдельном .xaml-файле.
Он отвечает за то, чтобы получить тикет посредством WCF-метода, сохранить его где-то, возможно, изменив, и отправить изменённый тикет обратно (для отображения).
Выглядит это так:

 Получение тикета через сервис

Здесь я просто “перетянул” мышкой в дизайнере ReceiveAndSendReply Activity, в результате чего образовались два блока, отмеченные стрелками. Первый – это получение WCF-сообщения, второй – возвращение значения.
Между получением значения и возвращением результата можно делать какие-то действия. Любые, в общем-то. Можно туда ещё один workflow вставить, который будет что-то важное делать, подготавливая данные к отправке, например, ещё одну FlowChart диаграмму :)
В моём случае всё предельно просто: я только добавляю запись в коллекцию History у тикета и возвращаю его обратно. Хотя, неплохо было бы сначала изменить историю, а потом уже сохранить ;)
Понятно, что если значение возвращать не надо, то можно воспользоваться просто Receive Activity.

Важно здесь то, что прямо в этих avtivities указывается имя метода (ProcessTicket), тип контракта, который будет принимать/получать метод (или можно указать кучку параметров) и что этот метод будет возвращать (это в SendReplyToReceive). Всё остальное будет сделано автоматически, метод станет виден WCF-клиентам как самый обычный WCF-метод.

Конфигурация WCF-метода.

Здесь MessageType = Ticket – это тип принимаемого сообщения (я принимаю Ticket), в Message data = _ticket – это имя переменной, куда я хочу положить принятый результат.

А ещё, поскольку мы получаем данные, производим над ними работу, а потом отправляем результат назад, нам нужно обеспечить для каждого такого процесса свой собственный, выделенный экземпляр workflow. Сделать это просто: нужно в свойствах Receive Activity включить галку CanCreateInstance. Впрочем, если Вы забудете это сделать, то при запуске Вам напомнят, да.
Итак, после этого у нас каждый тикет будет обрабатываться собственным экземпляром workflow. Что нам, собственно, и надо, ибо жизненные пути тикетов различны…

LogTicketInfo Activity я описывать не буду, скажу только, что это просто класс, activity, унаследованная от CodeActivity и она не делает ничего полезного, кроме Debug.WriteLine(..).
В WF4 предполагается наследоваться от CodeActivity и CodeActivity<T> для выполнения каких-то программных действий, либо от NativeActivity и NativeActivity<T>, если нужно как-то при этом взаимодействовать с Workflow Runtime. Generic-версии используются в том случае, если нужно вернуть какой-то результат заданного типа.

Блок, отвечающий за ожидание ответа клиента в течение недели выглядит так:

Ожидание ответа клиента в течение 7 дней

Тут я использовал стандартную Pick Activity для выбора одного из “направлений движения”. Их у меня всего два: либо клиент ответил, и тогда ничего делать не надо (анализируем новый статус как обычно), либо клиент НЕ ответил в установленный срок, и тогда закрываем тикет.

Кстати, для получения ответа клиента я использую ту же самую activity, что в самом начале, то есть, в моём случае, используется тот же самый WCF-метод. Что уже, в принципе, должно навести на мысли… ;)

Ну вот, а после получения тикета и записи лога (см. flowchart, первую картинку) всё стекается в блок Switch по полю Status тикета, который и решает, что мы должны делать дальше.

Казалось бы – должно работать! И если мы запустим проект и откроем любимый WcfTestClient.exe, то увидим, что оно и работает! Но если поиграться чуть дольше (или догадаться чуть раньше), то станет понятно, что работает оно неправильно.

Неправильно по двум причинам:

  1. Все Workflow, находящиеся в статусе отличном от Cancelled или Closed, висят в памяти неопределённое время (пока ресурсы не кончатся).
  2. Сколько бы мы ни пытались сказать, что тикет такой-то переходит из состояния WaitingForCustomer в любое другое – мы всё время попадаем в начальный блок “Receive And Update Ticket”, а не в тот, что находится в “Wait For Customer”. Иначе говоря, каждое обращение к методу у нас фактически активирует новый процесс, так как наш сервис не понимает, что это тот же самый тикет продолжает свой жизненный путь, а не просто новый “с неба свалился”. Таким образом, экземпляры всё копятся и копятся в памяти.

Самое время решить эти проблемы.

Первую решить довольно-таки просто и очевидно: воспользоваться стандартным persistence service, который уже есть в WF4. Сделаем так, что если workflow не активна, то она сохраняет себя в базе данных SQL Server и не занимает места в памяти и прочих ресурсов. Там, в базе, она может лежать годами, и никому от этого плохо не будет.
Для этого нам всего лишь нужно подготовить таблицы в БД, выполнив последовательно вот эти два (отмечены галочками, в том же порядке) скрипта:

Схема БД

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

Далее нужно просто пойти в web.config файл и прописать там следующие строки (понятное дело, строка подключения к БД у каждого своя):
Web.Config

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

Хорошо, что в .NET 4.0 всё, что только можно, уже прописано в “базовые” конфигурационные файлы :) Все конфигурационные файлы стали на порядок легче. Поэтому он уже “знает”, что такое эти два тега и может их использовать.

Со второй проблемой несколько сложнее.
Она ведь в чём состоит, проблема-то? Состоит она в том, что для обработки каждого тикета Workflow Runtime создаёт отдельный экземпляр workflow, но когда в метод ProcessTicket приходит “тот же самый” тикет, Workflow Runtime снова создаёт экземпляр, вместо того, чтобы использовать уже существующий.
Почему это происходит? Да потому, что Workflow Runtime просто не знает, что значит “тот же самый”. Как только она научится это знать – так сразу всё встанет “на свои места”.

Такой процесс “узнавания” называется “кореляцией” (correlation). Иными словами, мы должны провести кореляцию между нашим тикетом и экземпляром workflow.
Делается это тоже несложно: в Workflow для этого предусмотрен специальный тип переменной, называемый CorrelationHandle.

Так, как я использовал ReceiveAndSendReply Activity, то он создал мне такую переменную автоматически.
Я не буду тут отвлекаться на различные аспекты кореляции, (коих несколько, и есть дажа пара activities на данную тему), это отдельный разговор, здесь мы просто решим “насущную” задачу: укажем, что кореляция проводится по идентификатору тикета.

Для этого достаточно пойти в свойства Receive Activity, кликнуть там поле CorrelatesOn и указать нужный нам correlation handler (был создан автоматически, смотрите список переменных) и параметр для кореляции (Id):

Кореляция по полю Id 

В результате получится что-то такое:

Опять кореляция

Это означает, что когда в наш сервис в метод ProcessTicket приходит сообщение, то происходит его анализ по полю, указанному вот в этом вот XPath выражении, что было построено автоматически. Если значение этого выражения совпадает с каким-либо экземпляром workflow, то это экземпляр будет “поднят” из базы данных и это сообщение и будет обрабатываться именно этим экземпляром, что нам и нужно было сделать :)
Поэтому, когда в экземпляр, ждущий ответа от клиента, приходит такое сообщение, он продолжает свою работу именно с блока “Жду клиента”, а не попадает в начало процесса.

И, вроде бы, всё должно работать. И всё будет работать! :) И всё будет хорошо!

Но недолго. Кто знает, до каких пор? ;)

5/11/2010 3:56:43 PM

В прошлый раз я написал целую инструкцию по поводу того, как обновить TFS до 2010, да на новой машине, да в новом домене. Однако, ряд вопросов остался не решённым, в частности не работали репорты и документы, кроме того, пользователи не имели доступа к Sharepoint порталу проектов.

Проблема оказалась в том, что “TFSConfig identities” по всей видимости не делает remap для пользователей, прописанных в Sharepoint и в Reporting Services.

А для “старых”-то пользователей там, безусловно, всё есть.

Мигрировать пользователей в Sharepoint можно, видимо, двумя способами:

  1. С помощью команды “stadm –o migrateuser”. Делается это так:
    stsadm –o migrateuser –oldlogin LH\alexey.raga –newlogin INCITE\alexey.raga –ignoresidhistory
    Засада здесь в том, что stsadm не имеет опций для пакетного апдейта, поэтому каждого пользователя придётся мигрировать вручную. Сочувствую, если у Вас их сотни :)
    Ключ “ignoresidhistory” в документации (что я видел) описан очень невнятно, что он точно делает я не понял, но без него пользователь не мигрируется :)
    Для себя я решил, что поскольку это новый сервер с новой (чистой) инсталяцией Sharepoint, то использование этого ключа ничего лишнего ни дописать, ни удалить в правах пользователей не сможет. Ибо никаких прав для тех, кто там в –newlogin никогда задано не было.
  2. Да просто пойти в табличку UserInfo и сделать UPDATE туда.

Я делал всё через stsadm. Во-первых потому, что кто его знает, что он ещё делает, кроме обновления вышеуказанной таблички (есть овнования полагать, что ничего), а во-вторых некоторые имена пользователей не совпадают (был OLD\vpupkin, стал NEW\vasya.pupkin), поэтому просто сменить имя домена недостаточно, всё равно приходится обрабатывать таких вручную.
Ну и, как я говорил, это у меня тестовая миграция на тестовые серверы, с тем, чтобы всё проверить и сказать “Да! Мы можем это сделать!”.

А вот с Reporting Services грустнее.
Там, в базе, в таблице Users, хранятся не только имена пользователей, но и их SIDы. Понятное дело, что SIDы у “новых” пользователей будут иные, так что просто обновить имена недостаточно.
Утилиты для такой миграции я не нашёл. Понятно, что можно написать, или можно сделать вручную, но как-то это не так.
Поэтому, если у каких-то пользователей были почему-то заданы какие-то права в RS (чего в случае нормальной эксплуатации TFS делать, в общем-то, не приходится обычно), то права проще всего перенастроить заново.

А, да. Ещё темплейт проекта TFS.

Мы используем темплейт SCRUM от http://scrumforteamsystem.com/Products, версию 2.1.
Возможности обновить темплейт на проекте нет.
Как вариант – создать новый проект с нужным темплейтом. Если делать так, то TFS даже спросит, сделать ли новый пустой проект, либо же сделать ветку от другого проекта.

Сделать ветку, наверное, самый правильный вариант: сохраняется всё состояние на момент “до миграции” в нетронутом виде.
Однако, возникает неудобство в том случае, если в исходном проекте уже используются ветки. То есть, если в исходном проекте есть ветка A и от неё сделана ветка B, то после ветвления в новый проект мы получим ветки A1 и B1, где A1 есть потомок A, в B1 – потомок B.
Получается, что если в исходном проекте ветки A и B можно сливать друг в друга (B потомок А), то в результирующем – уже нет (B1 – потомок B, а не A1).
Да, в TFS2010 есть такая замечательная штука, как reparenting для веток, но тогда мы теряем связь с реальностью исходным проектом..
Надо попробовать :)

Сделать пустой проект – тоже вариант. В том смысле, что можно просто переместить код из предыдущего проекта в новый. Move. И всё. Все зависимости сохранятся, все workitems, к которым были “привязаны” предыдущие checkins – тоже.

В любом случае, у ребят из http://scrumforteamsystem.com/Products пока ещё нет релиза 3-й версии темплейта, так что, ждём.
Я, кстати, спросил у них: “Раз уж вы делаете шаблон для SCRUM, то, наверное, вы и сами используете SCRUM, следовательно, можете приблизительно сказать, когда же?
На что они ответили: “Да шаблон-то давно готов. Не готов guidance для него, а шаблон содержит ссылки на этот guidance. Так что, подождите немного”. Вопрос с датой обошли.
Вот, ждём.

Админы готовят “настоящие” серверы для “настоящей” миграции. А там уже и будем наступать на грабли :)

5/7/2010 3:03:25 PM

Взялся обновлять TFS, пока на тестовом сервере, чтобы проверить, что всё будет работать.

Я уже писал, что сама по себе установка TFS теперь – дело очень простое, оно даже умеет работать без Sharepoint Services, а если всё же хочется, то умеет установить и настроить их само.

Обновление с предыдущих версий TFS – тоже, в принципе, не проблема, теперь это можно сделать прямо в процессе установки (хотя предварительно и сделать кое-что руками).

Но в нашем случае обновление – это комбинация сразу трёх задач:

  1. Переезд на новый (физический) сервер;
  2. Переезд в новый домен;
  3. Собственно, обновление TFS.

Оказалось, что и это сделать не так страшно, как оно казалось.

Итак, для этого потребовалось:

  1. Сделать backup всех баз данных с TFS2008, включая базы ReportingService и Sharepoint. Для Reporting Services, похоже, даже encryption key не надо бекапить и потом восстанавливать;
  2. Установить то, что требуется для TFS2010 на новый сервер, а именно (пошаговая инструкция, куда ткнуть и что написать есть в прилагаемом TFSInstall.chm, очень советую смотреть туда):
    • SQL Server Standard 2008 SP1 (да, оно больше не работает с SQL 2005. Можно даже Express-версию, если но там нет Analysis Services, а без него репорты не будут работать, но если они не нужны – то и отлично, можно вообще Reporting Services не ставить);
    • Sharepoint Services 3.0 SP2 (да, оно больше не работает с 2.0. Можно обойтись и без Sharepoint, и даже Web Access работать будет. Чего не будет, не помню, смотрите прямо в инсталлере, там всё написано. Ценители могут воспользоваться MS Sharepoint Server 2007, тоже поддерживается).
      Опять отсылаю к TFSInstall.chm, там сказано, какой порт указать для административного портала;
    • IIS, понятное дело.
  3. Восстановить бекапы из пункта 1) на новом сервере;
  4. Если устанавливали Reporting Services, то пойти в Configure Reporting Services и “починить” там всё:
    • Выбрать только что восстановленную базу данных на вкладке Database;
    • На вкладке Report Manage URL задать имя виртуального каталога “Reports” и нажать Apply. Если кнопка задизейблена – попечатайте что-нибудь в текстовом поле, чтобы она стала доступной :)
    • На вкладке Web Service URL задать имя виртуального каталога “ReportServer”, тоже Apply его на всякий случай.
    • На вкладке про encryption key удалить этот самый encryption key.
  5. Если ставили Sharepoint Services, то нужно подключить предыдущие коллекции сайтов. Для этого:
    • Пойти в админилку Sharepoint и убедиться, что там есть web site для Вашего http://<servername>:80. Если такового нет – тогда создать (спасибо Мише, подсказал в нужном направлении). Site Collection создавать не надо.
    • Подключить старую базу данных (от имени администратора):
      stsadm -o addcontentdb -url http://<ServerName>/sites -databasename <Wss_ContentDBName> -databaseserver <server\instance>
    • Дать пользователю TFSSETUP, от имени которого вы и делаете всё это, немного прав (от имени администратора):
      stsadm -o addpermissionpolicy -url http://<ServerName> -userlogin <TFSSETUP user> -permissionlevel "full control"
    • iisreset;
  6. Запустить, наконец-то, инсталяцию TFS. В списке типов инсталяции выбрать Upgrade, а там дальше всё будет прозрачно совсем. Только пройдитесь по всем вкладочкам визарда внимательно, там кое-где надо будет логин/пароль для TFSSERVICE указать, например… Сложного ничего нет.
    Вот тут есть в картинках.
  7. Непосредственно перед установкой оно попробует произвести проверку “всё ли правильно сконфигурировано”. У меня показывал два красных креста – на Reporting Services и на Sharepoint. Выяснилось, что я забыл в первом случае encryption key удалить, а во втором – уже не помню что.
    В общем, перед началом обновления всё должно быть зелёным. Ну, в крайнем случае жёлтеньким :)
  8. Пойти пообедать. Попить чаю с баранками, я не знаю. Поскольку данные он будет перегонять физически из всего того немереного количества баз данных, что были сбекаплены, в две всего, скорость напрямую зависит от скорости сервера и винтов. Говорят, что на 50-гигабайтную базу уходит где-то часа три. У меня столько не было, но я и обедать ходил. Вернулся – готово.
  9. Не спешить радоваться ;)
  10. Слазать в TFS Admin Console и убедиться, что URL’ы все нормально там выставлены. Апгрейд на этом закончен.
  11. Переехать пользователей TFS со старого домена в новый (если надо, мне – надо). Это чтобы все права, история, команды и т.д продолжали вестись. Чтобы Вася Пупкин как был Васей, так и остался Пупкиным.
    Для этого:
    • Запустить командную строку от имени администратора, пойти в каталог TFS, где-то в Program Files, там в каталог Tools.
    • Выполнить команду:
      TFSConfig identities /change /fromdomain:<FromDomainName> /todomain:<NewDomainName>
      Эта команда “переведёт” всех пользователей TFS из “старого” домена в “новый”. То есть, LH\alexey.raga станет INCITE\alexey.raga, например.
      НО: только для тех пользователей, которые есть в “новом” домене. То есть, имена должны совпадать.
      Если нужно перевести не всех, а кого-то одного, например, то там есть и такие ключики, смотреть здесь: http://msdn.microsoft.com/en-us/library/ms404883.aspx#MoveAccounts
      У меня, кстати, все перевелись, кроме меня самого. Не знаю почему, все операции я делал под TFSSETUP, то есть, просто совпадение :) Но вот именно мой пользователь не хочет мигрироваться.
    • Там же выполнить:
      TFSConfig Accounts /change /AccountType:ApplicationTier /account:<TFSSERVICE account> /password:<Password>
      Это потому, что TFSSERVICE-то теперь другой у Вас.
    • Если ставили Reporting Services, то там же выполнить:
      TFSConfig Accounts /change /AccountType:ReportingDataSource /account:<AccountName> /password:<Password>
    • Если ставили прокси (а его кто-то ставит?), то то же самое, но для /AccountType:Proxy и его аккаунта.
  12. Пойти в админилку Sharepoint, пробежаться по Site Collections и посмотреть, чтобы Primary и Secondary админы были правильными пользователями, а не старыми, из старого домена. У меня почему-то было так.

После этого, в принципе, TFS должен работать. Вы можете делать чекауты, чекины, история вся хранится, словом, работать можно.

Какие проблемы.

  1. У меня пока не работают репорты. Но я даже ещё не смотрел, в чём там дело, отчего-то папка с репортами вообще недоступна в Team Explorer. Может что-то с правами, может от того, что темплейт у проекта был нестандартный, не майкрософтовский.
  2. Никто из разработчиков не имеет прав на Sharepoint Portal проекта. В Team Explorer нет доступа в папку Documents. Это точно что-то с правами.

С этим разберусь в следующий раз и отпишусь.

P.S. Честно говоря, я не понимаю.. Если у TFS2010 и так есть web access, то зачем ему sharepoint сдался?!…

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

Powered by BlogEngine.NET 1.6.0.0

About the author

Alexey Raga Alexey Raga
.NET software developer.

E-mail me Send mail

Twitter


Disclaimer

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

© Copyright 2010

Sign in