Обновить
67
Костя Д.@godzie

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

37
Подписчики
Отправить сообщение

Это именно тот вопрос, который очень старательно обходят стороной подобные статьи. Ведь если автор возьмет не банальную сущность заказ, а нечто действительно сложное (а зачем нам иначе DDD?) то код какого нибудь Update написанный руками отпугнет всякого.

Отвечая на ваш вопрос - в GO никак, инструменты которые позволяют сохранять графы объектов не взлетают, потому что никому особо не нужны тк в микросервисах DDD не часто нужен.

Хорошая статья для новичков и для введения в DDD. Конечно печалит наличие метода Update у репозитория. Представляет ли автор как будет выглядеть реализация этого метода, когда у агрегата будет допустим пять связанных сущностей или коллекций, а у этих коллекций будут свои связанные сущности?

Стоп стоп, вы хотите лечить симптом или болезнь?

Я говорил о лечении симптома - просто добавили sync.Once - победили лишний парсинг каждый раз на реквест. Не более.

Если мы говорим о лечении болезни то надо изначально сделать значение url'a конфигурируемым и добиться того чтобы в структуру trudVsem прокидывался *url.URL на этапе создания этой структуры (если хотите на этапе инициализации). А из строки в url.URL мы преобразовываем на этапе чтения конфигурации.

Ошибка здесь не просто маловероятна. Учитывая что мы парсим константу идемпотентной операцией - не проверять тут ошибку вполне нормально.

По поводу много сложный вещей внутри Do - да он не для этого, но и я предложил использовать его только в конкретном кейсе инициализации.

Все верно, это safe код, а в чем но?

var u *url.URL
var o = &sync.Once{}

func main() {
	o.Do(func() {
		u, _ = url.ParseRequestURI("http://google.com")
	})

	print(u.String())
}

Если once.Do вы оставите внутри рассматриваемой функции, тогда это будет работать только при условии, что запуск функции в разных рутинах не пересечется

Нет это будет работать безопасно, я в посте дал ссылку на memory model

Дык просто выносите url в поле структуры и оборачиваете парсинг url в once.Do(). Гарантий которые предоставляет once достаточно для синхронизации.

Может не выносить и воспользоваться sync.Once?

Мне, кстати, логика работы функции io_uring_cqe_seen совершенно не нравится

согласен с Вами, думаю это сделано из-за красивого api

Например, мы можем считать сразу несколько cqe через io_uring_peek_batch_cqe, а затем начать обрабатывать их не по порядку, тогда вызов io_uring_cqe_seen может привести к тому, что первые cqe могут быть затёрты.

я даже об это слегка спотыкался, правда теперь, немного разобравшись, везде где батчим cqe (а батчим везде где нужен высокий throughput) использую io_uring_cq_advance

По какому признаку пользователь должен это отслеживать?

В случае liburing - функция io_uring_get_sqe вернет null. Если пишите либу сами и работаете с чистыми сисколами то у Вас есть доступ к head и tail SQ + размер SQ тоже известен, так что просто сами смотрите

По какому признаку ядро отслеживает, какие пакеты вычитаны и могут быть безопасно перезаписаны?

В случае liburing - опять же мы подтверждаем прием cqe вызовом функции, под капотом - двигаем head CQ буфера (это значение шарится между юзер спейсом и ядром)

Спасибо! добавлю что для в случае переполнения CQ поведение зависит от версии ядра, раньше лишние cqe дропались (надо проверять FEAT_NODROP вообщем)

а) не перезаписать свой (еще) необработанный запрос

в случае использования liburing не получится перезаписать, если работать с SQ руками то можно

О каких именно буферах речь?

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

Вы точно не путаете статическую/динамическую типизацию с слабой/строгой? Вами было заявлено что GO слабо типизированный, причем тут википедия и динамическая типизация?

Но я вижу это в Go коде лишь чуть реже, чем всегда, поэтому определение «полудинамический ЯП», я думаю, вполне заслужено

Если "чуть реже, чем всегда" то это говорит о качестве таких проектов, кстати пиханием interface{} где надо и не надо страдают в основном новички которые перешли с динамических языков. Далее, в расте тоже есть std::any, он теперь тоже "полудинамический ЯП"?

variable.(type) можно на переменной любого типа вызвать. Точнее, на переменной с меткой любого типа (в случае динамики некорректно говорить о типизации). Таскает рантайм за собой метки для всех значений. =)

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

    var a interface{} = int64(5) 
    print(unsafe.Sizeof(a)) //16, тут 8 бит значение и 8 - тип

    var b int64 = 5
    print(unsafe.Sizeof(b)) //8, тип известен
    на этапе компиляции, хранить ничего не нужно

Что это вообще значит? interface{} — это просто top type, он населён всеми типами. Другими словами, все типы являются interface{}. Это важно лишь для статического анализа.

Это значит что информация о ниже лежащем типе хранится в переменной тип которой interface{} либо любой другой интерфейсный тип.

Чем меньше и чем проще система, тем меньше проблем с Go, и меньше бенефитов от лучших гарантий. Собственно, с какого-то объёма оно вообще вообще нормально ложится и на скрипты без всяких типов.

Вобщем вы частное привели к общему, все остальное - маркетинг.

Полудинамически типизированный из-за golangdocs.com/type-switches-in-golang и interface{}. В строготипизированном ЯП рантайм не таскает за собой типы ко всем значениям, хотя бы не так явно и легкодоступно.

Строго типизированный потому что нет магических преобразований из int32 в int64 например.

Рантайм в GO ничего не таскает, тем более для всех значений. Информация о ниже-лежащем типе действительно вшита внутрь реализации interface{}. Но это касается только интерфейсов.

Сложный код, сложные системы на Go требуют большей памяти и внимательности, чем Rust, где намного проще расслабиться и сосредоточиться на доменной области

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

Разработчику нужно больше интеллектуальных способностей, памяти и внимания, чтобы понять и поддерживать код на Go

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

PS GO строго типизированный

Дело не в явной обработке (её где-то надо делать), а в том, что основная логика "разрывается" обработкой ошибок, как в условном C.

Так в том и дело, что GO'шники считают обработку ошибок частью основной логики программы. Мне например текущая обработка ошибок в GO очень даже заходит.

Кстати писать системный код на GO поэтому тоже довольно удобно, C код с errno отлично мапится в GOшный.

На расте приятно писать, но через пол года, вернувшись к коду - без пол-литры не разберешься. На GO наоборот, сахара нет, зато читаемость кода лучше. Туда же изменчивость раста и стабильность GO. Ну и если хотите писать низкоуровневые вещи то GC go, его модель памяти и пр. может вставлять палки в колеса, а раст подойдет отлично.

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

Не могу говорить за Haskell. Но, например, в GO FFI(cgo) имеет свою, довольно ощутимую цену на вызов С функции из GO (в десятки раз медленнее). И если мы рассматривает io_uring в контексте высокого throughput то таких вещей хочется по максимуму избегать.

Информация

В рейтинге
Не участвует
Дата рождения
Зарегистрирован
Активность