Pull to refresh

Comments 49

Версии 2 и более должны сменить путь импорта. Теперь это разные библиотеки.

Эм… А есть ли смысл в этой странной сомнительной фичи, кроме призрачной возможности использовать несколько несовместимых версий одного пакета, что все равно в общем случае скорее всего будет невозможно или очень затратно из-за инициализаций и прочего?


Ну и да, надеяться на то, что все будут придерживаться semver это как-то очень наивно.

На мой взгляд это такая двойная перестраховка от того, чтобы случайно не обновить мажорную версию. Ну, то есть `go get`, конечно, бдит, но на всякий случай, пусть и пути импорта для мажорных версий различаются. Парни готовятся к Go 2.0

Ну, и, теоретически, возможность использовать разные мажорные версии в одном бинарнике может быть и востребована — например, если новая версия пакета для базы данных работает быстрее, но ломается на больших объемах. Т.е. понятно, что отсюда следуется, что новая версия — сырая, и тащить её в продакшен «не айс», но иногда хочется, а иногда и приходится. И оставить в коде возможность переключаться между версиями (например, флагом) без замены бинарника, на мой взгляд, имеет смысл. Правда, в очень редких и недолгих случаях, imho, п.ч. поддерживать в коде две разные версии пакета может оказаться очень затратным делом…

А почему надеятся на то, что все будут придерживаться semver — это наивно? Я за последние пару лет как-то не сталкивался с «экзотикой».
А почему надеятся на то, что все будут придерживаться semver — это наивно? Я за последние пару лет как-то не сталкивался с «экзотикой».

Например, вот эта репа, правда, она заброшена. Или вот gorm. А еще куча реп вообще без тегов.


Практика других языков показывает, что довольно мало разработчиков правильно понимают semver (например, ломая совместимость в пачтах) и обычно, те кто понимают — это хорошо организованные команды. А все остальные порождают страдания. Очень сомнительно, что программисты на go внезапно другие.


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

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


Касательно оставлять возможность переключать флагом — это всегда настолько затратно и усложняет код (особенно в Go, где плохо со всякими enterprice-шаблонами типо стратегий и DI), что на практике разработчикам конечных приложений практически никогда не стоит свеч.

Мне кажется, стремиться всегда надо к лучшему. И криворукие программисты тоже будут всегда. Поэтому идеала не существует, но если к нему не стремиться, то лучше не станет. imho, semver уже стандарт.

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

Я себе одновременную работу разных версий не представлял совсем. Т.е. если это iris.v5 и iris.v6, то на разных портах, как минимум.

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

Не спорю, но если «фича» досталась бесплатно, зачем от неё отказываться? Переименование импорта в наличии давно, плюс требование мажорную версию импортировать с другим путём (оставим за скобкой причины), равно возможность использовать разные мажорные версии пакета одновременно.
Делать специально возможность, которая, возможно, нафик ни кому не сдалась — это оверинжиниринг. А бесплатно получить фичу, которая не планировалась, но получилась — почему бы и нет? Её выкидывать затратнее, чем оставить
Я себе одновременную работу разных версий не представлял совсем. Т.е. если это iris.v5 и iris.v6, то на разных портах, как минимум.

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


А бесплатно получить фичу, которая не планировалась, но получилась — почему бы и нет? Её выкидывать затратнее, чем оставить

Тут вы правы, разумеется)

Часто наоборот бывает: затратно переключать проект, в котором используется какая-то старая либа в каких-то уголках проекта, в которые годами никто не заглядывал. А тут новая задача заходит, которая идеально ложится на новую версию либы. А годами забытые уголки часто и тестами не покрыты, или покрыты так, что лучше бы не были покрыты вообще. Тут патч проапдейдить не дают «у нас это баг проявляется? нет? значит не трогаем», не то что на пару мажорных версий обновить.
Я уже представил, что надо будет держать в голове несколько разных версий одной либы и помнить, где какая используется и мне стало очень печально.

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

Так себе идея, на мой взгляд. Я бы старался такого избегать в 100% случаев
Ну дополнительно запрещается писать новый код со старой версией. Как, например, в случае принятия новых правил форматирования кода или использования каких-то паттернов (в широком смысле слова).
Вы опять не разобрались в причинах, почему так сделали.
У вас есть зависимость libA-2.0 и libB-1.0, но у libB есть зависимость — libA-1.0. libA-1.0 и 2.0 не совместимы. Допустим должна быть всегда только одна версия либы (libA-2.0) — тогда сломается зависимость libB-1.0. С go mod все будет работать.

