Всем привет!
Начал писать цикл статей про многопоточность на разных уровнях абстракции. И представляю вам первую статью из этой серии - про процессор!
Начал писать цикл статей про многопоточность на разных уровнях абстракции. И представляю вам первую статью из этой серии - про процессор!
вАЙТИ
Многопоточность. Снизу вверх. Процессор
DIY-медиа для ИТ-специалистов. Практические истории про решение самых разных задач из ИТ и смежных областей.
Интеграционное тестирование MongoDB. Точки отказа
Всем привет! Сегодня рассмотрим тему настроек MongoDB для интгерационного тестирования и узнаем, что такое FailPoint и как он может помочь проверить нашу логику в нестандартных ситуациях.
Часто нам приходится писать интеграционные тесты. Интеграционные тесты — это тесты, которые проверяют взаимодействие нашего кода с внешними системами (БД, внешние API и т. д.) в как можно более приближённых к реальности условиях.
Зачем нужны такие тесты? Ну, например, чтобы убедиться, что наша логика корректно обрабатывает ситуации, когда база данных возвращает ошибку создания индекса или отказывает в записи. Юнит-тесты здесь уже не справятся, ведь они не покрывают фактическое взаимодействие с “живой” MongoDB.
Test Commands в MongoDB
MongoDB поддерживает набор специальных команд для тестирования (testCommands), которые обычно отключены в продакшене. Среди них есть конфигурация FailPoint — «точка отказа», позволяющая искусственно вызывать сбои и ошибки в различных операциях.
Чтобы включить testCommands, достаточно при запуске MongoDB задать параметр enableTestCommands=1. Вот пример docker-compose для этой задачи:
Для запуска команд нужно выбрать базу данных (для
Определение
1.
2.
3.
Пример использования для команды
А когда тесты будут завершены, точку отказа можно отключить:
В следующем посте мы рассмотрим пример интеграционного теста, который использует данную функциональность, а пока что можете погрузиться в вики-страничку про данный механизм.
Всем спасибо, с вами был Flexible Coding!
Всем привет! Сегодня рассмотрим тему настроек MongoDB для интгерационного тестирования и узнаем, что такое FailPoint и как он может помочь проверить нашу логику в нестандартных ситуациях.
Часто нам приходится писать интеграционные тесты. Интеграционные тесты — это тесты, которые проверяют взаимодействие нашего кода с внешними системами (БД, внешние API и т. д.) в как можно более приближённых к реальности условиях.
Зачем нужны такие тесты? Ну, например, чтобы убедиться, что наша логика корректно обрабатывает ситуации, когда база данных возвращает ошибку создания индекса или отказывает в записи. Юнит-тесты здесь уже не справятся, ведь они не покрывают фактическое взаимодействие с “живой” MongoDB.
Test Commands в MongoDB
MongoDB поддерживает набор специальных команд для тестирования (testCommands), которые обычно отключены в продакшене. Среди них есть конфигурация FailPoint — «точка отказа», позволяющая искусственно вызывать сбои и ошибки в различных операциях.
Чтобы включить testCommands, достаточно при запуске MongoDB задать параметр enableTestCommands=1. Вот пример docker-compose для этой задачи:
services:
mongo:
image: mongo:6.0
command: mongod --setParameter enableTestCommands=1
ports:
- "27017:27017"
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: password
Для запуска команд нужно выбрать базу данных (для
ConfigureFailPoint
это база данных admin) и вызвать метод db.runCommand({})
.Определение
ConfigureFailPoint
состоит из следующих полей:1.
configureFailPoint
- название точки отказа, которую мы настраиваем. Чаще всего встречается значение failCommand
для настройки отказа конкретной команды2.
mode
: определяет режим срабатывания точки отказа: alwaysOn
, off
, {times: n}
и другие3.
data
- является вложенным объектом, в котором указываются детали и условия сбояПример использования для команды
createIndexes
:
use admin;
db.runCommand({
configureFailPoint: "failCommand",
mode: { times: 1 },
data: {
failCommands: ["createIndexes"],
errorCode: 100,
closeConnection: false
}
});
А когда тесты будут завершены, точку отказа можно отключить:
use admin;
db.runCommand({
configureFailPoint: "failCommand",
mode: "off"
});
В следующем посте мы рассмотрим пример интеграционного теста, который использует данную функциональность, а пока что можете погрузиться в вики-страничку про данный механизм.
Всем спасибо, с вами был Flexible Coding!
Flexible Coding
Всем привет! Начал писать цикл статей про многопоточность на разных уровнях абстракции. И представляю вам первую статью из этой серии - про процессор!
Тем временем готова следующая статья из цикла про многопоточность - сегодня залезем на уровень операционной системы
вАЙТИ
Многопоточность. Снизу вверх. ОС
DIY-медиа для ИТ-специалистов. Практические истории про решение самых разных задач из ИТ и смежных областей.
Интеграционное тестирование MongoDB. Точки отказа. .NET
Всем привет! Продолжаем тему точек отказа у MongoDb. С основами разобрались, а теперь приступим к написанию тестов на .NET.
Тесты разделяются на три этапа: Given (Arrange), When (Act) и Then (Assert). Так же в рамках теста может происходит подготовка окружения -
В рамках конкретного теста точку отказа удобнее всего настраивать на этапе Setup. В NUnit это будет выглядеть следующим образом:
Далее - сама логика теста:
А затем возвращаем MongoDB в исходное состояние:
Таким образом можно настраивать необходимое поведение БД в рамках теста.
Flexible Coding
Всем привет! Продолжаем тему точек отказа у MongoDb. С основами разобрались, а теперь приступим к написанию тестов на .NET.
Тесты разделяются на три этапа: Given (Arrange), When (Act) и Then (Assert). Так же в рамках теста может происходит подготовка окружения -
Setup
и очистка - TearDown
.В рамках конкретного теста точку отказа удобнее всего настраивать на этапе Setup. В NUnit это будет выглядеть следующим образом:
[SetUp]
public void Setup()
{
// Подключаемся к локальной монге, где включены testCommands
var client = new MongoClient("mongodb://root:password@localhost:27017");
_database = client.GetDatabase("testDb");
_collection = _database.GetCollection<BsonDocument>("testCollection");
_adminDatabase = client.GetDatabase("admin");
// Включаем FailPoint для команды createIndexes
var enableFailPoint = new BsonDocument
{
{ "configureFailPoint", "failCommand" },
{ "mode", new BsonDocument("times", 1) },
{
"data", new BsonDocument
{
{ "failCommands", new BsonArray { "createIndexes" } },
{ "errorCode", 100 }, // код ошибки (для проверки)
{ "closeConnection", false } // соединение не рвём
}
}
};
_adminDatabase.RunCommand<BsonDocument>(enableFailPoint);
}
Далее - сама логика теста:
[Test]
public void CreateIndexes_ShouldThrowException_WhenFailPointEnabled()
{
var ex = Assert.Throws<MongoCommandException>(() =>
{
_collection.Indexes.CreateOne(
new CreateIndexModel<BsonDocument>(
Builders<BsonDocument>.IndexKeys.Ascending("someField")));
});
Assert.That(ex.Code, Is.EqualTo(100));
}
А затем возвращаем MongoDB в исходное состояние:
[TearDown]
public void TearDown()
{
// Выключаем FailPoint, чтобы вернуть MongoDb в исходное состояние
var disableFailPoint = new BsonDocument
{
{ "configureFailPoint", "failCommand" },
{ "mode", "off" }
};
_adminDatabase.RunCommand<BsonDocument>(disableFailPoint);
}
Таким образом можно настраивать необходимое поведение БД в рамках теста.
Flexible Coding
Flexible Coding
Тем временем готова следующая статья из цикла про многопоточность - сегодня залезем на уровень операционной системы
Слегка прошёлся по Thread в дотнете в новой статье из цикла про многопоточность. Приятного чтения!
Flexible Coding
Flexible Coding
вАЙТИ
Многопоточность. Снизу вверх. Потоки в языке C#
DIY-медиа для ИТ-специалистов. Практические истории про решение самых разных задач из ИТ и смежных областей.
Flexible Coding
Слегка прошёлся по Thread в дотнете в новой статье из цикла про многопоточность. Приятного чтения! Flexible Coding
Перебираемся ещё на уровень выше в теме многопоточности - в новой статье затронем пул потоков и асинхронность
Flexible Coding
Flexible Coding
вАЙТИ
Многопоточность. Снизу вверх. Асинхронность и пул потоков
DIY-медиа для ИТ-специалистов. Практические истории про решение самых разных задач из ИТ и смежных областей.
var functions = new List<Func<int, int>>();
for (int i = 0; i < 10; i++)
{
Func<int, int> function = x => i * x;
if (!functions.Contains(function))
{
functions.Add(function);
}
}
Console.WriteLine(functions.Count);
Правильный ответ - 1. Почему так? Давайте разбираться
Сравнение делегатов
Сперва нам кажется, что тут всё просто.
Но делегат - это непростой тип в .NET. Давайте посмотрим, из каких полей он состоит.
Method
Это поле с типом данных MethodInfo, который должен быть вызван при обращении к делегату. В памяти он представлен через внутренние поля
Target
Это объект, к которому привязан метод, если он нестатический. То есть если метод — экземплярный,
У делегатов так же переопределён метод Equals. В репозитории reference source можно увидеть полную его реализацию, и нас интересует следующий фрагмент:
Приватные поля
Если мы вызовем этот метод в цикле из примера выше, то обнаружим одинаковые адреса делегата на каждой итерации цикла.
Как вы думаете, почему так?
Flexible Coding
Сравнение делегатов
Сперва нам кажется, что тут всё просто.
Func<int, int> function = x => i * x;
- это создание новой переменной. На основе документации Microsoft узнаём, что делегат - это reference type, значит они сравниваются по ссылке. А раз мы на каждой итерации цикла создаём новую переменную function
- у неё каждый раз будет новый адрес в памяти и Contains всегда вернёт false.Но делегат - это непростой тип в .NET. Давайте посмотрим, из каких полей он состоит.
Method
Это поле с типом данных MethodInfo, который должен быть вызван при обращении к делегату. В памяти он представлен через внутренние поля
_methodPtr
и _methodPtrAux
, которые являются указателями на конкретную функцию.Target
Это объект, к которому привязан метод, если он нестатический. То есть если метод — экземплярный,
Target
указывает на объект, для которого этот метод будет вызван. Если метод статический, Target
может быть null
.У делегатов так же переопределён метод Equals. В репозитории reference source можно увидеть полную его реализацию, и нас интересует следующий фрагмент:
// do an optimistic check first. This is hopefully cheap enough to be worth
if (_target == d._target && _methodPtr == d._methodPtr && _methodPtrAux == d._methodPtrAux)
return true;
Приватные поля
_target
, _methodPtr
и _methodPtrAux
могут нам указывать на то, что два делегата равны. Они как раз дают нам информацию о методе и привязке метода к объекту. Попробуем вытащить их из делегата (тут нам пригодится немного магии unsafe):
void PrintFunctionInfo(Func<int, int> function)
{
unsafe
{
TypedReference tr = __makeref(function);
IntPtr* ptr = (IntPtr*)*(IntPtr*)&tr;
Console.WriteLine($"Pointer to Delegate: 0x{(ulong)ptr:X}");
IntPtr target = ptr[1];
IntPtr methodPtr = ptr[3];
IntPtr methodPtrAux = ptr[4];
Console.WriteLine($"Target: 0x{(ulong)target:X}");
Console.WriteLine($"MethodPtr: 0x{(ulong)methodPtr:X}");
Console.WriteLine($"MethodPtrAux: 0x{(ulong)methodPtrAux:X}");
}
}
Если мы вызовем этот метод в цикле из примера выше, то обнаружим одинаковые адреса делегата на каждой итерации цикла.
Как вы думаете, почему так?
Flexible Coding
Всем привет! Вот и наступило лето, а у нас - итоги прошедшего сезона!
Этой весной мы:
💥 Контролируемо ломали MongoDB - первая и вторая части
😤 Расстраивались из-за Open Source, который становится всё дороже
🧠 Закончили цикл статей про многопоточность (или нет???) - 2, 3 и 4
🔀 Сравнивали делегаты
❗ Пропускали точку с запятой в SQL
🤖 А так же смотрели на ИИ и рисовали схемки в Excalidraw
Flexible Coding
Этой весной мы:
💥 Контролируемо ломали MongoDB - первая и вторая части
😤 Расстраивались из-за Open Source, который становится всё дороже
🧠 Закончили цикл статей про многопоточность (или нет???) - 2, 3 и 4
🔀 Сравнивали делегаты
❗ Пропускали точку с запятой в SQL
🤖 А так же смотрели на ИИ и рисовали схемки в Excalidraw
Flexible Coding