Skip to content

Commit

Permalink
Iter20 (#23)
Browse files Browse the repository at this point in the history
* добавил TODO

* актуализировал вопросы

* переделал metric_operation_handler - убрал все что относится к бизнес-логике
сделал более аккуратную обработку ошибок

* восстанавливаю тесты;
актуализировал вопросы

* убрал лишний код, поправил мидлу с логированием статуса ответа
(см Questions.md)

* актуализировал вопросы

* реализовал agent

* отладил проверку запроса

* мидла на ответ
отладился, прошел локальный тест

* поправпил статику

* актуализировал вопросы

* iter15

* переименовал sender, отвечаюищий за пул
добавил тест

* поправил время в time.Sleep

* добавил вопрос по горутины

* iter16

* передалал на общий логгер

* добавил профилирование

* Наработки

* поправил импорт

* добавил вопрос

* ещё правка

* добавил скрипт для локального прогона тестов

* добавил скрипт форматирования
поправил форматирвоание

* отформатировал goimports

* убрал мусор

* добавил скрипт для сравнения результатов

* инкремент 18
обновил вопросы

* поправил ReadAll, добавил Questions

* Актуализировал TODO, Questions по итогам созвона

* добавил requestID мидлу; убрал лишие выводы в лог

* добавил тестов с testcontainers

* добавил retriable мидлу, убрал retriable-wrapper перед хранилищем
добавил маппинг внутренних ошибок на ошибки http

* поправил проект для работы staticcheck

* перенес retryable в agent - применяется только там; server переведен на retrymw

* наработки

* добавил config.json

* отключил проверку генерируемых go test файлов

* добавил глобальные переменные

* поправил вызов shell-команд через Makefile

* убрал бинарник из исходников
  • Loading branch information
StasMerzlyakov authored Jun 12, 2024
1 parent 717b48f commit 2655119
Show file tree
Hide file tree
Showing 58 changed files with 1,854 additions and 708 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ cmd/agent/main
cmd/agent/agent
cmd/server/main
cmd/server/server

cmd/staticlint/staticlint
# Dependency directories (remove the comment below to include it)
vendor/

Expand Down
13 changes: 13 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.PHONY: build test clean

build:
go clean -testcache
GOOS=linux GOARCH=amd64 go build -buildvcs=false -o=cmd/server ./cmd/server/...
GOOS=linux GOARCH=amd64 go build -buildvcs=false -ldflags "-X main.buildVersion=v1.0.1 -X main.buildDate=$(shell date +'%Y-%m-%d') -X main.buildCommit=$(shell git rev-parse HEAD)" -o=cmd/agent ./cmd/agent/...

test: build
GOOS=linux GOARCH=amd64 go build -buildvcs=false -o=cmd/staticlint ./cmd/staticlint/...
cmd/staticlint/staticlint ./...
go mod tidy
go mod vendor
go test ./...
49 changes: 31 additions & 18 deletions Questions.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,28 @@ go tool pprof -http=":9090" -seconds=30 heap.out
3. godoc

* Не могу добиться чтоб заработали ссылкы на структуры в другом файле например [domain.Metrics]. Хотя в документации например ioutil.ReadDir() ссылки работают.
* -play

4. staticlint

Не работает чтение файла конфигурации при вызове через
```go
go vet -vettool=cmd/staticlint/staticlint ./...
```

есть ли идеи?


5. go vet - Можно ли отключить конкретный чекер на конкретной строке?

Прим. internal/exitcheck/testdata/pkg2/pkg2.go


6. (__ответ получен__ - Да) Цикл в b.Run
* play


4. Цикл в b.Run (__ответ получен__ - Да)
7. Цикл в b.Run (__ответ получен__ - Да)

Цикл в bench-тесте: до b.N или до фиксированного testN?

Expand All @@ -40,21 +58,16 @@ go tool pprof -http=":9090" -seconds=30 heap.out

*Если нужно делать профилирование(cpu/memory) - то нужно ставить функции в одинаковые условия запускать цикл фиксированное число раз; затем смотреть на результаты профилирования*

5. Статический анализатор для выявления не проверенных ошибок типа (отложить до следующего спринат? (там про go vet было?))
```go
_, _ = io.ReadAll(req.Body)
```
и необрабываемого проброса ошибок
```go
if _, err ;= io.ReadAll(req.Body); err!=nil {
return err
}
```

6. Не понял комментарий на строку
```go
logger.Infow("GetAllMetrics", "status", "start")

7. (__ответ получен__) Статический анализатор для выявления не проверенных ошибок типа (отложить до следующего спринат? (там про go vet было?))

[golangci](https://golangci-lint.run/)

[errcheck](https://github.com/kisielk/errcheck)

```bash
golangci-lint run ./...

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


18 changes: 17 additions & 1 deletion cmd/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,38 @@ package main

import (
"context"
"fmt"
"log"
"os"
"os/signal"
"syscall"

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

var (
buildVersion string = "N/A"
buildDate string = "N/A"
buildCommit string = "N/A"
)

func printVersion() {
fmt.Printf("Build version: %s\n", buildVersion)
fmt.Printf("Build date: %s\n", buildDate)
fmt.Printf("Build commit: %s\n", buildCommit)
}

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

func main() {

printVersion()

agentCfg, err := config.LoadAgentConfig()
if err != nil {
log.Fatal(err)
Expand Down
54 changes: 26 additions & 28 deletions cmd/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,16 @@ import (
"syscall"
"time"

"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/digest"
"github.com/StasMerzlyakov/go-metrics/internal/server/adapter/http/middleware/logging"
"github.com/StasMerzlyakov/go-metrics/internal/server/adapter/http/middleware/retry"
"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"
Expand All @@ -36,6 +35,7 @@ type Server interface {

func createMiddleWareList(srvConf *config.ServerConfiguration) []func(http.Handler) http.Handler {
var mwList []func(http.Handler) http.Handler
mwList = append(mwList, logging.EncrichWithRequestIDMW())
mwList = append(mwList, logging.NewLoggingResponseMW())
if srvConf.Key != "" {
mwList = append(mwList, digest.NewWriteHashDigestResponseHeaderMW(srvConf.Key))
Expand All @@ -45,6 +45,30 @@ func createMiddleWareList(srvConf *config.ServerConfiguration) []func(http.Handl

mwList = append(mwList, logging.NewLoggingRequestMW())

// при работе с 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
}
mwList = append(mwList, retry.NewRetriableRequestMWConf(
time.Duration(time.Second), time.Duration(2*time.Second), 4, pgErrPreProcFn,
))

return mwList
}

Expand Down Expand Up @@ -83,32 +107,6 @@ func main() {

if srvConf.DatabaseDSN != "" {
storage = postgres.NewStorage(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)

} else {
storage = memory.NewStorage()
}
Expand Down
172 changes: 172 additions & 0 deletions cmd/staticlint/staticlint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// Package main contains project multicheck linter:
// - standart golang.org/x/tools/go/analysis
// - SAXXX + QF1004 staticcheck.io
// - github.com/jingyugao/rowserrcheck/passes/rowserr
// - github.com/kisielk/errcheck/errcheck
//
// If `config.json` exists in the current project directory it will be used for reading excludedchecks.
//
// content: {
//
// "excludedcheck": ["appends", "QF1004"]
// }
package main

import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/StasMerzlyakov/go-metrics/internal/osexit"
"github.com/jingyugao/rowserrcheck/passes/rowserr"
"github.com/kisielk/errcheck/errcheck"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/multichecker"
"golang.org/x/tools/go/analysis/passes/appends"
"golang.org/x/tools/go/analysis/passes/asmdecl"
"golang.org/x/tools/go/analysis/passes/assign"
"golang.org/x/tools/go/analysis/passes/atomic"
"golang.org/x/tools/go/analysis/passes/bools"
"golang.org/x/tools/go/analysis/passes/buildtag"
"golang.org/x/tools/go/analysis/passes/cgocall"
"golang.org/x/tools/go/analysis/passes/composite"
"golang.org/x/tools/go/analysis/passes/copylock"
"golang.org/x/tools/go/analysis/passes/defers"
"golang.org/x/tools/go/analysis/passes/directive"
"golang.org/x/tools/go/analysis/passes/errorsas"
"golang.org/x/tools/go/analysis/passes/framepointer"
"golang.org/x/tools/go/analysis/passes/httpresponse"
"golang.org/x/tools/go/analysis/passes/ifaceassert"
"golang.org/x/tools/go/analysis/passes/loopclosure"
"golang.org/x/tools/go/analysis/passes/lostcancel"
"golang.org/x/tools/go/analysis/passes/nilfunc"
"golang.org/x/tools/go/analysis/passes/printf"
"golang.org/x/tools/go/analysis/passes/shift"
"golang.org/x/tools/go/analysis/passes/sigchanyzer"
"golang.org/x/tools/go/analysis/passes/slog"
"golang.org/x/tools/go/analysis/passes/stdmethods"
"golang.org/x/tools/go/analysis/passes/stringintconv"
"golang.org/x/tools/go/analysis/passes/structtag"
"golang.org/x/tools/go/analysis/passes/testinggoroutine"
"golang.org/x/tools/go/analysis/passes/tests"
"golang.org/x/tools/go/analysis/passes/timeformat"
"golang.org/x/tools/go/analysis/passes/unmarshal"
"golang.org/x/tools/go/analysis/passes/unreachable"
"golang.org/x/tools/go/analysis/passes/unsafeptr"
"golang.org/x/tools/go/analysis/passes/unusedresult"
"honnef.co/go/tools/staticcheck"
)

type ConfigData struct {
ExcludedChecks []string `json:"excludedChecks"`
}

const ConfigX = "config.json"

func main() {

var analyzers []*analysis.Analyzer
var cfg ConfigData

ex, err := os.Executable()
if err != nil {
panic(err)
}

exPath := filepath.Dir(ex)
confFilePath := filepath.Join(exPath, ConfigX)
fmt.Println(confFilePath)

data, err := os.ReadFile(ConfigX)
if err != nil {
fmt.Printf("can't find conf file: %s; used default conifguration\n", ConfigX)
} else {
if err = json.Unmarshal(data, &cfg); err != nil {
panic(err)
}
}

excludedChecks := make(map[string]bool)
for _, v := range cfg.ExcludedChecks {
excludedChecks[v] = true
}

standardAnalyzers := []*analysis.Analyzer{
appends.Analyzer, // check for missing values after append
asmdecl.Analyzer, // report mismatches between assembly files and Go declarations
assign.Analyzer, // check for useless assignments
atomic.Analyzer, // check for common mistakes using the sync/atomic package
bools.Analyzer, // check for common mistakes involving boolean operators
buildtag.Analyzer, // check //go:build and // +build directives
cgocall.Analyzer, // detect some violations of the cgo pointer passing rules
composite.Analyzer, // check for unkeyed composite literals
copylock.Analyzer, // check for locks erroneously passed by value
defers.Analyzer, // report common mistakes in defer statements
directive.Analyzer, // check Go toolchain directives such as //go:debug
errorsas.Analyzer, // report passing non-pointer or non-error values to errors.As
framepointer.Analyzer, // report assembly that clobbers the frame pointer before saving it
httpresponse.Analyzer, // check for mistakes using HTTP responses
ifaceassert.Analyzer, // detect impossible interface-to-interface type assertions
loopclosure.Analyzer, // check references to loop variables from within nested functions
lostcancel.Analyzer, // check cancel func returned by context.WithCancel is called
nilfunc.Analyzer, // check for useless comparisons between functions and nil
printf.Analyzer, // check consistency of Printf format strings and arguments
shift.Analyzer, // check for shifts that equal or exceed the width of the integer
sigchanyzer.Analyzer, // check for unbuffered channel of os.Signal
slog.Analyzer, // check for invalid structured logging calls
stdmethods.Analyzer, // check signature of methods of well-known interfaces
stringintconv.Analyzer, // check for string(int) conversions
structtag.Analyzer, // check that struct field tags conform to reflect.StructTag.Get
testinggoroutine.Analyzer, // report calls to (*testing.T).Fatal from goroutines started by a test
tests.Analyzer, // check for common mistaken usages of tests and examples
timeformat.Analyzer, // check for calls of (time.Time).Format or time.Parse with 2006-02-01
unmarshal.Analyzer, // report passing non-pointer or non-interface values to unmarshal
unreachable.Analyzer, // check for unreachable code
unsafeptr.Analyzer, // check for invalid conversions of uintptr to unsafe.Pointer
unusedresult.Analyzer, // check for unused results of calls to some functions
}

// golang.org/x/tools/go/analysis/passes analazers
for _, v := range standardAnalyzers {
if !excludedChecks[v.Name] {
analyzers = append(analyzers, v)
}
}

// SA + QF1004 staticcheck.io
for _, v := range staticcheck.Analyzers {
if !excludedChecks[v.Analyzer.Name] {
if strings.HasPrefix(v.Analyzer.Name, "SA") ||
v.Analyzer.Name == "QF1004" { // use strings.ReplaceAll instead of strings.Replace with n == -1
analyzers = append(analyzers, v.Analyzer)
}
}
}

// errcheck
errCheck := errcheck.Analyzer
if !excludedChecks[errCheck.Name] {
analyzers = append(analyzers, errCheck) // check for unchecked errors in Go code.
}

// rowserrcheck
rser := rowserr.NewAnalyzer(
"github.com/StasMerzlyakov/go-metrics/internal/server/adapter/storage/postgres",
)

if !excludedChecks[rser.Name] {
analyzers = append(analyzers, rser) // rowserrcheck is a static analysis tool which checks whether sql.Rows.Err is correctly checked
}

// osexit
ocheck := osexit.Analyzer
if !excludedChecks[ocheck.Name] {
analyzers = append(analyzers, ocheck) // prohibits the use of a direct os.Exit call in the main function of the main package.
}

multichecker.Main(
analyzers...,
)
}
3 changes: 3 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"excludedChecks": ["errcheck"]
}
Empty file modified cover.sh
100644 → 100755
Empty file.
Loading

0 comments on commit 2655119

Please sign in to comment.