И вот уже в которой раз Вы приходите в пост про Go и опять только претензии, и опять не понимаете суть.
Кстати, Вы уже разобрались с многопоточностью, goroutine и каналами?
Вы опять не разобрались в причинах, почему так сделали.
У вас есть зависимость libA-2.0 и libB-1.0, но у libB есть зависимость — libA-1.0. libA-1.0 и 2.0 не совместимы. Допустим должна быть всегда только одна версия либы (libA-2.0) — тогда сломается зависимость libB-1.0. С go mod все будет работать.

Я скажу проще — это не очень полезная и опасная фича. Очень наивно предполагать, что в общем случае две разные версии пакета не будут конфликтовать между собой. Какие-то простые либы вполне возможно, но держать в рантайме два пула коннекшенов к базе данных?


Я понимаю, какую проблему решает это решение, но на мой взгляд решение привносит еще более страшную проблему.


Кстати, Вы уже разобрались с многопоточностью, goroutine и каналами?

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


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


Ну и да, оверхед на рантайм и скорость работы — это не единственные критерии языка. Я вот смотрю на некоторых чатботов на go и мне хочется плакать от реализации и скорости поддержки. Но они очень производительны, да.

Я скажу проще — это не очень полезная и опасная фича. Очень наивно предполагать, что в общем случае две разные версии пакета не будут конфликтовать между собой. Какие-то простые либы вполне возможно, но держать в рантайме два пула коннекшенов к базе данных?

Я понимаю, какую проблему решает это решение, но на мой взгляд решение привносит еще более страшную проблему.

А что может быть страшнее, чем сломанные зависимости? Опять же, это очень специфический кейс, но даже если так — с go mod все будет работать.
Что это за библиотека, которая автоматом создает пул коннектов при импорте? Выбросите ее.


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

Если у вас такой мир, забудьте про Go и живите спокойно, хватит приходит в каждый пост про Go, он не для вас.
Хотя про утилизацию ресурсов вы так и не поняли.

Что это за библиотека, которая автоматом создает пул коннектов при импорте? Выбросите ее.

Не инициализированных? Практически любая для работы с бд, разве нет?


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


А что может быть страшнее, чем сломанные зависимости? Опять же, это очень специфический кейс, но даже если так — с go mod все будет работать.

Использование двух версий либы для сериализации с разными нотациями и несовместимыми структурами? Это как сломанные зависимости, но наоборот. Причем, если этот подход будет популярным, будем как в javascript, когда они пилят костыли, что бы в js-bundle не подтягивались абсолютно все версии lodash, которые существуют.


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


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

Я просто захожу в каждый пост на хабре, правда.


Хотя про утилизацию ресурсов вы так и не поняли.

Возможно, я не прав, но для меня шедулер, который вы описали очень полезен только в случае, если задача сразу и cpu-bound, и io-bound и по какой-то причине вы ее не разделили. Это круто, но в моем случае простое деление задач на cpu-bound части (где хватает синхронных воркеров с асинхронным мастером) и io-bound (просто асинхронные воркеры) в целом покрывает большое количество кейс. В остальных нужно страдать, давить ресурсами ну или использовать черную магию/перенос задачи на базу данных/rust/c++

Возможно, я не прав, но для меня шедулер, который вы описали очень полезен только в случае, если задача сразу и cpu-bound, и io-bound и по какой-то причине вы ее не разделили. Это круто, но в моем случае простое деление задач на cpu-bound части (где хватает синхронных воркеров с асинхронным мастером) и io-bound (просто асинхронные воркеры) в целом покрывает большое количество кейс. В остальных нужно страдать, давить ресурсами ну или использовать черную магию/перенос задачи на базу данных/rust/c++

Нет, как раз это очень важно для io-bound задач.
Вот есть у вас веб сервер и 10 тредов, каждый реквест обрабатывается одним тред, при этом это io-bound задача — то есть будет момент когда тред будет полностью заблокирован на IO. В Go же не будет такого момента, когда тред будет заблокирован — goroutine будет заблокирована, но треды нет — они будут обрабатывать другие горутины (горутина — один http запрос, читай другие запросы).
Допустим длительность обработки одного запроса одинаковая c Go и c xxxx (python, java, etc). В таком случае Go обработает больше запросов за один и тот же промежуток времени.

Для чисто io-bound задач есть асинхронное программирование, которое позволяет проворачивать такие же вещи.


Например, в python есть asyncio с event loop, который позволяет обрабатывать и отправлять одному потоку довольно большое количество запросов одновременно.


Разница только в том, что это все привязано к одному треду. То есть это менее оптимально, но в случае веб-сервиса обычно перед вашим приложением запускается master, который делает примитивный round-robin или что-то более сложное, организовывая распределение нагрузки.

Один поток будет обрабатывать одновременно только один запрос.


Почитайте все таки про многопоточное и конкурентное программирование. Особенно в Go — горутины, каналы и так далее.

Возможно я чего-то не понимаю, давайте я попробую объяснить, что я имею ввиду на примере:


