Найдены серьёзные баги в pymongo

Жаль отменили топики ссылки. Этот топик получается изрядно коротким.
В багтрекере разработчиков mongodb появился забавный bug, касающийся качества исполнения драйвера Python->MongoDB.
Я думаю, всем заинтересованным лучше проверить, затронет ли их эта проблема. И, похоже, null pointer dereference может встретиться далеко за пределами pymongo. Ждём красивых багрепортов от Монги.

ссылка на оригинал статьи http://habrahabr.ru/post/181668/

Обзор Windows Workflow Foundation на примере построения системы электронного документооборота

Часть 1. Что такое WF

Windows Workflow Foundation (WF) — уже не новая технология компании Microsoft, разработанная для создания и выполнения потоков работ (Workflow). Однако на данный момент она используется не очень активно, а многие разработчики вообще не слышали про нее. И мое знакомство с ней произошло относительно недавно. Тогда как применение WF при реализации некоторых категорий задач может быть более чем оправданным. В связи с этим хочу рассказать о самой технологии, области ее применения и рассмотреть пример ее использования при реализации конкретного проекта.


Ключевым понятием в WF является Активность (Activity) — класс выполняющий единицу работы в среде выполнения WF. Термины Поток работ и Активность являются синонимами в контексте WF. Каждая Активность выполняет какое-либо действие — буквально программный код (например, на языке C#). Активности имеют входные и выходные параметры, переменные. Активность может представлять собой композицию из нескольких дочерних Активностей, в таком случае в процессе работы родительская Активность управляет запуском своих дочерних элементов в среде выполнения в соответствии со своей внутренней логикой. Например, Активность Parallel из базовой библиотеки Активностей (входит в поставку .NET Framework) запускает дочерние элементы параллельно. А поток работ If, как не сложно догадаться из названия, запускает один из двух дочерних элементов в зависимости от результата проверки заданного условия.

Таким образом, в конечном итоге создание потока работ обычно сводится к составлению в дизайнере блок-схемы на основе Активностей базовой библиотеки в сочетании с Активностями собственной разработки. Поток работ, построенный в дизайнере, кодируется на языке XAML (расширение XML). Внешний вид дизайнера представлен на рисунке.


“Хорошо, но для чего это мне? — спросите вы. — Я лучше напишу килограмм кода, чем буду копаться с блоками и стрелками”.

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

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


В момент А на первом компьютере поток работ доходит до точки, когда для продолжения работы требуются входные данные. Далее, происходит сохранение состояния (значений всех переменных, аргументов в указанной точке). Затем, спустя какой-то промежуток времени, поступают требуемые входные данные, и в момент Б происходит восстановление состояния, продолжение работы алгоритма с сохраненной точки на другом компьютере. Было бы замечательно, если бы таким свойством обладал любой код на C#, не правда ли?

Сейчас принято разделять логику на независимые участки кода. В частности, и для того, чтоб обеспечить масштабируемость. Для примера рассмотрим два метода: LogOn и GetData. Пока очевидно, что вначале вызывается метод LogOn, за ним — GetData. Но когда таких методов становится много, нам бывает сложно разобраться в логике (она может быть “размазана” по всему приложению) и порядке их выполнения. Использование WF снимает данную проблему: мы имеем разделенные задачи, которые связывает нить общего потока работ, отображаемого в виде простой для понимания блок-схемы, что даже неопытный разработчик сможет быстро разобраться в правилах запуска.

Следующая совсем уже прикладная особенность WF, которую не стоило бы обходить вниманием — это возможность вынести программную логику в область конфигурации системы, если необходима такая гибкость. То есть, из компонентов стандартной поставки .NET Framework можно собрать дизайнер в рамках своего программного продукта. На этапе настройки системы происходит модификация потоков работ, управление их свойствами (например, условиями запуска). Далее, при эксплуатации в нужных точках выполняется не “хард-код”, а созданные ранее Активности. Таким образом, WF в сочетании с Dynamic LINQ является мощным инструментом для придания системе такого качества, как настраиваемость. Но, конечно, нельзя сказать, что для настройки такой системы вообще не потребуются навыки .NET программирования.

Важно, чтобы применение какого-либо фреймворка было действительно оправдано. Классический пример анти-паттерна необоснованного применения технологии я наблюдаю в текущем проекте в приложении, реализованном стороной заказчика своими силами. В нем используется схема WF фактически в качестве замены коду на C#, которая в итоге к тому же сильно деградировала вследствие незнания технологии всеми разработчиками, производившими модификацию. Но надо сказать, что получившийся “франкенштейн” неспешно, но все же выполняет возложенные на него обязанности1.

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

Когда к нам в команду поступила задача реализовать User Store типа: “Как директор, я хочу, чтобы в системе заявления на отпуск сотрудников согласовывались у непосредственного руководителя, затем поступали в отдел кадров для составления приказа, а приказ, после моей подписи, уходил бухгалтерию для начисления отпускных, чтобы исключить ошибки и задержки, уменьшить затраты”, — оказалось, что технология WF идеально подходит для ее реализации. При этом могут быть использованы все описанные выше возможности.

В следующей части рассмотрим практическую иллюстрацию применения WF на данном примере.

______________

1В проекте используется устаревший WF 3.0. Известно, что в рамках .NET Framework 4.0 WF был полностью переписан Microsoft в новом пространстве имен. Оптимизация производительности — одно из улучшений.

ссылка на оригинал статьи http://habrahabr.ru/company/luxoft/blog/181562/

Полная поддержка GROUP_CONCAT в Doctrine2

Привет всем.

Так сложилось, что в проекте, над которым я сейчас работаю, потребовалось использовать функцию GROUP_CONCAT(). К сожалению, Doctrine2 «из коробки» эту функцию не поддерживает. Имеющееся же расширение от одного из разработчиков (Benjamin Eberlei) Doctrine2 значится как «limited support for GROUP_CONCAT». Понимаю, что использование данной функции автоматом ставит проект в зависимость от MySQL, но менять СУБД как перчатки не планируется. Так что оставим этот вопрос за рамками поста.

Поскольку погуглив я не нашел готового решения, решил написать его сам (взяв за основу разработку Benjamin’а). Комментировать там особо нечего, поэтому просто представляю его на суд общественности:

/**  * DoctrineExtensions Mysql Function Pack  *  * LICENSE  *  * This source file is subject to the new BSD license that is bundled  * with this package in the file LICENSE.txt.  * If you did not receive a copy of the license and are unable to  * obtain it through the world-wide-web, please send an email  * to kontakt@beberlei.de so I can send you a copy immediately.  */  namespace DoctrineExtensions\Query\Mysql;  use Doctrine\ORM\Query\AST\Functions\FunctionNode,     Doctrine\ORM\Query\Lexer;  /**  * Full support for:  *   * GROUP_CONCAT([DISTINCT] expr [,expr ...]  *              [ORDER BY {unsigned_integer | col_name | expr}  *                  [ASC | DESC] [,col_name ...]]  *              [SEPARATOR str_val])  *   */ class GroupConcat extends FunctionNode {     public $isDistinct = false;     public $pathExp = null;     public $separator = null;     public $orderBy = null;      public function parse(\Doctrine\ORM\Query\Parser $parser)     {         $parser->match(Lexer::T_IDENTIFIER);         $parser->match(Lexer::T_OPEN_PARENTHESIS);                  $lexer = $parser->getLexer();         if ($lexer->isNextToken(Lexer::T_DISTINCT)) {             $parser->match(Lexer::T_DISTINCT);                          $this->isDistinct = true;         }          // first Path Expression is mandatory         $this->pathExp = array();         $this->pathExp[] = $parser->SingleValuedPathExpression();          while ($lexer->isNextToken(Lexer::T_COMMA)) {             $parser->match(Lexer::T_COMMA);             $this->pathExp[] = $parser->StringPrimary();         }          if ($lexer->isNextToken(Lexer::T_ORDER)) {             $this->orderBy = $parser->OrderByClause();         }                  if ($lexer->isNextToken(Lexer::T_IDENTIFIER)) {             if (strtolower($lexer->lookahead['value']) !== 'separator') {                 $parser->syntaxError('separator');             }             $parser->match(Lexer::T_IDENTIFIER);                      $this->separator = $parser->StringPrimary();         }          $parser->match(Lexer::T_CLOSE_PARENTHESIS);     }      public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)     {         $result = 'GROUP_CONCAT(' . ($this->isDistinct ? 'DISTINCT ' : '');          $fields = array();         foreach ($this->pathExp as $pathExp) {             $fields[] = $pathExp->dispatch($sqlWalker);         }                  $result .= sprintf('%s', implode(', ', $fields));                  if ($this->orderBy) {             $result .= ' '.$sqlWalker->walkOrderByClause($this->orderBy);         }                  if ($this->separator) {             $result .= ' SEPARATOR '.$sqlWalker->walkStringPrimary($this->separator);         }                  $result .= ')';                  return $result;     }  } 

Пример использования:

$query = $this->createQueryBuilder('c')         ->select("             c as company,             GroupConcat(b.id, ';', b.headOffice, ';', b.city, ';', s.name             ORDER by b.id             SEPARATOR '|') AS branches         ")->leftJoin('c.branches','b')         ->leftJoin('b.country','s')         ->groupBy('c.id')         ->setFirstResult(0)         ->setMaxResults(10)         ->getQuery() ; $result = $query->getResult() 

Официальная документация по теме:
Регистрация пользовательских DQL функция в Doctrine2
Как подключить в пользовательские функции DQL в Symfony2.
Описание MySQL-функции GROUP_CONCAT

ссылка на оригинал статьи http://habrahabr.ru/post/181666/

Как мы устроили бранчи под каждую задачу

Всем привет! Сегодня я бы хотел рассказать о том, какие есть способы устроить бранчи под каждую задачу и как мы это сделали у себя в Alawar. Здесь рассматривается простой приём с apache2, который позволяет получить отличный результат и может быть полезна как веб-разработчиками, так и системным администраторам. Стоит отметить, что аналогичные решения уже освещались на хабре (например, тут), но часто они написаны для слишком узкой аудитории и не дают ответы на все вопросы и, главное, часто усложняют задачу. Цель же конкретно этой статьи, – показать, как на самом деле всё просто.

Итак, начнём.

Несколько лет назад у нас была проблема, которая заключалась в том, что было достаточно трудозатратно подготовить отдельную тестовую площадку на сервере под каждую задачу. На тот момент использовалась одна ветка (trunk) в репозитории для всех разработчиков и один тестовый сервер. Для крупных задач, конечно же, вручную готовились отдельные инстансы, но это была скорее вынужденная мера, чем нормальное решение. Всё это периодически приводило к конфликтам в коде по ходу разработки, и самое печальное,– к неожиданным поломкам одной задачи, из-за проведённых работ в рамках другой задачи.

Немного об особенностях проекта

Речь идёт об одном из наших крупных проектов (сайты alawar.ru, alawar.com и т.д.), суть которого – предоставлять разный контент с разных доменов на разных языках. В рамках одного проекта задействовано порядка сотни доменов и поддоменов.

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

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

Какие у нас были варианты

1. Создавать конфигурацию сервера вручную

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

2. Настроить по 1-2 тестовых стенда на разработчика

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

3. На тестовом сервере менять правила формирования и обработки URL-ов

Например сделать все ссылки в проекте по такому принципу: http:// test.local / (task-number) / (host) / (uri).

Был и такой вариант, но требовал очень большого рефакторинга, так что здравый смысл не позволил нам пойти по такому пути. Но это не значит, что этот метод не подойдёт другим.

4. Виртуалка под каждую задачу

Идея в том, что хосты и пути всегда одни, код от нужной задачи, БД и прочие сервисы – свои. С точки зрения тестирования это, наверное, идеальный вариант.

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

5. Использовать виртуальные хосты

В apache2 есть параметры UseCanonicalName и VirtualDocumentRoot (Ссылка на документацию). Они позволяют использовать субдомены хоста как часть пути к док. руту проекта и всё это без рестарта apache2. Благодаря этим параметрам фактически мы можем указать в хосте путь к инстансу + uri чтобы получить нужный нам ответ.

Наш выбор

В итоге мы выбрали вариант с виртуальными хостами и определили следующий формат адресов: http:// (task-number). (site-identifier). test.local / (uri).

Где (site-identifier) может принимать значения вида:

• ru – в этом случае понимаем, что нужно показать основной сайт в зоне .ru

• com – основной сайт в зоне .com

• test.ru – субдомен test основного сайта в зоне .ru

• data.export.com – субдомен data.export основного сайта в зоне .com

Для проектов, где нет понятия основного сайта, применимы следующие правила:

• example.com – в этом случае понимаем, что нужно показать сайт example.com

• test. example.com – субдомен test. example.com

• data.export. example.com – субдомен data.export. example.com

• и т.д.

В итоге, набирая адрес в браузере: http://task-1234.example.com.test.local/some/page/?someParams=value мы однозначно знаем, что сервер отдаст ответ с ветки кода задачи task-1234, обработчик сайта example.com/some/page/?someParams=value.

Чтобы всё стало работать так, как мы это задумали, потребовалось зафиксировать пути к док.руту следующим образом:

/data/projects/projectName/branches/(task-number)/application/public

И обучить проект определять имя хоста, согласно нашим правилам.

Из особенностей могу отметить, что для систем, где важен регистр пути к файлу, (task-number) и (site-identifier) должны быть в нижнем регистре.

Пример конфигурации тестовой площадки Apache2:

<VirtualHost *:80>     UseCanonicalName Off     VirtualDocumentRoot /data/projects/projectName/branches/%1/application/public     ServerName test.local     ServerAlias *.test.local     Options -Indexes     CustomLog /dev/null combined </VirtualHost> 
Автоматический деплой

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

Автоматизацию деплоя можно провести разными средствами (скрипт на bash-е, phing, ant и т.д.).

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

1. Спросить task-number (или получить в качестве параметра имя ветки);

2. Создать бранч в репозитории, если его нет;

3. Создать папку под бранч (если её нет);

4. Сделать чекаут(ы) кода;

5. Настроить права на запись в нужные папки/файлы;

6. Настроить конфигурацию проекта;

7. Проставить нужные симлинки;

8. (выполнить другие специфичные проекту действия);

9. Выполнить инициализационные скрипты проекта;

10. При желании запустить тесты, чтобы убедиться, что всё ок.

Итак,

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

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

ссылка на оригинал статьи http://habrahabr.ru/company/alawar/blog/181636/