Flexible Coding
155 subscribers
165 photos
2 files
100 links
Download Telegram
Проверяем, существует ли документ в коллекции MongoDB

Всем привет! Сегодня взглянем на интересные детали работы с MongoDB.

MongoDB - это документная база данных, которая позволяет хранить неструктурированные данные в json-like формате под названием bson.


Итак, задача: написать метод, который проверяет, существует ли документ в БД по фильтру.

Вроде всё просто, однако у Mongo нет операции Exist на уровне документов и коллекций. А это значит, что мы можем выполнить задачу двумя способами:
1. Сделать запрос find с фильтром и параметром limit=1 - чтобы получить первый документ по фильтру. Если он не null - значит такой документ есть.
2. Сделать запрос countDocuments по фильтру и получить количество документов. Если их больше нуля - значит документ существует

Если рассуждать логически, не вникая в детали монги, то вызов countDocuments кажется наиболее быстрой операцией - ведь нам по сети надо гонять только число (4 или 8 байт), а не целый bson-документ, у которого есть как минимум идентификатор. Да и можно предположить, что у метода countDocuments есть ещё какие-то оптимизации...

Собственно, в проекте мы думали также, поэтому изначально для проверки существования документа в коллекции использовали countDocuments. Однако выяснилось, что эта операция внутри монги раскрывается в агрегацию следующего формата:


db.collection.aggregate(
[
{ $match: <query> },
{ $group: { _id: null, n: { $sum: 1 }}}
])

И тут уже возникает вопрос:
А точно ли это быстрее, чем find с лимитом и проекцией?


Я решил провести небольшой бенчмарк с локальной монгой для того, чтобы проверить разницу этих методов. Код бенчмарка тут, в Readme есть описание происходящего, а выводы сделаны следующие:
- Разница минимальна - наносекунды для фильтров по идентификатору и милисекунды для фильтров без индексов
- В случае поиска по индексу (по _id) - быстрее будет find с проекцией
- В случае поиска без индекса - быстрее будет countDocuments

Flexible Coding
👍8🔥2
Всем привет!
Начал писать цикл статей про многопоточность на разных уровнях абстракции. И представляю вам первую статью из этой серии - про процессор!
🔥13
Интеграционное тестирование MongoDB. Точки отказа

Всем привет! Сегодня рассмотрим тему настроек 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!
🔥4👍21🤔1
Интеграционное тестирование MongoDB. Точки отказа. .NET

Всем привет! Продолжаем тему точек отказа у 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
🔥32
💵 Open-Source становится платным (опять)

Всем привет! Сегодня поговорим о том, как open-source проекты собираются переходить на коммерческие модели. В прошлый раз была библиотека FluentAssertions, а на этот раз речь идёт о MassTransit, MediatR и AutoMapper.

🔗 Ссылки на анонсы:
- MassTransit
- MediatR и AutoMapper

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

Надеюсь, в старых версиях MassTransit не найдут какую-нибудь страшную уязвимость, которую пофиксят только в платной версии

🤔 Какие есть альтернативы MassTransit?

Из того, с чем работал я, могу предложить следующие варианты:
- EasyNetQ для RabbitMQ
- KafkaFlow для Apache Kafka

Flexible Coding
🤬51👍1
🤖 Заметки ИИ-юзера. Qwen-coder, continue и VS Code

TLDR: Он поднял локальную модель, которая хуже облачных и теперь ругает её. А плагин норм.

Всем привет! С недавних пор я начал пользоваться плагином Continue, который предоставляет некоторые ИИ-возможности для вашей IDE. Среди них:
- Чат с возможностью положить в контекст файлы, папки и т.д.
- Tab-автокомплит (как в GitHub Copilot)
- Автоматическое редактирование файла на основе промпта
- Создание и использование своих провайдеров контекста (например на основе браузера или БД) - пока не пробовал, но знаю что есть

Начал я с чего-то попроще: для чата настроил модель GPT-4o, а для автокомплита поднял локальную модель qwen-coder. Подключил это дело в файлах конфигурации и пошёл тестить - сразу на большом проекте.

Вот мои промежуточные выводы.

😡 Сначала плохое

