Обновить
4
0.4

Пользователь

Отправить сообщение

Чтобы точно сказать надо глубже в код погружаться, на, да, предполагаю, что тут есть место переаллокациям, как при работе с List.

  1. Вариант со stackalloc не для всякого размера строки подойдёт из-за StackOverflowException,хорошо бы про такие вещи упоминать.

  2. Аллокацию памяти можно свести до нуля, если ввести флаг hasWhitespaces и устанавливать его в цикле при встрече пробельного символа. Тогда в случае hasWhitespaces=false можно возвращать исходную строку не создавая новую. Но осмысленность такой оптимизации зависит от контекста: как часто в рассматриваемом сценарии будут встречаться строки без пробельных символов и готовы ли вы за отсутствие аллокации в некоторых случаях платить расходом нескольких дополнительных тактов процессора в случае каждом.

В примере с буфером размер строки известен, в примере с ToArray используется итератор, ToArray в данном случае не может делать предположений о конечном размере коллекции.

P.S. Если бы ToArray к ICollection применялся, тогда другое дело, вот что у метода ToArray() под капотом.

if (source is ICollection<TSource> collection)
{
    return ICollectionToArray(collection);
}

private static TSource[] ICollectionToArray<TSource>(ICollection<TSource> collection)
{
    int count = collection.Count;
    if (count != 0)
    {
        var result = new TSource[count];
        collection.CopyTo(result, 0);
        return result;
    }

    return [];
}


  1. Запрос на ревью должен быть в приоритете у проверяющего, тогда MR не будет долго мариноваться. Если ты не можешь прервать свой "поток", тогда тебе лучше не заниматься код-ревью.

  2. Проверять код должен тот, кто ставил задачу, тогда не будет формальных код-ревью.

  3. Ревьюер должен уходить от болезни "я бы сделал по-другому". Если нет чётких причин не принимать код, но хочется по-другому - прими MR и сделай по-другому.

    Если ничего этого нет, тогда действительно лучше без код-ревью.

>Именно так работает потоковая сериализация, мы не строим весь объект в памяти, а записываем каждую пару ReportItem в ReportValue сразу в JSON-файл. Никаких временных словарей. Никаких temp-файлов. Только один проход и готово.

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

>В .NET подобные структуры данных имеют дополнительную служебную нагрузку (примерно 24 байта на элемент), а также хранят ссылки на свои элементы.

А у вас есть контроль над типами ReportItem/ReportValue? Нельзя ли там какие-то элементы уложить собственно в структуры (struct) и посмотреть, какая экономия памяти из этого получится?

Мне кажется, я привёл довольно конструктивные аргументы в своём первом сообщении, на что в ответ получил телепатическое код-ревью в стиле "код-говно". Неудивительно, что беседа после этого продолжилась так, как она продолжилась.

>судя по анамнезу - забили на SRP, и отсюда все проблемы

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

>вступая в полное противоречие с кейсом что вы описываете (пришлось менять все тесты из-за новой зависимости)

Вы опять не понимаете, что тесты пришлось переписывать не из-за "новой зависимости", но зачем то бежите доказывать что-то про код, который ни разу не видели.

Я могу привести пример из своего предыдущего проекта, где бизнес-логика легче билась на изолированные блоки и там функциональные юнит-тесты стоят как влитые, хотя за два года проект прошёл уже не одну стадию "редизайна". Сильно менялся один use-case из-за вскрывшихся новых аспектов поведения интегрируемой системы, но там как раз был случай, когда SUT под капотом поменялся очень сильно, а система тестов на нём осталась почти не тронутой, за исключением пары краевых случаев. Так что по своему опыту могу сказать, что данный подход работает и обеспечивает устойчивость к рефакторингу.

А про то, как вы в любой системе любой доменной области и любой сложности всегда точно и с первого раза "мельчите", выделяете абстракции и всё это благодаря интерфейсам и мокам, давайте вы расскажете кому-нибудь другому.

>ну а у вас оказали
Нет, "изменения конкретных классов внутри тестируемых методов" влияния не оказали.

>ну а у вас ее, устойчивости, не получилось. почему так, если вы сделали всё правильно?

Если этот вопрос не риторический, то потому, что предметная область довольно трудная где "всё зависит от всего" и построение алгоритмов и моделей приходится вести итерационно, игнорируя некоторые аспекты. Возвращение к этим аспектам меняет и высокоуровневые модели/контракты. И этот факт не зависит от выбранного способа тестирования.

>я просто пошел и переписал все тесты когда у одной из зависимостей SUT появилась новая зависимость

Просто сам SUT изменился по причинам, которые я объяснил выше. И переписать тесты, написанные в функциональном стиле, было конечно же проще, чем переписать те же тесты, написанные на моках. А переписывать тесты всё равно пришлось бы.

Надеюсь, я смог рассеять возникшие у вас недоразумения.

Я, например, не понимаю.

У меня тесты написаны на чистые функции (хоть и C#) и у меня нет боли от того, что изменился конструктор и вот теперь мне приходится...

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

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

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

>OpenTelemetry is the mechanism by which application code is instrumented to help make a system observable

Да, OpenTelemetry инструмент, который может помочь сделать систему наблюдаемой. Спасибо, чо не поленились проиллюстрировать мою мысль примером из документации.

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

Так вот, OpenTelemetry - это не философия, не подход к проектированию, а способ писать трейсы, метрики и логи. Это стандарт, о существе которого вы не написали ничего. А о проектировании наблюдаемости системы вы по существу тоже ничего не написали.

Ну, "подтверждаю", так "подтверждаю", чего же тут спорить. Понятно, что ваш самописный велосипед для вас не "просто самописный велосипед".

Ошибки на единицу самые трудноотлавливаемые. Тут их две.

В DDD самая главная неправда - это постулирование искусственного отрыва кода от данных. Хотя весь Enterprise - это про работу с данными и персистентностью. Т.е., да, на верхнем уровне надо обсуждать потребности бизнеса и строить модели по возможности абстрагируясь от способа хранения. Но когда мы сажаем эти модели "на землю", то всегда получается, что персистентность имеет значение.

  1. Выносишь сложную логику в функциональное ядро (чистые функции на статике).

  2. Остальное остаётся без покрытия или покрывается сразу интеграционными тестами.

  3. PROFIT - "легкотестируемый кодом без неиспользуемых абстракций". И без возни с моками.

Не писать запрос и ответ имеет смысл, чтобы не раздувать индексы логов. Чуть более хитро - писать запрос и ответ только в случае ошибки, ведь ошибки нам наиболее интересны.

С интерсепторами в dotnet не особо богато. Castle мне приходилось использовать в Autofaq в эпоху Framework. Что из этого живо сейчас? C Microsoft DI можно Scrutor использовать, в целом пашет, хоть на этом спасибо.

А так то... не надо ни централизованно, ни через атрибуты обкладывать каждый метод "телеметрией". Толку в этом мало. Метрики и трейсы важно снимать при обращении к инфраструктуре (БД, REST etc.), но большинство популярных библиотек и так имеет свой инструментарий для поддержки OpenTelemetry. Для остального немногого можно написать трейсы и метрики кастомно.

P.S. Когда я сам взялся писать такие метрики и трейсы, то у меня был выбор: использовать EventSource (путь многих библиотек, например AspNetCore и Quartz), использовать интерсепторы или кастомно прописать в нужных местах работу с Activity и счётчиками. OpenTelemetry в каких-то своих документах рекомендовало именно последний вариант. На нём я и остановился.

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

Информация

В рейтинге
2 259-й
Зарегистрирован
Активность