NEventStore 3.0 — пропускная способность/производительность

Я экспериментировал с Event Store 3.0 от JOliver как с потенциальным компонентом проекта и пытался измерить пропускную способность событий через Event Store.

Я начал использовать простую обвязку, которая, по сути, повторяла цикл for, создавая новый поток и фиксируя очень простое событие, состоящее из идентификатора GUID и строкового свойства, в базу данных MSSQL2K8 R2. Диспетчер, по сути, был безработным.

Благодаря этому подходу удалось добиться ~3 тыс. операций в секунду на 8-процессорном HP G6 DL380 с БД на отдельном 32-процессорном G7 DL580. Тестовые машины не были привязаны к ресурсам, в моем случае блокировка выглядит пределом.

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


person MattC    schedule 22.11.2011    source источник


Ответы (3)


Я согласен, что блокировка ввода-вывода будет самым большим узким местом. Одна из проблем, которую я вижу в тесте, заключается в том, что вы работаете с одним потоком. Сколько совокупных корней у вас есть в вашем домене с более чем 3 000 событий в секунду? Основной дизайн EventStore предназначен для многопоточных операций с несколькими агрегатами, что уменьшает количество конфликтов и блокировок для приложений, предназначенных для чтения.

Кроме того, какой механизм сериализации вы используете? JSON.NET? У меня нет реализации Protocol Buffers (пока), но каждый тест показывает, что PB значительно быстрее с точки зрения производительности. Было бы интересно запустить профилировщик для вашего приложения, чтобы увидеть, где находятся самые большие узкие места.

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

Наконец, использовало ли ваше эталонное приложение System.Transactions или оно по умолчанию не использовало транзакций? (EventStore безопасен без использования System.Transactions или любых транзакций SQL.)

Теперь, после всего сказанного, я не сомневаюсь, что в EventStore есть области, которые можно значительно оптимизировать, если уделить немного внимания. На самом деле, я использую несколько обратно совместимых ревизий схемы для выпуска 3.1, чтобы уменьшить количество операций записи, выполняемых в SQL Server (и механизмах СУБД в целом) во время одной операции фиксации.

Один из самых больших вопросов дизайна, с которым я столкнулся, когда начинал переписывать 2.x, который служит основой для 3.x, — это идея асинхронного, неблокирующего ввода-вывода. Все мы знаем, что node.js и другие неблокирующие веб-серверы превосходят многопоточные веб-серверы на порядок. Однако вероятность усложнения вызывающего объекта увеличивается, и это следует серьезно учитывать, поскольку это фундаментальное изменение в том, как работает большинство программ и библиотек. Если и когда мы перейдем к событийной, неблокирующей модели, это будет больше во временных рамках 4.x.

Итог: опубликуйте свои тесты, чтобы мы могли видеть, где находятся узкие места.

person Jonathan Oliver    schedule 23.11.2011
comment
Спасибо за ответ Джонатан. Для уточнения ;) Каждый коммит — это новый источник событий, поэтому я фиксирую 3 тыс. различных источников событий в секунду. Исключение сетевого прыжка не улучшило ситуацию, но является правильным моментом. Что касается транзакций, я явно не зачисляю их в транзакцию, но это может не совпадать с отказом от использования транзакций. Я использую JSON для сериализации, хотя, поскольку мы не привязаны к процессору, я не думаю, что это нас ограничивает. Я опубликовал тестовую программу на GitHub (github.com/MattCollinge/EventStore-Performance-Tests). .git). - person MattC; 29.11.2011

Отличный вопрос, Мэтт (+1), и я вижу, что сам мистер Оливер ответил как ответ (+1)!

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

Шаблон CQRS, которому, по-видимому, пытаются следовать большинство людей, использующих EventStore от JOliver, допускает ряд подшаблонов «масштабирования». Первое, что люди обычно ставят в очередь, — это сами фиксации событий, в которых вы видите узкое место. «Очередь выключена» означает выгрузку из фактических коммитов и вставку их в некоторый оптимизированный для записи, неблокирующий процесс ввода-вывода, или « очередь".

Моя свободная интерпретация:

Широковещательная рассылка команд -> Обработчики команд -> Широковещательная рассылка событий -> Обработчики событий -> Хранилище событий

На самом деле в этих шаблонах есть две точки масштабирования: обработчики команд и обработчики событий. Как отмечалось выше, большинство начинают с масштабирования частей обработчика событий или коммитов в вашем случае в библиотеку EventStore, поскольку это обычно является самым большим узким местом из-за необходимости где-то сохранять его (например, в базе данных Microsoft SQL Server).

Я сам использую несколько разных провайдеров, чтобы проверить наилучшую производительность, чтобы «поставить в очередь» эти коммиты. CouchDB и .NET AppFabric Cache (у которого есть отличная функция GetAndLock()). [OT]Мне очень нравятся функции надежного кэша AppFabric, которые позволяют создавать резервные серверы кэша, которые резервируют ваши регионы на нескольких машинах, поэтому ваш кэш остается активным до тех пор, пока работает хотя бы один сервер.[/OT]

