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

Sprint 04 #6

Merged
merged 12 commits into from
Feb 2, 2023
Merged

Sprint 04 #6

merged 12 commits into from
Feb 2, 2023

Conversation

fev0ks
Copy link
Owner

@fev0ks fev0ks commented Jan 23, 2023

No description provided.

@fev0ks fev0ks force-pushed the origin/feature/sprint_04 branch 5 times, most recently from d5e9ac0 to 3833023 Compare January 23, 2023 18:07
@fev0ks fev0ks force-pushed the origin/feature/sprint_04 branch 3 times, most recently from 5c53bca to 6675a9a Compare January 23, 2023 19:10
@fev0ks fev0ks force-pushed the origin/feature/sprint_04 branch from 6675a9a to 30ed88c Compare January 23, 2023 19:17
@fev0ks fev0ks force-pushed the origin/feature/sprint_04 branch from 098a6b0 to ee2c6bd Compare January 23, 2023 19:39
@fev0ks fev0ks force-pushed the origin/feature/sprint_04 branch from 38a3711 to 6966ab7 Compare January 24, 2023 07:00
)

type bulkSender struct {
msCtx context.Context

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

контекст нежелательно хранить в рамках описания структур
придерживаться правила keep Context out of structs

из офф.документации
image

использовать контекст в рамках передаче параметров, таймаута при вызовах API


для ознакомления
для ознакомления

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

client *resty.Client
metrics []*model.Metric
batchLimit int
mutex sync.RWMutex

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

можно использовать возможности эмбеддинга без указания названия параметра (Anonymous Embedded Fields)

type bulkSender struct {
    ...
    sync.RWMutex
}

при таком описании вызовы можно сократить до

s.Lock()
defer s.Unlock()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

return useBuffSendClient
}

func GetBuffBatchLimit() int {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

реализация методов аналогична пакету
можно переиспользовать его

}

