я обычно размещаю их в месте использования, а не рядом с реализацией
Согласен. Можно легко и убедительно обосновать. Но я бы оставлял в комментариях реализуемых методов, что это реализация такого-то интерфейса. Поможет поддерживать код?
Интерфейсы UserSaver и UserProvider реализованы в Storage (как и в предыдущей статье), но тогда контекст можно вынести туда, а не таскать его каждый раз в методах SaveUser() и User(). Или я что-то не учитываю?
ведь кто сказал, что за сохранение и получение пользователей обязана отвечать одна система?
Нашёл ответ, зачем таскать контексты в каждом методе. Попахивает "предварительной оптимизацией". Даже если предположить, что источники данных для каждого интерфейса могут быть разные, у них вполне себе может быть общий слой Storage (в котором находится базовый контекст).
type My struct{}
func (My) Try() {
println("My")
}
type My2 struct {
My
}
func (My2) Try() {
println("My2")
}
func main() {
My2{}.Try() // My2
}
Понял, что для структуры serverAPI можно перезаписать методы из встроенной UnimplementedAuthServer. Но зачем прослойка в виде интерфейса Auth, можно же сразу реализовывать интерфейс AuthServer внутри services/auth?
Сервер валидирует и тупо перекладывает данные и ошибки в слой бизнес-логики (services). Но валидация - часть бизнес-логики, а перекладывать данные и ошибки - вообще сомнительное занятие.
// TODO: инициализировать объект конфига // TODO: инициализировать логгер
Лучше бы наоборот. Иначе мы не увидим в логе ошибки инициализации конфига. Но, тут возможна проблема курицы и яйца, т.к. в конфиге определяем, куда складывать логи. Вывод: нужно воспользоваться переменными окружения для конфигурирования логов.
Чего ещё не хватает для Graceful Shutdown? Все функции sql-lite имеют двойников с контекстом, например: .Exec вызывает .ExecContext с базовым context.Background(). У меня не хватило ума реализовать nested-контекст для стораджа и для веб-сервера. Как оно сейчас работает: создаю контекст с таймаутом, затем ожидаю srv.Shutdown(ctx) и отменяю контекст в defer cancel() либо подаю по таймауту сигнал <-ctx.Done() для прерывания Shutdown. Правильно? Пока не смог чётко сформулировать, для чего мне nested-контекст. А правильная постановка вопроса - половина решения.
Уже вроде обсуждали в этом канале, стандартный context обеспечивает закрытие по дереву вверх, но это не graceful shutdown (для Graceful Shutdown требуется ожидание обслуживающего детей родителя пока он не обслужит всех своих детей и все его дети не закроются, после чего и сам родитель уже может благополучно закрыться). На деле же при отмене стандартного контекста все горутины отменяющейся ветви получают сигнал закрывающего канала (не важно в каком порядке) после чего все одновременно (и родители и дети) начинают закрываться без ожидания детей, что может привести просто к блочке (когда ребенок отправляет родителю сообщение, а родитель уже завершил свою работу). Я на прошлой неделе уже писал о том что реализовал контекст, который позволяет дожидаться детей путём обертывания горутины ребенка в вызов метода (мой проект: https://github.com/mcfly722/context ) но в процессе разработки данного контекста мной был понят очень важный аспект того, что мы используем вообще не ту структуру данных. Т.е. из контекстов нам нужно вообще НЕ ДЕРЕВО, нам нужен направленный ГРАФ! Это значит что у каждого контекста не должно быть ограничения только в одного родителя (родители являются инфраструктурой для своих детей) поэтому их должно быть множество, а не строго один. И соответственно также должна соблюдаться последовательность закрытия (пока есть контекст который зависит от своих родителей (своей инфраструктуры), его родителей (инфраструктуру) нельзя закрывать). На реализацию я стартанул проект dependency, как будут по нему новости отпишу в чатик.
Это внешняя инфраструктурная задача относительно функциональности. Мы же не пихаем в каждый веб-сервис защиту от DDOS-атак. Например, поможет
fail2banКак применяется claims - не очень понятно, я бы исправил.
Согласен. Можно легко и убедительно обосновать. Но я бы оставлял в комментариях реализуемых методов, что это реализация такого-то интерфейса. Поможет поддерживать код?
Интерфейсы UserSaver и UserProvider реализованы в Storage (как и в предыдущей статье), но тогда контекст можно вынести туда, а не таскать его каждый раз в методах SaveUser() и User(). Или я что-то не учитываю?
Нашёл ответ, зачем таскать контексты в каждом методе. Попахивает "предварительной оптимизацией". Даже если предположить, что источники данных для каждого интерфейса могут быть разные, у них вполне себе может быть общий слой Storage (в котором находится базовый контекст).
Понял, что для структуры serverAPI можно перезаписать методы из встроенной UnimplementedAuthServer. Но зачем прослойка в виде интерфейса Auth, можно же сразу реализовывать интерфейс AuthServer внутри services/auth?
Сервер валидирует и тупо перекладывает данные и ошибки в слой бизнес-логики (services). Но валидация - часть бизнес-логики, а перекладывать данные и ошибки - вообще сомнительное занятие.
/config/config_local.yamlхорошо бы прописать в.gitignore// TODO: инициализировать объект конфига
// TODO: инициализировать логгер
Лучше бы наоборот. Иначе мы не увидим в логе ошибки инициализации конфига. Но, тут возможна проблема курицы и яйца, т.к. в конфиге определяем, куда складывать логи. Вывод: нужно воспользоваться переменными окружения для конфигурирования логов.
Просто добавить две звёздочки для подпапок:
$ protoc -I proto proto/sso/**/*.proto --go_out=./gen/go/ --go_opt=paths=source_relative --go-grpc_out=./gen/go/ --go-grpc_opt=paths=source_relative
На Маке работает, если запускать команду в терминале.
Но если запускать команду внутри go_task, то ругается на "Could not make proto path relative: proto/sso/**/*.proto: No such file or directory"
Возможно, проблема связана с тем, как
go_taskобрабатывает символы подстановки, такие как**.Потому для
Taskfile.yamlпришлось применить вот такой вариант:- mkdir -p gen/go & find ./proto/sso -name '*.proto' | xargs protoc -I proto --go_out=./gen/go/ --go_opt=paths=source_relative --go-grpc_out=./gen/go/ --go-grpc_opt=paths=source_relativeпочему "ручка"? потому-что handle
"golang.org/x/exp/slog" можно заменить на "log/slog", т.к. в go.mod заявлено go 1.21
Не нашёл в заметке ссылку на исходники: https://github.com/GolangLessons/sso
Чего ещё не хватает для Graceful Shutdown? Все функции sql-lite имеют двойников с контекстом, например: .Exec вызывает .ExecContext с базовым context.Background(). У меня не хватило ума реализовать nested-контекст для стораджа и для веб-сервера. Как оно сейчас работает: создаю контекст с таймаутом, затем ожидаю srv.Shutdown(ctx) и отменяю контекст в defer cancel() либо подаю по таймауту сигнал <-ctx.Done() для прерывания Shutdown. Правильно? Пока не смог чётко сформулировать, для чего мне nested-контекст. А правильная постановка вопроса - половина решения.
Исходники исправлений: https://github.com/comerc/url-shortener
"в редакцию пришло письмо":
Перевёл!
А почему нет индекса по полю
price?