Итак, представьте, что ваши обработчики событий не записывают коммиты напрямую в EventStore. Вместо этого у вас есть обработчик, вставляющий их в систему «очереди», такую ​​как Windows Azure Queue, CouchDB, Memcache, AppFabric Cache и т. д. Суть в том, чтобы выбрать систему с небольшим количеством блоков или вообще без блоков для постановки событий в очередь, но что-то это надежно со встроенной избыточностью (Memcache — мой наименее любимый вариант избыточности). У вас должна быть эта избыточность на случай, если сервер упадет, у вас все еще будет очередь событий.

Чтобы окончательно зафиксировать это «событие в очереди», есть несколько вариантов. Мне нравится шаблон Queue Windows Azure для этого из-за множества «работников», которые вы можете постоянно искать в очереди. Но это не обязательно должна быть Windows Azure — я имитировал шаблон очереди Azure в локальном коде, используя «Очередь» и «Рабочие роли», работающие в фоновых потоках. Он очень хорошо масштабируется.

Скажем, у вас есть 10 рабочих, постоянно просматривающих эту «очередь» для любых обновленных пользователем событий (обычно я пишу одну рабочую роль для каждого типа события, что упрощает масштабирование, поскольку вы можете отслеживать статистику каждого типа). Два события вставляются в очередь, первые два рабочих мгновенно получают сообщение каждый и вставляют их (фиксируют их) непосредственно в ваш EventStore одновременно - многопоточность, как упомянул Джонатан в своем ответе. Узким местом с этим шаблоном будет любая база данных/хранилище событий, которую вы выберете. Допустим, ваш EventStore использует MSSQL, а узким местом остается 3000 RPS. Это нормально, потому что система построена таким образом, чтобы «наверстать упущенное», когда эти RPS падают, скажем, до 50 RPS после 20 000 всплесков. Это естественный шаблон, который CQRS допускает: «Eventual Consistency».

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

Очевидно, недостатком этого является то, что вы теряете некоторые проверки в реальном времени. Я решаю это, обычно разделяя проверку на две категории при структурировании своего домена. Одним из них является Ajax или «облегченная» проверка в реальном времени в домене (что-то вроде проверки перед командой). А остальные — это проверки с жестким сбоем, которые выполняются только в домене, но недоступны для проверки в реальном времени. Затем вам нужно будет кодировать отказ в модели предметной области. Это означает, что всегда кодируйте выход, если что-то пошло не так, обычно в форме уведомления по электронной почте пользователю о том, что что-то пошло не так. Поскольку пользователь больше не блокируется этой поставленной в очередь командой, его необходимо уведомить в случае сбоя команды.

И ваши проверочные проверки, которые должны быть отправлены в «бэкенд», направляются в вашу базу данных Query или «только для чтения», верно? Не заходите в EventStore, чтобы проверить, скажем, уникальный адрес электронной почты. Вы будете выполнять проверку своего высокодоступного хранилища данных только для чтения для запросов вашего внешнего интерфейса. Черт возьми, пусть один документ CouchDB будет посвящен только списку всех адресов электронной почты в системе в качестве вашей части запроса CQRS.

CQRS - это просто предложения... Если вам действительно нужна проверка в реальном времени тяжелого метода проверки, вы можете создать вокруг него хранилище запросов (только для чтения) и ускорить проверку - на этапе PreCommand, прежде чем он будет вставлен в очередь. Много гибкости. И я бы даже сказал, что проверка таких вещей, как пустые имена пользователей и пустые электронные письма, - это даже не проблема домена, а ответственность пользовательского интерфейса (разгрузка необходимости выполнять проверку в реальном времени в домене). Я разработал несколько проектов, в которых у меня была очень богатая проверка пользовательского интерфейса на моих MVC/MVVM ViewModels. Конечно, мой домен прошел очень строгую проверку, чтобы убедиться, что он действителен перед обработкой. Но перенос посредственных проверок ввода или того, что я называю «облегченной» проверкой, на уровни ViewModel обеспечивает почти мгновенную обратную связь с конечным пользователем, не затрагивая мою область. (Есть также приемы, позволяющие синхронизировать это с вашим доменом).

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

person eduncan911    schedule 21.06.2012
comment
Интересный ответ. Спасибо, что написали! - person Daniel Lidström; 13.02.2013

Мы создали небольшой шаблон для массового параллелизма с использованием Erlang/Elixir, https://github.com/work-capital/elixir-cqrs-eventsourcing с помощью Eventstore. Нам все еще нужно оптимизировать соединения с базой данных, пулы и т. д., но идея иметь один процесс на агрегат с несколькими соединениями с базами данных соответствует вашим потребностям.

person Henry H.    schedule 27.10.2016