@app.get('/test')
async def test_handler(request):
    result1 = await first_database_request()
    result2 = await second_database_request(result1)
    result3 = await file_reading(result2)
    return json(result3)

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

И это означает, что несколько запросов будут обрабатываться параллельно, я не прав?

Вы не правы, если у вас один тред (как вы сами сказали), выполняться будут конкурентно, не параллельно, просто тред не будет ждать i/o и в это время обрабатывать другой запрос.


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

Вы не правы, если у вас один тред (как вы сами сказали), выполняться будут конкурентно, не параллельно, просто тред не будет ждать i/o и в это время обрабатывать другой запрос.

Да, вы правы, я использовал не правильный термин, но имел ввиду именно то, что вы описали.


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

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


Касательно каналов, как часто вы используете их не как очередь и не из-за того, что у вас недостаток контроля над горутинами?

Касательно каналов, как часто вы используете их не как очередь и не из-за того, что у вас недостаток контроля над горутинами?

Каналы использую для коммуникации (в основном) и управления (старт-стоп).


Вы разницу понимаете между каналами в Го внешней очередью?

Вы разницу понимаете между каналами в Го внешней очередью?

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

ну так как вы можете это сравнивать? Так же как сравниваете горутины и процессы питона.

Вы сравниваете многопоточное приложения с практически распределенными системами. Это разные уровни.
Точно так же сервисы на Go будет использовать внешнею очередь для коммуникации между сервисами.
$ go mod init mod

Почему не так: go init
Инициализировали пакет, создался файл go.package.
Всё, работаем с пакетом и дальше. Зачем вводить какую-то новую сущность…
М.б. потому что модули — пока фича экспериментальная и с особенностями. И да, go обновился с 1.10 до 1.11 — ломать ничего нельзя, только добавлять.
Но это же ничего не ломает.
и надо, чтобы не сломалось и потом. пока модули экспериментальные, работа с ними происходит через субкоманды команды mod. если не зайдет, забыли и ладно.
или я ваш вопрос как-то неправильно понял?

Правильно я понимаю, что выкачать все зависимости в папку vendor теперь моветон? И чтобы в жёсткой корпоративной среде себя от роском… сбоев гитхаба нужно понимать локальное хранилище а-ля гитлаб?

Ещё пока не моветон. Отказ от вендоринга — это мнение автора. В официальных доках я такого нигде не встречал. Просто у модулей "ноги растут" из vgo, который "детище" Пайка. Если модули "зайдут" (imho, не если, а когда), vgo забросят, а с ним и остальные инструменты вендоринга станут неправославными. В общем, судя по активности, модули и wasm — это тренд, но пока ещё далеко не мэйнстрим (три дня назад всего релиз был).

Просто у модулей "ноги растут" из vgo, который "детище" Пайка.

Нет, vgo это прототип от Russ Cox.


Если модули "зайдут" (imho, не если, а когда), vgo забросят, а с ним и остальные инструменты вендоринга станут неправославными.

Так модули в Go это и есть vgo — его Расс сделал и предложил включить в Go.


Не путайте людей.

Нет, vgo это прототип от Russ Cox.

Точно. Это я попутал. Спасибо за исправление.

Так модули в Go это и есть vgo — его Расс сделал и предложил включить в Go.

Вот, тут не соглашусь. Да, разработку перенесли в основной репозиторий. Да, изменения зеркалируют в репо vgo (пока). Но все-таки это разные продукты. Можно в модулях убрать вендоринг, а в vgo оставить. Ну, и так далее… Слияния/поглощения не было, значит, это разные продукты.

Изменения не зеркалируют


The code in this repo is auto-generated from and should behave exactly like the Go 1.11 go command, with two changes:

It behaves as if the GO111MODULE variable defaults to on.
When using a Go 1.10 toolchain, go vet during go test is disabled.

я имею ввиду, что коммиты не зеркалируются, vgo — это go mod в отдельном репозитории. Сделано это для go 1.10 пользователей. Не думаю, что будут как-то отдельно развивать vgo, смысла нету две код базы поддерживать.

Слово «зеркалировать» я взял тут:
Currently, module support is in active development in the main go repository, with changes mirrored back to the vgo repository.
. Перевести можно по-разному, но смысл в том, что это не backport.

Посмотрел в репозиторий: 19 июля и 8 августа были коммиты rsc с комментарием «import from main repo ...». В основном репо есть коммиты по функционалу модулей (тоже от rsc), например, 21 августа (старее не смотрел). Т.е. на текущий момент устанавливая vgo вы получаете чуточку не то, что есть в основном репо… пока Russ снова не зальёт изменения.

Я к тому, что лучше относится к vgo и модулям как к разным штукам
Нет, я не буду использовать vgo, у меня уже go 1.11. vgo только для тех, кто хочет использовать модули в go 1.10.
нет, типа гитлиба не надо, есть уже github.com/gomods/athens — подымаешь у себя прокси athens, указываешь в GOPROXY и все ок

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

