Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Iter13 #16

Merged
merged 50 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
bf83e4d
рефакторинг агента по итогам встречи с ментором
StasMerzlyakov Feb 24, 2024
493bb30
рефакторинг сервера по результам встречи с ментором
StasMerzlyakov Feb 24, 2024
35e5034
реализовал инкремент 6
StasMerzlyakov Feb 25, 2024
33b8c15
исправления по результатам работы статического анализатора
StasMerzlyakov Feb 25, 2024
e0ad640
реализовал iter7
StasMerzlyakov Feb 27, 2024
deae2b3
pool->poll
StasMerzlyakov Feb 27, 2024
0efc03f
обновление в chi
StasMerzlyakov Feb 27, 2024
fe592ec
исправление по результатам статического анализатора
StasMerzlyakov Feb 27, 2024
7cc0390
ещё исправления по статическому анализатору
StasMerzlyakov Feb 27, 2024
bbdfb43
поправил обновление метрик
StasMerzlyakov Feb 27, 2024
d359ac7
убрал лишние проверки, из-за которых не проходили значения спорядком …
StasMerzlyakov Feb 27, 2024
c9fb46f
перевел агента на json
StasMerzlyakov Feb 27, 2024
d8f355b
реализовал компрессию на сервере при передаче ответов
StasMerzlyakov Feb 27, 2024
e7feff5
incr8
StasMerzlyakov Feb 28, 2024
812fe7d
поправил названия
StasMerzlyakov Feb 28, 2024
f4f82b1
обновил чтение конфигурации для сервера
StasMerzlyakov Feb 29, 2024
d2adb56
добавил методы сохранения и восстановления хранилища
StasMerzlyakov Feb 29, 2024
dc07f35
наработки
StasMerzlyakov Feb 29, 2024
457ffcc
наработки (не собирается)
StasMerzlyakov Feb 29, 2024
83ba3d9
рефакторинг сервера, тесты пройдены
StasMerzlyakov Mar 1, 2024
7e9cb5d
наработки по инкременту 9
StasMerzlyakov Mar 1, 2024
b4dddfb
небольшой комментарий
StasMerzlyakov Mar 1, 2024
3b1e067
ещё рефакторинг
StasMerzlyakov Mar 1, 2024
fae1af1
наработки по инкременту 9
StasMerzlyakov Mar 1, 2024
3565934
изменения по результам работы статического анализатора
StasMerzlyakov Mar 1, 2024
e91f47d
накидал идею по архитектуре
StasMerzlyakov Mar 1, 2024
1fa7648
наработки по gomock
StasMerzlyakov Mar 8, 2024
ccdc442
реализовал adapter для ping
StasMerzlyakov Mar 8, 2024
80c6159
небольшой рефакторинг, логирование + postgres
StasMerzlyakov Mar 8, 2024
1c339e3
поправил имя
StasMerzlyakov Mar 8, 2024
a9be316
сделал memStorage и pgStorage заменяемыми
StasMerzlyakov Mar 10, 2024
8acb868
iter11
StasMerzlyakov Mar 10, 2024
ad0733d
добавил проверку rows.Err()
StasMerzlyakov Mar 10, 2024
42a5cd7
переформулировка гексогональной архитектуры с добавлением usecase
StasMerzlyakov Mar 13, 2024
7ce4843
Упроситл конфигурацию сервера и вынес в cmd.
StasMerzlyakov Mar 13, 2024
24c3759
реализовал сервер
StasMerzlyakov Mar 13, 2024
49337e2
поправил агент, поправил ошибку в сервере
StasMerzlyakov Mar 13, 2024
dc1587e
вопрос про docker
StasMerzlyakov Mar 13, 2024
4fa5095
актуализировал вопросы
StasMerzlyakov Mar 13, 2024
bd78d54
добавил функцию для обработки ошибок
StasMerzlyakov Mar 13, 2024
5f8db79
реализовал обработку ошибок
StasMerzlyakov Mar 14, 2024
116a0c5
Добавил обработку ошибки соединения в agent
StasMerzlyakov Mar 14, 2024
0854c50
инкремент 13
StasMerzlyakov Mar 14, 2024
0bf48de
всякая мелочевка
StasMerzlyakov Mar 15, 2024
f64a107
переименовал пару пакетов; заменил самописные заглушки на gomock
StasMerzlyakov Mar 16, 2024
0232d86
закрыл Body
StasMerzlyakov Mar 18, 2024
a3917bb
небольшие правки после ревью второго спринта
StasMerzlyakov Mar 18, 2024
278aba5
актуализировал вопросы
StasMerzlyakov Mar 18, 2024
f2a2ead
добавил все же ReadAll
StasMerzlyakov Mar 18, 2024
16b1668
ещё вопрос
StasMerzlyakov Mar 18, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.png filter=lfs diff=lfs merge=lfs -text
23 changes: 23 additions & 0 deletions Questions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
- АРХИТЕКТУРА!!!
- убрал картинку - после нескольких итераций пришел к гексогональной архитектуре но с use-кейсами и доменной областью, как средством деления логики приложения
в учебном приложении получилиcь области
- работа с бэкапом
- администривные операции(пока только ping)
- работа с метриками