func GetBuffSendInterval() time.Duration {
buffSendInterval := os.Getenv("BUFF_SEND_INTERVAL")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BUFF_SEND_INTERVAL - неизменняеые переменные (текст, числа) лучше определять как константы

@@ -46,3 +49,37 @@ func GetServerAddress() string {
func GetHashKey() string {
return os.Getenv("KEY")
}

func UseBuffSenderClient() bool {
useBuffSendClient, err := strconv.ParseBool(os.Getenv("USE_BUFF_SEND_CLIENT"))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

можно не использовать дополнительный флаг, а использовать ранее описанную переменную BUFF_BATCH_LIMIT:
если значение 0 или -1, то обычная отправка
если значение не 0 (в идеале >= 2), то использовать отправку метрик "пачками"

metrics: make([]*model.Metric, 0, batchLimit),
batchMetricsCh: make(chan []*model.Metric),
}
go sender.startSenderListener(msCtx, sendInterval)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

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


например,

func myFunc1():
    func2()
    func3()

func func2() {
    func4()
    ...
}

func func4() {
    ...

func func3() {
    ...

для данного файла:

NewBulkMetricSender()
startSenderListener()
uploadMetrics()
sendMetricsAsync()
sendMetrics()
SendMetric() <- можно в начало, сразу же после "конструктора" т.к. метод публичный

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

ticker.Stop()
return
}
if len(metrics) != 0 {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

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

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

в этом и была идея, если чанк заполнен - отправлять
если не заполнен и что то есть, то отправить через интервал, чтоб данные не зависли до след сбора

Copy link

@mr-fard4y mr-fard4y Jan 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

изначально, в конфигурации аганта уже задан параметр reportInterval, определяющий периоды отправки
до этого момента метрики будут копиться в некотором буфере, а далее отправляться
такое поведение эмулирует штатные ситуации, например:

  • метрики нужно отсылать с определенной периодичностью - 1/5/10м/30 мин, 1/4/12 часов - системы мониторинга вроде Zabbix, Nagios, Cacti, Prometheus, etc конфигурируются по такому принципу;
  • на сервере есть ограничение на отправку, которое обнуляется по некоторому периоду - но в такой ситуации лучше дополнительно обрабатывать ответ и повторять отправку n-е количество раз или с некоторой задержкой;

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

отправку данных сразу же при наполнении буфера можно предусмотреть, когда reportInterval равен 0

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

func (s *bulkSender) uploadMetrics() []*model.Metric {
s.mutex.Lock()
metrics := s.metrics
s.metrics = make([]*model.Metric, 0, s.batchLimit)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

if len(s.metrics) >= s.batchLimit {
log.Printf("Flush batch of '%v' metrics\n", len(s.metrics))
s.batchMetricsCh <- s.metrics
s.metrics = make([]*model.Metric, 0, s.batchLimit)
Copy link

@mr-fard4y mr-fard4y Jan 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

можно попробовать:

  1. оставить только канал, как единую структуру взаимодействия
  2. использовать слайсы

детально про массив и слайсы
внутреннее устройство
так же посмотреть, как работает append/make с массивами/слайсами, и как меняется адресация, len, cap

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

func (s *bulkSender) startSenderListener(ctx context.Context, sendInterval time.Duration) {
ticker := time.NewTicker(sendInterval)
for {
var metrics []*model.Metric

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

метод усложнен

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@fev0ks fev0ks force-pushed the origin/feature/sprint_04 branch 6 times, most recently from 689362d to 4b046f2 Compare January 29, 2023 16:37
@fev0ks fev0ks force-pushed the origin/feature/sprint_04 branch from 4b046f2 to f9d0871 Compare January 29, 2023 16:48
@@ -41,6 +41,7 @@ func (eh *ExitHandler) shutdown() {
select {
case <-successfullyFinished:
log.Println("System finished work, graceful shutdown")
//time.Sleep(time.Second)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

неиспользуемый код лучше сразу же вычищать
стараться не увеличивать энтропию ( :

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ок

}
}

// Looks like useless for current project specific :((((

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

неиспользумый код лучше сразу же вычищать

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

нуу ок

}

func (cmr *commonMetricCollector) processGopsMetrics() {
metrics := make([]*model.Metric, 0)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

можно сразу же задать емкость структуры:
минимум - 3 метрики (общая память, свободная память, загрузка 1го процессора), максимум - динамическое значение, в зависимости от количества ядер

metrics := make([]*model.Metric, 0, 3)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

memoryStat, _ := mem.VirtualMemory()
metrics = append(metrics, cmr.mf.NewGaugeMetric("TotalMemory", model.GaugeVT(memoryStat.Total)))
metrics = append(metrics, cmr.mf.NewGaugeMetric("FreeMemory", model.GaugeVT(memoryStat.Free)))
cpuUsed, _ := cpu.Percent(time.Second*10, true)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"магическое число" в параметре вызова


параметр периода опроса можно задать как 0

If an interval of 0 is given it will compare the current cpu times against the last call.

или пробрасывать из конфигурации

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Магическим его сложно назвать, согалсно документации оно задано однозначно и не предполагает изменений в дальнейшем через параметры или флаги :)
Имя метрики: "CPUutilization1", тип: gauge - загрузка ядра в процентах за 10 секунд (точное количество - по числу CPU определяемое во время исполнения)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

при изменении ТЗ придется изменять все значения в кода, которые используют данное значение

при выносе в константы или в рамках параметра структуры получаем некоторую параметризацию
и минимальный дифф для изменений

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

добавил константу

func (cmr *commonMetricRepository) GetMetrics() []*model.Metric {
cmr.Lock()
defer cmr.Unlock()
metrics := make([]*model.Metric, 0, len(cmr.cache))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

начиная с 1.18 версии, появился аналогичный фунционал получения значений - map.Values

values := maps.Values(<map>)

пакет экспериментальный: использование на свое усмотрение
можно ознакомиться с реализациями, как примеры использования дженериков

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

в целом там тоже самое, тащить експериментальный импорт ради одной функции мб не лучшая идея, но использовал для "пусть будет так"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

можно не тащить весь пакет, а только скопипастить реализации

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

@@ -49,9 +49,9 @@ func (cmp *commonMetricPoller) PollMetrics() chan struct{} {
case <-ticker.C:
start := time.Now()
log.Println("Poll metrics start")
metrics := cmp.mr.GetMetricsList()
metrics := cmp.mr.GetMetrics()
cmp.sendMetrics(metrics)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

обновил

err := js.SendMetrics(js.buffer)
if err != nil {
return fmt.Errorf("cannot send batch of metrics: %v", err)
func (js *jsonSender) SendMetrics(ctx context.Context, metrics []*model.Metric) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

методы дублируется в text_sender и bulk_sender

стоит подумать над объединением общего кода и выноса в некоторую абстракцию или независимый метод

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

text и json согласен,
в bulk все же по другому

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

выделил разные интерфейсы MetricSender - MetricsSender и композицию для общего Sender

case metrics := <-s.batchMetricsCh:
log.Printf("Sending bulk %d metrics", len(metrics))
err := s.sendMetrics(metrics)
if err != nil {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

хорошая практика возвращать ошибку в рамках выполнения метода
но не "глушить" явно

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

обновил

//}

func (s *bulkSender) startSendMetricsListener(ctx context.Context) {
go func() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

горутины можно запускать явно через указание go перед вызываемым методом

go sender.startSendMetricsListener(msCtx)

такой подход дает возможность запускать метод как синхронно, так и асинхронно

нет необходимости определять и вызывать анонимные функции в рамках метода

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

убрал листенер, тк в целом все тормозилось об каналы и в асинхронности нет смысла + обработка ошибок затрудняется - нужен до канал на них

sender := &bulkSender{
client: client,
batchLimit: batchLimit,
metrics: make([]*model.Metric, 0, batchLimit),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

неиспользуемый параметр
можно убрать из определения

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

убрал

@fev0ks fev0ks force-pushed the origin/feature/sprint_04 branch from edca830 to d8fa5bf Compare January 31, 2023 06:26
@fev0ks
Copy link
Owner Author

fev0ks commented Jan 31, 2023

Огромное спасибо за ревью!
На текущем месте работы такого фидбека очень не хватает :)

Copy link

@mr-fard4y mr-fard4y left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

if dbConfig != "" {
repository = repositories.NewPgRepository(dbConfig, ctx)

if appConfig.DBConfig != "" {
Copy link

@mr-fard4y mr-fard4y Jan 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

27-45 можно инкапсулировать в рамках пакета: вынести в отдельный метод в рамках хранилища/repositories

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

@fev0ks fev0ks merged commit 24a407c into main Feb 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants