All streams
Search
Write a publication
Pull to refresh
32
0
Dmitry Tikhonov @0x1000000

Software Developer

Send message

Статья, вроде как, должна ратовать за микро-сервисы, но после прочтения не покидает ощущение, что идея была “так себе”. Заметил общий шаблон подобных историй: “Был монолит и мы тратили X усилий на его поддержку, но потом нам все надоело и мы потратили 10X усилий на микросервисы и теперь все хорошо, но некоторые проблемы ещё остаются.”
В целом статья классная — очень многие недостатки микросервисов хорошо подмечены.

Добавлю, что если перформанс не очень важен, то можно обойтись и без компиляции — имея только примитивный парсер и AST можно легко вычислять значения выражений. Вот когда-то сделал простой примерчик.

Yep, but when you are looking at code you cannot say that some type is a struct or not. That is why mutable structs are not recommended since it is very easy to make a mistake using them.

You need to be very cautious with mutable structs. Just a simple modification and the caching does not work:


    public sealed record Number(int Value)
    {
        public int Tripled => Extract(this.tripled);

        private int Extract(FieldCache<int> t) 
             => t.GetValue(@this=> @this.Value * 3, this);

        private FieldCache<int> tripled = new FieldCache<int>();
    }

Demo:
public static void Main()
{
    var n = new Number(7);

    Console.WriteLine(n.Tripled);
    Console.WriteLine(n.Tripled);
    Console.WriteLine(n.Tripled);
}

...
value = factory(@this);
holder = @this;
Console.WriteLine("Factory");

Output:


Factory
21
Factory
21
Factory
21

Я думаю, что смущение вызывает ключевое слово “async”, но никакой асинхронности здесь нет.
Эта конструкция была изначально введена для упрощения работы с тасками, но поскольку Таск я является, по сути, монадой, то async/await удивительным образом подошёл и для других монад, что я показывал в своих предыдущих статьях. Асинхронность — это только частный случай.

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


Но, все-таки, непонятно к чему тут вообще таски и async/await

Тасков здесь и нет, я намерено использовал ValueTask, который хоть и связан с Тасками, но может быть легко заменен своей реализаций (через Method Builder), и тогда зависимости от System.Threading.Tasks вообще не будет.


async/await преобразует исходный метод в виде конечного автомата и больше он ничего не делает.

Это синхронный однопоточный код и все исключения ловятся в нем как и в любом другом синхронном однопоточном коде. (try/catch/finally). Стектрейсы будут не самые простые, но вполне анализируемые.

Прибитость гвоздями к таскам вылезает, когда вы используете синтаксический сахар async/await. Функция должна возвращать Task. Правда, потом в C# разрешили использовать и свои реализации, используя атрибут AsyncMethodBuilder: ValueTask — один из примеров (правда, под капотом там все равно Task сидит).

Эту статью я задумывал как логическое продолжение цикла про то, как можно использовать этот MetodBuilder. Вот например Монада «Maybe» через async/await в C# (без Task-oв!) или Writing “Lazy Task” Using New Features of C# 7 В данном случае Method Builder не пригодился, но акцент я хотел сделать именно на конструкциях языка C#, нежели на практической стороне вопроса.

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

В целом да, соглашусь. Хоть это все и прибито гвоздями к таскам.

Изначально я вообще хотел в этом случае исключение кидать, поскольку не надо там Delay использовать, подход вообще не про это.

AutoReset WaitOne это обращение к ядру и перформанс тут сильно проседает. Мое решение вообще никак не связанно с многопоточкой и async/await используется просто как синтаксический сахар

Их там никогда и не было. Все работает синхронно.

Надо добавить в конец Thread.Sleep(...) поскольку ваш Delay никто не ждет и программа завершается раньше.
У меня вышло так:


Заголовок спойлера
Work A: 1, Thread: 1
Work B: 1, Thread: 1
Work C: 1, Thread: 1
Work D: 1, Thread: 1
Work D is completed, Thread: 4
Work B: 1 (Extra), Thread: 5
Work C: 2, Thread: 7
Work A: 2, Thread: 6
Work B: 2, Thread: 5
Work A: 3, Thread: 4
Work B: 2 (Extra), Thread: 10
Work B: 3, Thread: 10
Work C is completed, Thread: 11
Work A: 4, Thread: 11
Work B: 3 (Extra), Thread: 10
Work B is completed, Thread: 10
Work A is completed, Thread: 10

Работать не перестанет — в статье я про это не стал упоминать, но в коде есть проверка этой ситуации.

Спасибо за информацию про плагин! Попробую.


Кстати, недавно наткнулся на интересную статью про кодогенерацию, где автор использует scriban в качестве шаблонизатора. На мой взгляд, это более интересная альтернатива t4.

Что бы не быть голословным вот скриншот реально работающего фильтра:
Advanced Filter
image

и JSON создаваемый в клиентском приложении, который легко преобразуется в синтаксическое дерево логического выражения и передается в таком виде до репозитория, где и вставляется в нужный запрос.
Заголовок спойлера
{"typeTag":"and","items":[{"typeTag":"oneOfNum","field":"i_lcid","values":[1049]},{"typeTag":"or","items":[{"typeTag":"not","expr":{"typeTag":"containsStr","field":"vch_firstname","value":"admin"}},{"typeTag":"hasAnyInt","field":"i_user_role","values":[11]}]}]}

Причем к запросу можно автоматически добавить подзапрос, добавляющий “вычисляемое” поле если оно есть в фильтре.

Я для себя нашел решение в виде повторения синтаксического дерева SQL (даже статья на хабре про это есть "Дерево синтаксиса и альтернатива LINQ при взаимодействии с базами данных SQL"). “Разухабистый” фильтр передается в виде выражения по всем уровням абстракции (сериализация /десериализация в JSON/XML происходит без проблем) в репозиторий, где он легко превращается в SQL текст. Пользуюсь этим подходом уже несколько лет, забыв про все эти IQueryable как про страшный сон. Все наработки выложил в виде библиотеки на github.

Без динамических запросов иногда бывает довольно сложно обойтись. С процедурами тоже не всё гладко, но есть еще один способ – это использование Query Builder-ов. Идею я недавно описал в этой статье на Хабре (так же есть пример реализации для .Net)

Сам по себе подход в некоторых случаях имеет право на жизнь. Проблема мне видится в том, что на волне хайпа микро-сервисы стали использовать, где надо и (очень часто) где не надо.

Information

Rating
Does not participate
Registered
Activity