- storage/wrapper/retriable.go - может есть более изящное решение? (дублирование кода, создание функциональные оберток)

- middleware/testutils/http_handler.go - добавил Handler интерфейс ради генерации mock. Можно ли как-то заставить gomock сгенерировать mock для стандартного интерфейса http.Handler.

- defer req.Body.Close() в http-хэндлере:
В документации (https://pkg.go.dev/net/http#Request):
The Server will close the request body. The ServeHTTP Handler does not need to.
Получается что можно не заморачиваться?

- DOCKER - было бы логично добавить тестирование storage на postgres, но смущает реализация unit-тестов и для локального запуска и в связке с github metricstest. (появляются вопросы по реализации - можно ли завязаться на переменные окружение POSTGRES_PASSWORD: postgres, POSTGRES_DB: praktikum и т.д). Возможно будет использование docker в кусре далее? Стоит ли сейчас заниматься?

- (ОТВЕТ ПОЛУЧЕН; TODO) w.WriteHeader(http.StatusOK) - где писать, надо ли вообще писать? TODO поправить хэндлеры

- (ОТВЕТ ПОЛУЧЕН; TODO ) Загрузка конфигурации (flag, env) (см internal/config/server.go) - можно ли покрыть тестами (ссылка на пример, если есть; надо ли вообще? - TODO пример - https://github.com/golang/go/blob/master/src/flag/example_test.go)

34 changes: 25 additions & 9 deletions cmd/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,41 @@ import (
"syscall"

"github.com/StasMerzlyakov/go-metrics/internal/agent"
"github.com/StasMerzlyakov/go-metrics/internal/common/wrapper/retriable"
"github.com/StasMerzlyakov/go-metrics/internal/config"
)

type Agent interface {
Start(ctx context.Context)
Wait()
}

func main() {
agentCfg, err := agent.LoadConfig()
agentCfg, err := config.LoadAgentConfig()
if err != nil {
log.Fatal(err)
}

metricStorage := agent.NewMemStatsStorage()
resultSender := agent.NewHTTPResultSender(agentCfg.ServerAddr)
retryCfg := retriable.DefaultConf(syscall.ECONNREFUSED)
retryableResultSender := agent.NewHTTPRetryableResultSender(*retryCfg, resultSender)

var agnt Agent = agent.Create(agentCfg,
retryableResultSender,
metricStorage,
)

// Взято отсюда: "Реализация Graceful Shutdown в Go"(https://habr.com/ru/articles/771626/)
// Сейчас выглядит избыточным - оставил как задел на будущее для сервера
ctx, cancel := context.WithCancel(context.Background())
ctx, cancelFn := context.WithCancel(context.Background())
exit := make(chan os.Signal, 1)
signal.Notify(exit, os.Interrupt, syscall.SIGTERM)

if agent, err := agent.CreateAgent(ctx, agentCfg); err != nil {
panic(err)
} else {
<-exit
cancel()
agent.Wait() // ожидаение завершения go-рутин в агенте
}
agnt.Start(ctx)
defer func() {
cancelFn()
agnt.Wait()
}()
<-exit
}
179 changes: 174 additions & 5 deletions cmd/server/server.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,188 @@
package main

import (
"log"
"context"
"errors"
"net/http"
"os"
"os/signal"
"syscall"
"time"

"github.com/StasMerzlyakov/go-metrics/internal/server"
"github.com/StasMerzlyakov/go-metrics/internal/common/wrapper/retriable"
"github.com/StasMerzlyakov/go-metrics/internal/config"
"github.com/StasMerzlyakov/go-metrics/internal/server/adapter/fs/backup"
"github.com/StasMerzlyakov/go-metrics/internal/server/adapter/http/handler"
"github.com/StasMerzlyakov/go-metrics/internal/server/adapter/http/middleware"
"github.com/StasMerzlyakov/go-metrics/internal/server/adapter/http/middleware/compress"
"github.com/StasMerzlyakov/go-metrics/internal/server/adapter/http/middleware/logging"
"github.com/StasMerzlyakov/go-metrics/internal/server/adapter/storage/memory"
"github.com/StasMerzlyakov/go-metrics/internal/server/adapter/storage/postgres"
"github.com/StasMerzlyakov/go-metrics/internal/server/adapter/storage/wrapper"
"github.com/StasMerzlyakov/go-metrics/internal/server/app"
"github.com/StasMerzlyakov/go-metrics/internal/server/domain"
"github.com/go-chi/chi/v5"
"github.com/jackc/pgerrcode"
"github.com/jackc/pgx/v5/pgconn"

"go.uber.org/zap"
)

type Server interface {
Start(ctx context.Context)
Shutdown(ctx context.Context)
}

func createMiddleWareList(log *zap.SugaredLogger) []func(http.Handler) http.Handler {
return []func(http.Handler) http.Handler{
logging.NewLoggingResponseMW(log),
compress.NewCompressGZIPResponseMW(log), //compress.NewCompressGZIPBufferResponseMW(log),
compress.NewUncompressGZIPRequestMW(log),
logging.NewLoggingRequestMW(log),
}
}

type FullStorage interface {
app.Storage
app.Pinger
Bootstrap(ctx context.Context) error
Close(ctx context.Context) error
}

func main() {

srvConf, err := server.LoadConfig()
// -------- Контекст сервера ---------
srvCtx, cancelFn := context.WithCancel(context.Background())

// -------- Конфигурация ----------
srvConf, err := config.LoadServerConfig()
if err != nil {
panic(err)
}

// -------- Логгер ---------------
logger, err := zap.NewDevelopment()
if err != nil {
log.Fatal(err)
// вызываем панику, если ошибка
panic("cannot initialize zap")
}
defer logger.Sync()

sugarLog := logger.Sugar()

// -------- Хранилище -------------
var storage FullStorage

if srvConf.DatabaseDSN != "" {
storage = postgres.NewStorage(sugarLog, srvConf.DatabaseDSN)

// при работе с Postgres добавляем retriable-обертку

// функция, обрабатывающая ошибки; в нужных случаях выкидывает нужную ошибку(domain.ErrDBConnection)
// на которую реагирует retriableWrapper
pgErrPreProcFn := func(err error) error {
if err == nil {
return nil
}
var pgErr *pgconn.PgError
if errors.As(err, &pgErr) {
if pgerrcode.IsConnectionException(pgErr.Code) {
return domain.ErrDBConnection
}
}

var conErr *pgconn.ConnectError
if errors.As(err, &conErr) {
return domain.ErrDBConnection
}
return err
}

retriableConf := retriable.DefaultConfFn(domain.ErrDBConnection, pgErrPreProcFn)
storage = wrapper.NewRetriable(retriableConf, sugarLog, storage)

if err := server.CreateServer(srvConf); err != nil {
} else {
storage = memory.NewStorage()
}

defer storage.Close(srvCtx)

if err := storage.Bootstrap(srvCtx); err != nil {
panic(err)
}

// -------- Бэкап ------------
backupFomratter := backup.NewJSON(sugarLog, srvConf.FileStoragePath)
backUper := app.NewBackup(sugarLog, storage, backupFomratter)

if srvConf.Restore {
// восстановленгие бэкапа
if err := backUper.RestoreBackUp(srvCtx); err != nil {
panic(err)
}
}

// проверяем - нужен ли синхронный бэкап
doSyncBackup := srvConf.StoreInterval == 0

if !doSyncBackup {
// запускаем фоновый процесс
go func() {
storeInterval := time.Duration(srvConf.StoreInterval) * time.Second
var ticker = time.NewTicker(storeInterval)
defer ticker.Stop()
for {
select {
case <-srvCtx.Done():
sugarLog.Infow("Run", "msg", "backup finished")
return
case <-ticker.C:
if err := backUper.DoBackUp(srvCtx); err != nil {
sugarLog.Fatalw("DoBackUp", "msg", err.Error())
}
}
}
}()

}

// ---------- Http сервер -----------
httpHandler := chi.NewMux()

// мидлы
mwList := createMiddleWareList(sugarLog)
middleware.Add(httpHandler, mwList...)

// операции с метриками
metricApp := app.NewMetrics(storage)
handler.AddMetricOperations(httpHandler, metricApp, sugarLog)

// административные операции
adminApp := app.NewAdminApp(sugarLog, storage)
handler.AddAdminOperations(httpHandler, adminApp, sugarLog)

// запускаем http-сервер
srv := &http.Server{
Addr: srvConf.URL,
Handler: httpHandler,
ReadTimeout: 0,
IdleTimeout: 0,
}

go func() {
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
sugarLog.Fatalw("ListenAndServe", "msg", err.Error())
panic(err)
}
}()

// --------------- Обрабатываем остановку сервера --------------
exit := make(chan os.Signal, 1)
signal.Notify(exit, os.Interrupt, syscall.SIGTERM)

defer func() {
cancelFn()
srv.Shutdown(srvCtx)
}()
<-exit
}
29 changes: 24 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,31 @@ module github.com/StasMerzlyakov/go-metrics
go 1.19

require (
github.com/caarlos0/env v3.5.0+incompatible // indirect
github.com/caarlos0/env v3.5.0+incompatible
github.com/go-chi/chi/v5 v5.0.11
github.com/go-resty/resty/v2 v2.11.0
github.com/golang/mock v1.6.0
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa
github.com/jackc/pgx/v5 v5.5.4
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.8.4
github.com/ungerik/go-pool v0.0.0-20140720100922-d102a2c7872a
go.uber.org/zap v1.27.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-chi/chi/v5 v5.0.11 // indirect
github.com/go-resty/resty/v2 v2.11.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.8.4 // indirect
golang.org/x/net v0.17.0 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/net v0.22.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading
Loading