Aleksandr
Stanislav
3030
Коллеги, подскажите пожалуйста. Есть канал, туда идет запись. Нужно читать из этого канала в буффер и по заполнению буфера, либо достижению какого-то таймаута скидывать из этого буфера в базу. Сейчас это делается через case <-time.After(5 * time.Second) (https://gist.github.com/anonymous/2311adbd6fd3316c2643cd0228ededc0)
Проблема в том, что этот таймаут "отсчитывается" от момента последнего чтения из канала. и если в канал идет запись, скажем, каждую секунду, а размер буфера, например, 1000, то через желаемые 5 секунд "сброса" в базу не произойдет. Он будет только, если эти 5 секунд вообще не будет ничего из канала приходить. Как реализовать желаемое?
🎉
Andrey
Коллеги, подскажите пожалуйста. Есть канал, туда идет запись. Нужно читать из этого канала в буффер и по заполнению буфера, либо достижению какого-то таймаута скидывать из этого буфера в базу. Сейчас это делается через case <-time.After(5 * time.Second) (https://gist.github.com/anonymous/2311adbd6fd3316c2643cd0228ededc0)
Проблема в том, что этот таймаут "отсчитывается" от момента последнего чтения из канала. и если в канал идет запись, скажем, каждую секунду, а размер буфера, например, 1000, то через желаемые 5 секунд "сброса" в базу не произойдет. Он будет только, если эти 5 секунд вообще не будет ничего из канала приходить. Как реализовать желаемое?
Могу наврать с синтаксисом и можно оптимальнее, но идея такая:
https://gist.github.com/Vesninandrey/9ca20ec81c687d444172d624ab53de89
3030
идея ясна, уже как раз в эту сторону начал эксперементировать
3030
спасибо!
heavycharged
читать из <-time.After(), это вообще законно? он же bool возращает.
Andrey
он Time возвращает
heavycharged
а, да, гоню.
heavycharged
кстати, лучше на NewTicker переделать.
Andrey
Ну, и это же канал. Он и создан чтобы из него читать
heavycharged
func (t Time) After(u Time) bool я сюда глянул сначала
heavycharged
на NewTicker там из-за утечки.
heavycharged
The underlying Timer is not recovered by the garbage collector until the timer fires. If efficiency is a concern, use NewTimer instead and call Timer.Stop if the timer is no longer needed.
3030
да, тикер выглядит более правильным
heavycharged
там нужно в условии написать, по заполнению буфера дернуть ticker.Close()
3030
попробую
Andrey
3030
А вот насколько в таком случае плохо использовать для буфера слайсы и каждый раз после flush'а создавать новый слайс? Можно же создать слайс известной длины (массив?) и писать/сбрасывать в/из него? тогда на каждой итерации не будет лишнего выделения памяти (и потом чистки) - верно понимаю?
Andrey
Верно
3030
спасибо
🎉
Я вот чото не пойму, вот образец простого веб-сервера.
package main
import (
"fmt"
"http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Привет %s!", r.URL.Path[1:])
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
Но вот не вижу здесь многопоточности, она под капотом? Или как это вообще работает?
Aleksandr
на каждый реквест открывается отдельная горутина
heavycharged
да, под капотом :)
🎉
А, всё понятно теперь, спасибо.
heavycharged
bytes.Buffer может как-то вычитать всю строку, если он уже проинициализирован, без создания strings.NewReader?
Aleksandr
heavycharged
методы посомтрел у буфера?
метода нет, а вообще вызов strings.NewReader выделяет какую-то память или нет, он же вроде строку копирует - а значит выделяет? я использую буфферы для построения SQL запросов, и там в некоторых случаях делается запись в буфер разных ORDER BY LIMIT и прочего, после чего вызываю buffer.String() и передаю параметры в запрос. Ко всему этому я прикрутил sync.Pool, чтобы буферы из пула брать а не создавать постоянно. Вот собственно возник вопрос, я тут выиграю в производительности, или нет?
Aleksandr
heavycharged
один отпал, но есть ещё несколько
Vitaly
А кто-нибудь юзает gobuffalo.io ? Тут вроде и миграции и фронт есть.
🎉
heavycharged
heavycharged
Я в терминах не силён)) Что такое враппер?
враппер - функция-обертка над другой, более низкоуровневой функцией. buffalo беглым взглядом глянул, они взяли кучу пакетов, объединили и написали врапперы, для удобства.
🎉
А, ну они вроде позиционируют это не как фреймворк, а как эко систему, для разрабов.
Vitaly
Нарыл недавнишний обзор buffalo - выглядит очень удобным https://www.youtube.com/watch?v=J0JnHNgPMRk
🎉
Чото упарился, никак не могу придумать как обойти ошибку.
sql: Scan error on column index 3: converting driver.Value type <nil> ("<nil>") to a int64: invalid syntax
Vasily
golang sql null handling
🎉
Да вроде как почитал, поменял в структуре на sql.NullInt64 и sql.NullString. Не помогает, что-то видимо не так делаю.
Stanislav
Stanislav
помогает, сам пользую
Заур
народ, вот такая проблема
при сравенении l.ch == '\n' выдаёт false, хотя l.ch руна и содержит символ \n. проверял, у обоих код 10
Заур
в чём может быть причина?
Заур
ой, всё, пардон))
Oleh
мне нужно сделать прокси, который будет пересылать через другой прокси (разпределять данные между ними)
посоветуйте пожалуйста как лутше сделать?
Oleh
пробовал
https://github.com/elazarl/goproxy
но что то не могу норм конект к проксе настроить
Oleh
к той другой, куда нужно проксировать
🎉
sql.NullInt64
Да я тоже прописал, не пойму как ошибку отловить. Может что не так пишу? Можно на образец взглянуть?
Stanislav
🎉
🎉
type DATA struct {
ID int64
ChatID sql.NullInt64
Balance sql.NullInt64
MessageID int
StateWhat sql.NullString
PathWhere sql.NullString
DateTime *time.Time
}
Читаю из базы
func ReturnUser(db *sql.DB, ChatID int64) (p *DATA, err error) {}
Если пользователя нет, пишу в неё.
p := new(DATA)
p.ChatID.Int64 = ChatID
p.MessageID = MessageID
p.StateWhat.String = "start"
p.PathWhere.String = "main"
InsertUser(db, p)
func InsertUser(db *sql.DB, p *DATA) (lastID int64, err error) {}
С первого подключения, я не вижу пользователя в базе, и прописываю его туда. Но так как Balance, в структуре я не заполнил, то в базу пишется nil(я так думаю). Затем, очищаю историю(это бот), и снова жму старт. Тут бот падает с этой ошибкой.
🎉
То есть он пытаясь читать юзера из базы встречает nil.
Stanislav
В nullint64 есть еще параметр Valid
Stanislav
Как и у всех null*
Andrey
Покажите сам запрос
Andrey
В смысле как делаете rows.Scan
🎉
func ReturnUser(db *sql.DB, ChatID int64) (p *DATA, err error) {
p = new(DATA)
err = db.QueryRow("SELECT * FROM data WHERE ChatID=?", ChatID).Scan(&p.ChatID, &p.Balance, &p.MessageID, &p.StateWhat, &p.PathWhere)
return p, err
}
Вот так, потом я его ещё проверяю.
if err != nil {
if err == sql.ErrNoRows {
// Если строки нет, то пишу в базу
}
log.Fatal(err)
}
Andrey
а MessageID у вас 100% не nil?
Andrey
Сделайте его тоже sql.NullInt64
просто ради интереса
Andrey
🎉
Andrey
Ну, подставьте в скан локальную переменную для теста
🎉
Anonymous
func ReturnUser(db *sql.DB, ChatID int64) (p *DATA, err error) {
p = new(DATA)
err = db.QueryRow("SELECT * FROM data WHERE ChatID=?", ChatID).Scan(&p.ChatID, &p.Balance, &p.MessageID, &p.StateWhat, &p.PathWhere)
return p, err
}
Вот так, потом я его ещё проверяю.
if err != nil {
if err == sql.ErrNoRows {
// Если строки нет, то пишу в базу
}
log.Fatal(err)
}
Прикольно ты придумал, а если будет конкурентное создание, то у тебя будет два(а то и более) юзеров?)
🎉
Andrey
😂
Andrey
Так, ладно)
🎉
Залочить структуру нуно?
Andrey
Andrey
Вам компилятор сообщает где у вас ошибка
Anonymous