- Во-первых, КАК ЖЕ QWEN-CODER ЛЮБИТ ФИГУРНЫЕ СКОБКИ!!! Я писал некоторе большое switch-выражение, и он постоянно добавлял мне эти скобки, игнорируя их наличие ниже. По итогу я выключил автокомплит, пока писал этот свич, потому что это невозможно }}}}}}}

- Во-вторых - qwen как будто бы ничего не знает про ValueTask, и даже когда метод в сигнатуре содержит его, локальная модель всё равно предлагает использовать Task.CompletedTask или другие методы класса Task.

🥹А теперь хорошее

Это прекрасный генератор json! Когда мне надо было подготовить тестовые переменные для сериализации, я просто открыл нужный класс-dto, написал


var json = """
"""

И автокомплит сам мне предложил корректный json на базе класса. Тема классная, пользуйтесь)

А модель ChatGPT в чате и в задачах авторедактирования файла действительно неплоха - главное добавить в промпт пример, так как эти модели лучше понимают задачи с примерами и аналогиями.


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


Что дальше?
- Я хочу попробовать Codestral от Mistral - судя по документации Continue это лучшее решение для автокомплита
- Тестирование IDE Cursor - это вызывает у меня скептицизм и сомнения относительно большого проекта на миллион строк, но попробовать можно
- Возможно, использование GitHub Copilot и Jetbrains AI Assistant

Flexible Coding
🔥6👍1
🎨Опять рисуем схемы

Всем привет! Давно не было обзоров приложений, исправляюсь. Встречайте - Excalidraw!

Предыстория

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

Всё началось с Miro, и это действительно монстр из мира вайтбордов. Монстр, который решил покинуть РФ, а бесплатной версии ой как не хватает.

После Miro я начал использовать Whimsical, о котором писал тут и даже частично внедрил его использование в компанию. Изначально там была удобная модель лицензирования - не по количеству досок, а по количеству элементов на каждой доске, но она изменилась, и бесплатная версия перестала меня удовлетворять.

Время шло, я исследовал тему ведения заметок, узнал про Obsidian, и вместе с ним про Excalidraw. И понял: это оно. То, что мне надо.

Exalidraw
У этого приложения есть открытая версия, где можно сразу рисовать схемки и даже создавать сессии коллаборации.

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

Я даже нашёл библиотеку с прикольными человечками и стилизовал на её основе свою презентацию про процессы


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

Помимо версии с сайта, это приложение может встраиваться в другие приложения - например в тот же заметочник Obsidian. Таким образом заметки можно объединять со схемами, всё это добро будет храниться у вас на ПК - и никакой облачный сервис внезапно не скажет: "Русский? Отказано."


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

В общем, всем рекомендую. С вами был Flexible Coding.

#приложения
👍4🔥3
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. Почему так? Давайте разбираться

Сравнение делегатов

Сперва нам кажется, что тут всё просто. 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
🤔7🆒1
Сравнение делегатов

Как мы узнали из предыдущего поста, создаваемые делегаты в цикле равны. Но почему так происходит, ведь i меняется с каждой итерацией?

Тут в игру вступают замыкания.

Замыкание (closure) представляет объект функции, который запоминает свое лексическое окружение даже в том случае, когда она выполняется вне своей области видимости.


Скажем проще. В этом коде:

var i = 1;
Action a = () =>
{
Console.WriteLine(i);
}

Делегат a "знает", что он использует переменную i из другой области видимости - это и есть "запоминание лексического окружения"

Вернёмся к примеру с циклом:

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);
}
}


Так как переменная-итератор создаётся вне тела цикла, функция превращается в замыкание, которое выглядит как класс ...DisplayClass0_0 в IL-коде. И у этого класса можно увидеть поле .field public Int32 i - наш "захваченный" итератор. Так как объект замыкания был создан вне тела цикла и только один раз - функции считаются равными.

Однако если мы добавим одну маленькую деталь - скопируем i в переменную в области видимости цикла

var functions = new List<Func<int, int>>();

for (int i = 0; i < 10; i++)
{
var iCopy = i;
Func<int, int> function = x => iCopy * x;
if (!functions.Contains(function))
{
functions.Add(function);
}
}

Console.WriteLine(functions.Count);

