Производительность и качество
Архитектурные решения, влияющие на пропускную способность
- Одна сериализация на рассылку. Рассылка сериализуется один раз, и одни и те же байты записываются каждому локальному получателю.
- Параллельный fanout. Локальным получателям данные записываются параллельно,
каждому с ограничением
BroadcastSendTimeout, поэтому медленный сокет прерывается, не задерживая остальных. - Ограниченный объём работы на соединение. Одновременно выполняется не больше
MaxConcurrentRequestsPerConnectionзапросов; дальнейшее чтение ждёт, поэтому один занятый клиент не может поставить в очередь неограниченный объём работы. - Последовательная запись. У каждого сокета одновременно только один писатель; фреймы ответов и рассылок никогда не перемешиваются.
Бенчмарки
Микробенчмарки локального Release-прогона тега v4.0.0 на .NET 10.0.12, AMD Ryzen 7
3700X, Windows 11 (медиана пяти замеров, payload 364 байта). Транспорт — in-memory
приёмник без сетевого I/O, поэтому эти числа показывают накладные расходы библиотеки,
а не сквозную задержку.
| Сценарий | Получатели | Время на операцию | Память на операцию |
|---|---|---|---|
| Сериализация для каждого получателя | 100 | 0.356 ms | 39.2 KB |
| Однократная сериализация | 100 | 0.004 ms | 1.2 KB |
| In-memory fanout | 100 | 0.210 ms | 36.3 KB |
| Сериализация для каждого получателя | 1 000 | 3.693 ms | 385 KB |
| Однократная сериализация | 1 000 | 0.010 ms | 1.2 KB |
| In-memory fanout | 1 000 | 2.680 ms | 268 KB |
| Сериализация для каждого получателя | 10 000 | 6.431 ms | 3.84 MB |
| Однократная сериализация | 10 000 | 0.023 ms | 1.2 KB |
| In-memory fanout | 10 000 | 13.656 ms | 2.65 MB |
| Разбор 1 000 запросов | – | 2.349 ms | 984 KB |
- Сериализация для каждого получателя и однократная сериализация сравнивают наивную сериализацию под каждого клиента с общим payload в DarkWS.
- In-memory fanout — это настоящая рассылка через
IBroadcasterи in-memory backplane каждому получателю. - Разбор 1 000 запросов разбирает конверты запросов, около 2 µs на запрос.
В 5.0 локальная доставка переработана: тот же fanout на 1 000 получателей занимает там около 0.14 ms и 12 KB (см. документацию 5.x).
Результаты зависят от железа и среды выполнения; сравнивайте прогоны на одной и той же машине. Они не моделируют TLS, медленных клиентов и работу обработчиков.
Тесты
Набор тестов 4.0.0 запускается на .NET 8, 9 и 10:
| Набор | Тесты |
|---|---|
Сервер (DarkWS) | 109 |
| .NET-клиент и DI | 41 |
| Redis backplane (настоящий Redis в Docker) | 14 |
| Браузерный клиент (Vitest) | 41 |
Пороги покрытия проваливают сборку, если у любого пакета покрытие ниже 90% по строкам или 80% по ветвям. Покрытие в 4.0.0:
| Пакет | Строки | Ветви |
|---|---|---|
DarkWS | 95.4% | 91.8% |
DarkWS.Redis | 96.1% | 100% |
DarkWS.Client | 98.4% | 91.7% |
DarkWS.Client.DependencyInjection | 100% | 100% |
darkws | 97.4% | 92.6% |
Помимо модульных тестов, проверка тестирует Redis backplane с StackExchange.Redis 2.13.17 и 3.2.1, упаковывает каждый NuGet-пакет, проверяет его XML-документацию и символы, устанавливает в чистый проект-потребитель, запускает на всех целевых фреймворках и упаковывает npm-пакет из чистой копии, чтобы проверить его сборку.
Стабильность API
Публичные API отслеживаются с помощью PublicApiAnalyzers: любое изменение публичной поверхности должно быть задекларировано и проверено, иначе сборка падает. Ломающие изменения выходят только в мажорных версиях, с инструкциями по миграции в changelog.