Кто-то пояснит за что минусим?

как минимум за грамматику

Ааа, спасибо за прояснение. Жестко тут. Постораюсь учесть.
Я просто 23-года в Германии живу (и клавы русской нет), а уехал в 15. ;)
Понимаю что никого не интересует и отмазка слабая, но таки роль играет ;)
p.s. выше это не гратика, а описка в основном. С транслитом сложно все уследить.

это я предположил, просто еле расшифровал ваш комментарий

Да я сам сейчас читаю, стремно получилось ;)


Еще раз:
Короче я поддерживаю данное новшество. Мне знакомы причины нововведения из моего бэкграунда.
Предложеный вариант тоже выглядит лаконичным и неинвазивным, но вполне рещающим проблему.
Так что пусть будет!

Все еще недоумеваю почему они сразу так не сделали?!

Ждем статью про GOPROXY.
С точки зрения Go модулей, мажорная версия — это совершенно другой пакет.
А вот это круто!

Это будет сильно стимулировать разработчиков пореже выпускать мажорные версии. Еще бы — каждый раз создавать новый пакет больно, придется обновлять исходники, переименовывать файлы, менять везде импорты. Затратно и неудобно. Поэтому разработчик библиотеки постарается держать обратную совместимость подольше. Обычно откладывая новые «ломающие» фичи на потом. Чтобы потом сразу так бахнуть новый мажорный релиз, который ломает все и сразу (аля angular1 -> angular2).

А уж пользователю библиотеки как хорошо!
При каждом мажорном релизе нужно переписывать код (пускай и sed-ом). Переписывать тесты.
Захотел проверить «а заработает ли мое приложения с новой версией, могу ли я сделать апгрейд». Не проблема, сначала сделай апгрейд, а потом уже сможешь узнать, успешен ли он. Затратно и неудобно. Поэтому клиенты библиотеки постараются подольше использовать старую версию библиотеки. А всякие хотелки «ой, там новая прикольная фича» отклаывать на потом. Чтобы потом так резко и больно переходить на новый мажорный релиз (аля py2 -> py3).

В итоге ломающие изменения начнут просачиваться в минорные релизы. С поддержкой «устаревших» апи еще несколько минорных релизов, пометками deprecated и ворнингами. А мажорная версия будет «вечно молодой v1».
Попробовал новую систему модулей, довольно неплохо. После статьи остается много вопросов
1) «Модули также избавляют от необходимости в $GOPATH, которая была камнем преткновения для новых разработчиков Go».И добавили GOPROXY. А это не одно и тоже получается?
2) Как будет работать билд через прокси? каждый раз выкачивать пакеты или хранить в локальном кеше? в чем профит по сравнению с вендорингом?
3) Радует конечно что теперь можно назвать пакет mypackage вместо github.com/myawesomeaccount/mypackage без всякой возник с GOPATH, но интересно тогда он просто не будет автоматически скачивать зависимости проекта (а отдельный реджистри позволит и это" или еще какие подводные камни есть? надо будет потестить этот момент
4) Как будет разруливаться вопрос откуда брать пакет? Есть на гитхабе пакет github.com/a/b и есть в прокси модуль с названием github.com/a/b. Вообще тема с импортом довольно спорная, с одной стороны просто указать ссылку на репозиторий и готово, просто и эффективно. С другой стороны длиннющие пути до репозиторией, одинаковые названия пакетов, геморой с контрибьютингом через форк и т.п
Спасибо за статью.
Пока еще не успел попробовать, но у меня возник вопрос, а сейчас то в такой модели куда будут складываться скаченные либы, если и не в /vendor и не в GOPATH? В директорию пользователя куда-нибудь?
Проект с модулями должен быть за пределами GOPATH. Пакеты скачиваются в $GOPATH/pkg/mod. Если и GOPATH не задана, то в HOME/go/pkg/mod (в linux).
Тут еще нужно бы написать для новичков в Go, типа меня, которые решили сразу воспользоваться системой модулей, о том, что если назвать модуль mod, то попытка создать внутри этого модуля package вызовет ошибку при билде — что-то вроде «can't load package: package „mod/somepackage“: disallowed import path». При поиске в исходных текстах всплывает следущее объяснение:

// Paths beginning with «mod/» might accidentally
// look in the module cache directory tree in $GOPATH/pkg/mod/.
// This prefix is owned by the Go core for possible use in the
// standard library (since it does not begin with a domain name),
// so it's OK to disallow entirely.


Поэтому называем модуль хоть как-нибудь по другому, пусть даже modz, создаем внутри какой-нибудь package с именем test, и можно его спокойно импортировать (import «modz/test»).
Sign up to leave a comment.

Articles