Skip to content

Хранилище

m.DB — постоянное key-value хранилище, которое пишется на диск и переживает перезапуски.

Владелец и ключ

Каждое значение живёт под владельцем — используйте m.Name(), чтобы ваши данные оставались вашими:

go
m.DB.SetString(m.Name(), "last_city", "Москва")
city := m.DB.GetString(m.Name(), "last_city", "")

Геттеры принимают значение по умолчанию, которое вернётся при отсутствии ключа, так что плясок «а есть ли такой ключ» не требуется.

Типизированный доступ

go
m.DB.SetString(m.Name(), "city", "Москва")
m.DB.SetInt(m.Name(), "count", 42)
m.DB.SetInt64(m.Name(), "user_id", 123456789)
m.DB.SetBool(m.Name(), "enabled", true)
m.DB.SetStringSlice(m.Name(), "cities", []string{"Москва", "Казань"})
m.DB.SetInt64Slice(m.Name(), "admins", []int64{1, 2, 3})
m.DB.SetStringMap(m.Name(), "labels", map[string]string{"ru": "Москва"})
m.DB.SetAnyMap(m.Name(), "state", map[string]any{"n": 1})

У каждого есть парный геттер со значением по умолчанию:

go
city    := m.DB.GetString(m.Name(), "city", "Москва")
count   := m.DB.GetInt(m.Name(), "count", 0)
enabled := m.DB.GetBool(m.Name(), "enabled", false)
cities  := m.DB.GetStringSlice(m.Name(), "cities", nil)

Обрабатывайте ошибки записи

Запись идёт на диск и может не удаться. Проигнорировать ошибку — значит молча потерять данные:

go
if err := m.DB.SetString(m.Name(), "city", city); err != nil {
	return fmt.Errorf("сохранение города: %w", err)
}

Чтение упасть не может — для этого и нужно значение по умолчанию.

Данные по чатам и пользователям

Составьте ключ:

go
func chatKey(chatID int64) string {
	return "chat:" + strconv.FormatInt(chatID, 10)
}

m.DB.SetString(m.Name(), chatKey(msg.ChatID), "Москва")

Если мелких значений много, карта под одним ключом дешевле, чем много ключей:

go
cities := m.DB.GetStringMap(m.Name(), "cities_by_chat", nil)
if cities == nil {
	cities = map[string]string{}
}
cities[strconv.FormatInt(msg.ChatID, 10)] = city
if err := m.DB.SetStringMap(m.Name(), "cities_by_chat", cities); err != nil {
	return fmt.Errorf("сохранение городов: %w", err)
}

Что можно хранить

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

Геттеры возвращают копию, поэтому изменение полученной карты не меняет сохранённое — записывайте её обратно.

Цена

Каждая запись сериализует базу и перезаписывает файл. При обычной частоте это нормально, но не пишите по разу на каждое входящее сообщение в вотчере. Накапливайте в памяти и сбрасывайте периодически:

go
type Counter struct {
	goroku.Base
	mu     sync.Mutex
	counts map[int64]int
}

func (m *Counter) flush() error {
	m.mu.Lock()
	snapshot := make(map[string]int, len(m.counts))
	for id, n := range m.counts {
		snapshot[strconv.FormatInt(id, 10)] = n
	}
	m.mu.Unlock()
	return m.DB.SetStringMapInt(m.Name(), "counts", snapshot)
}

Секреты

Значения, которых не должно быть в бэкапе, объявляйте полем настройки с Secret: true — см. Настройки. Обычные значения в DB попадают в бэкап как есть.

Released under the GNU AGPL v3 License.