то вывод будет уже 10, и логика приложения будет корректна.

Какие выводы можно сделать?
1. Аккуратнее со сравнением делегатов. Тут велик шанс допустить ошибку и долго пытаться понять, а что же пошло не так
2. Если вы увидели такой код в продакшене, убейте автора

А с вами был Flexible Coding!

P.S. И кстати, спасибо за комментарий к предыдущему посту, там всё написано верно!
🔥5👍3
Всем привет!

Сегодня о странностях в EF Core.

EF Core - это библиотека-ORM от Microsoft, которая позволяет взаимодействовать с сущностями в БД как с объектами


Миграции в EF - это обновления схемы данных, которые можно сгенерировать или написать самостоятельно. В них используется абстракция MigrationBuilder, с помощью которой можно декларативно описывать создаваемые объекты: индексы, таблицы и т.д. А с помощью метода Sql() можно написать свой sql-скрипт, ну для крайних случаев.

И вот, допустим, у нас есть необходимость написать часть миграции с помощью sql-скрипта. Пишем:


protected override void Up(MigrationBuilder migrationBuilder)
{
// some code
migrationBuilder.Sql(
@"UPDATE schema.table
SET key = id::text
WHERE key IS NULL");

migrationBuilder.CreateIndex(
name: "IX_NAME",
schema: "schema",
table: "table",
columns: new[] { "key" },
unique: true);
}

Вроде всё хорошо. Вот примерный сгенерированный скрипт:


UPDATE schema.table SET key = id::text WHERE key IS NULL CREATE UNIQUE INDEX IX_NAME ...


Видите ошибку? При генерации скрипта EF Core не добавляет ; между кастомным скриптом и дальнейшим декларативным описанием. Из-за этого при прогоне миграции я получил ошибку: "syntax error near or at CREATE" и очень долго пытался найти, а какой такой CREATE есть в этой миграции?

В общем, несмотря на наличие крутых IDE, линтеров и прочего, спустя много лет разработки я наткнулся на ошибку, связанную с ПРОПУЩЕННОЙ ТОЧКОЙ С ЗАПЯТОЙ!

Будьте внимательны, коллеги)

С вами был Flexible Coding
😱8👍2
Всем привет! Вот и наступило лето, а у нас - итоги прошедшего сезона!

Этой весной мы:

💥 Контролируемо ломали MongoDB - первая и вторая части

😤 Расстраивались из-за Open Source, который становится всё дороже

🧠 Закончили цикл статей про многопоточность (или нет???) - 2, 3 и 4

🔀 Сравнивали делегаты

Пропускали точку с запятой в SQL

🤖 А так же смотрели на ИИ и рисовали схемки в Excalidraw

Flexible Coding
5😁1
🕵️ Open Source, которому не доверяешь

Всем привет!

На работе рисёрчил разные open source системы веб-аналитики. Это приложения, которые считают метрики, посещаемость сайта, просмотры страниц — если знаете Яндекс.Метрика или Google Analytics — оно, только надо open-source и self-hosted.

И вроде бы опенсорс = хорошо, системы есть, можно тестить. И тут начинают вылезать разные нюансы.

🐘 PostHog - https://posthog.com/
Это большое и мощное решение. Оно требует Kafka, ClickHouse, Elastic, PostgreSQL и ещё кучу всего. В сумме — 22 контейнера

И даже варианте Self Hosted PostHog лезет в облако...
🧾 фронтенд — за информацией о фичах, а ещё отправляет метрики в облачный PostHog
📡 бэкенд — постоянно чекает биллинг

Да, сервер (слава богу) работает без интернета, но нужно вручную вычищать обращения к облаку с клиентской части. Без этого — ощущение, что ты просто хостишь чей-то SaaS.


📊 Umami - https://umami.is/
Это более лёгкое и дружелюбное решение. Оно написано на Node.js и использует Prisma ORM. Разворачивается легко, вроде всё круто. Но вот беда: Prisma при выполнении миграций автоматически скачивает бинарники с внешнего ресурса - https://binaries.prisma.sh. И вот что мы получаем:
💥 В изолированной среде — падение.
🕳️ В открытой — потенциальная дыра в безопасности.

