Это именно тот вопрос, который очень старательно обходят стороной подобные статьи. Ведь если автор возьмет не банальную сущность заказ, а нечто действительно сложное (а зачем нам иначе DDD?) то код какого нибудь Update написанный руками отпугнет всякого.
Отвечая на ваш вопрос - в GO никак, инструменты которые позволяют сохранять графы объектов не взлетают, потому что никому особо не нужны тк в микросервисах DDD не часто нужен.
Хорошая статья для новичков и для введения в DDD. Конечно печалит наличие метода Update у репозитория. Представляет ли автор как будет выглядеть реализация этого метода, когда у агрегата будет допустим пять связанных сущностей или коллекций, а у этих коллекций будут свои связанные сущности?
Я говорил о лечении симптома - просто добавили sync.Once - победили лишний парсинг каждый раз на реквест. Не более.
Если мы говорим о лечении болезни то надо изначально сделать значение url'a конфигурируемым и добиться того чтобы в структуру trudVsem прокидывался *url.URL на этапе создания этой структуры (если хотите на этапе инициализации). А из строки в url.URL мы преобразовываем на этапе чтения конфигурации.
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
Мне, кстати, логика работы функции 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 буфера (это значение шарится между юзер спейсом и ядром)
Возможно Вам будем интересно, я еще раз провел ресерч работы 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
Вот как раз больше интелектуальных способностей и нужно, чтобы понять, что там очередной разработчик сварганил вооружившись, без спорно, очень мощной системой типов раста. И особенно это видно в больших проекта, где люди меняются, а код остается.
Дело не в явной обработке (её где-то надо делать), а в том, что основная логика "разрывается" обработкой ошибок, как в условном C.
Так в том и дело, что GO'шники считают обработку ошибок частью основной логики программы. Мне например текущая обработка ошибок в GO очень даже заходит.
Кстати писать системный код на GO поэтому тоже довольно удобно, C код с errno отлично мапится в GOшный.
На расте приятно писать, но через пол года, вернувшись к коду - без пол-литры не разберешься. На GO наоборот, сахара нет, зато читаемость кода лучше. Туда же изменчивость раста и стабильность GO. Ну и если хотите писать низкоуровневые вещи то GC go, его модель памяти и пр. может вставлять палки в колеса, а раст подойдет отлично.
Ну и бла бла бла про порог вхождения и прочее (я считаю что гораздо важнее сложность задач разработчика и концепций с которыми он имеет дело, освоить язык - дело наживное).
Не могу говорить за Haskell. Но, например, в GO FFI(cgo) имеет свою, довольно ощутимую цену на вызов С функции из GO (в десятки раз медленнее). И если мы рассматривает io_uring в контексте высокого throughput то таких вещей хочется по максимуму избегать.
Это именно тот вопрос, который очень старательно обходят стороной подобные статьи. Ведь если автор возьмет не банальную сущность заказ, а нечто действительно сложное (а зачем нам иначе DDD?) то код какого нибудь Update написанный руками отпугнет всякого.
Отвечая на ваш вопрос - в GO никак, инструменты которые позволяют сохранять графы объектов не взлетают, потому что никому особо не нужны тк в микросервисах DDD не часто нужен.
Хорошая статья для новичков и для введения в DDD. Конечно печалит наличие метода Update у репозитория. Представляет ли автор как будет выглядеть реализация этого метода, когда у агрегата будет допустим пять связанных сущностей или коллекций, а у этих коллекций будут свои связанные сущности?
Стоп стоп, вы хотите лечить симптом или болезнь?
Я говорил о лечении симптома - просто добавили sync.Once - победили лишний парсинг каждый раз на реквест. Не более.
Если мы говорим о лечении болезни то надо изначально сделать значение url'a конфигурируемым и добиться того чтобы в структуру trudVsem прокидывался *url.URL на этапе создания этой структуры (если хотите на этапе инициализации). А из строки в url.URL мы преобразовываем на этапе чтения конфигурации.
Ошибка здесь не просто маловероятна. Учитывая что мы парсим константу идемпотентной операцией - не проверять тут ошибку вполне нормально.
По поводу много сложный вещей внутри Do - да он не для этого, но и я предложил использовать его только в конкретном кейсе инициализации.
Все верно, это safe код, а в чем но?
Нет это будет работать безопасно, я в посте дал ссылку на memory model
delДык просто выносите url в поле структуры и оборачиваете парсинг url в once.Do(). Гарантий которые предоставляет once достаточно для синхронизации.
Может не выносить и воспользоваться sync.Once?
согласен с Вами, думаю это сделано из-за красивого api
я даже об это слегка спотыкался, правда теперь, немного разобравшись, везде где батчим 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 слабо типизированный, причем тут википедия и динамическая типизация?
Если "чуть реже, чем всегда" то это говорит о качестве таких проектов, кстати пиханием interface{} где надо и не надо страдают в основном новички которые перешли с динамических языков. Далее, в расте тоже есть std::any, он теперь тоже "полудинамический ЯП"?
Нет, не правда, можно вызвать только для интерфейсов. Рантайм таскает метки для всех значений? Неправда, информация о типе лежит в структуре описывающей интерфейс, другими словами
Это значит что информация о ниже лежащем типе хранится в переменной тип которой interface{} либо любой другой интерфейсный тип.
Вобщем вы частное привели к общему, все остальное - маркетинг.
Строго типизированный потому что нет магических преобразований из int32 в int64 например.
Рантайм в GO ничего не таскает, тем более для всех значений. Информация о ниже-лежащем типе действительно вшита внутрь реализации interface{}. Но это касается только интерфейсов.
Есть конечно фреймворки типа actix которые позволяют например писать веб не парясь что он пишется на языке для системного программирования, НО шаг влево - шаг в право от возможностей которые предоставляет actix - и кроме сосредоточения на доменной области нужно еще будет думать о памяти, синхронизации и пр. Вобщем тут я не согласен, бессмысленно спорить, мы с Вами своих останемся так и так.
Вот как раз больше интелектуальных способностей и нужно, чтобы понять, что там очередной разработчик сварганил вооружившись, без спорно, очень мощной системой типов раста. И особенно это видно в больших проекта, где люди меняются, а код остается.
PS GO строго типизированный
Так в том и дело, что GO'шники считают обработку ошибок частью основной логики программы. Мне например текущая обработка ошибок в GO очень даже заходит.
Кстати писать системный код на GO поэтому тоже довольно удобно, C код с errno отлично мапится в GOшный.
На расте приятно писать, но через пол года, вернувшись к коду - без пол-литры не разберешься. На GO наоборот, сахара нет, зато читаемость кода лучше. Туда же изменчивость раста и стабильность GO. Ну и если хотите писать низкоуровневые вещи то GC go, его модель памяти и пр. может вставлять палки в колеса, а раст подойдет отлично.
Ну и бла бла бла про порог вхождения и прочее (я считаю что гораздо важнее сложность задач разработчика и концепций с которыми он имеет дело, освоить язык - дело наживное).
Не могу говорить за Haskell. Но, например, в GO FFI(cgo) имеет свою, довольно ощутимую цену на вызов С функции из GO (в десятки раз медленнее). И если мы рассматривает io_uring в контексте высокого throughput то таких вещей хочется по максимуму избегать.