🧪 Выводы
Для успешного внедрения даже open source решения требуется:
- Аудит внешних запросов 🌐
- Замеры производительности 📉
- Проверка работы в офлайне 🔌

Open source ≠ безопасно по умолчанию.

С вами был Flexible Coding
👏4🔥3😢2
👨‍💻Код на слайдах...

Я часто делаю презентации - внутренние доклады на работе, конференции или просто для себя. И постоянной проблемой были слайды с кодом.
📏 Разный масштаб и размер кода
🎨 Разные темы в IDE, если в процессе работы над презой я её менял
✂️ Непонятно как обрезать, что переносить, как показать длинные строки....

Знаю, что такие сервисы уже давно есть, но вот недавно я решил попробовать визуализатор сниппетов carbon (почему он называется так же как продукт в Positive Technologies????). Не знаю, есть ли сервисы лучше, но мне прям зашло - можно отрегулировать длину сниппета, выбрать тему, выбрать язык...

В общем, я тут над одним докладом работаю, и это просто отличная находка!

А вы пользуетесь подобным сервисом? Пишите в комментарии!

Flexible Coding
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7
Версионирование
 
Всем привет!
Сегодня мы поговорим про версии и версионирование. Они повсюду:
🧪 у релизов приложений
🧱 у миграций баз данных
🔌 у API и протоколов
 
 Но зачем вообще нужны эти версии?
💡 Версия — это способ сказать: «Эта штука изменилась».
Она помогает понять, что именно изменилось, насколько это важно, и как с этим работать. Версии дают структуру хаосу и позволяют системам (и людям) быть на одной волне.

 
Какие бывают версии?
 
Semantic Versioning (SemVer)
📍 Формат: MAJOR.MINOR.PATCH (например, 1.4.2)
📌 MAJOR — ломает обратную совместимость
📌 MINOR — добавляет функциональность
📌 PATCH — фиксит баги
Используется повсеместно — от npm-библиотек до .NET NuGet-пакетов.
 
Calendar Versioning (CalVer)
📅 Формат: 2025.07.1, YY.MM, YYYY.MM.DD и т.д.
🧭 Привязан к дате выпуска, а не к смыслу изменений.
Полезен, когда частота релизов важнее, чем их содержание.
👀 Пример: Ubuntu 24.04 (тут можно почитать подробнее), JetBrains 2024.1
 
SeqVer — Sequential Versioning
🔢 Просто увеличивающийся счётчик (v1, v2, v3)
Минимум смысла, максимум простоты.
Подходит для внутреннего использования, документации или версий данных в БД/API
 
Гибриды
Иногда какой-то принятой методики недостаточно - и приходится комбинировать подходы. Такие версии дают максимум контекста.
📅🔢 Date + Counter - 2025.07.01.1
🧠🔡 SemVer + Git SHA - 1.0.0+abc123
🧠🔁 SemVer + Revision - 1.0.0.1
 
Flexible Coding 2.0 (да, раньше был ещё один такой же канал)

P.S. Пост написан в telegram версии 11.13
👍8🔥2🤔1🤩1
🤖 Заметки ИИ-юзера. Mistral и continue

Запоздалый пост про мои эксперименты с AI. В этот раз я добрался до codestral - французской модели от компании Mistral. Плагин Continue уже подключил и в Rider, и в VS Code.

Эта модель уже облачная, и посильнее чем локальная qwen. А ещё там дают триал на целых 8 недель!

Какие выводы теперь?

Автокомплит уже лучше, не так любит скобки как qwen и делает меньше странных предложений
Плагин Continue ломает райдер и становится невозможно работать - умирает навигация по коду, помогает только откат к дефолтным настройкам
А вот чат у Mistral очень слабый - рекомендации так себе, действия в коде - тоже
В общем, для бесплатного теста работы с автокомплитом - норм, для серьёзного вайбкодинга - не подойдёт.

Как запустить Codestral:

* Регистрируемся в mistral на гугл аккаунт
* Указываем номер телефона (оно даже для рф работает, но иностранный надёжнее)
* Подключаем continue
* Profit!


А теперь пришло время тестить агентов и копайлота!

Flexible Coding
👍2🔥1👏1