Speicher
m.DB ist ein dauerhafter Schlüssel-Wert-Speicher, der auf die Platte geschrieben wird und Neustarts übersteht.
Besitzer und Schlüssel
Jeder Wert liegt unter einem Besitzer — nimm m.Name(), damit deine Daten deine bleiben:
m.DB.SetString(m.Name(), "last_city", "Berlin")
city := m.DB.GetString(m.Name(), "last_city", "")Getter nehmen einen Standardwert, der bei fehlendem Schlüssel zurückkommt — das „gibt es den überhaupt"-Tänzchen entfällt.
Typisierter Zugriff
m.DB.SetString(m.Name(), "city", "Berlin")
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{"Berlin", "Hamburg"})
m.DB.SetInt64Slice(m.Name(), "admins", []int64{1, 2, 3})
m.DB.SetStringMap(m.Name(), "labels", map[string]string{"de": "Berlin"})
m.DB.SetAnyMap(m.Name(), "state", map[string]any{"n": 1})Zu jedem gibt es einen Getter mit Standardwert:
city := m.DB.GetString(m.Name(), "city", "Berlin")
count := m.DB.GetInt(m.Name(), "count", 0)
enabled := m.DB.GetBool(m.Name(), "enabled", false)
cities := m.DB.GetStringSlice(m.Name(), "cities", nil)Schreibfehler behandeln
Schreibvorgänge gehen auf die Platte und können scheitern. Den Fehler zu ignorieren heißt, Daten stillschweigend zu verlieren:
if err := m.DB.SetString(m.Name(), "city", city); err != nil {
return fmt.Errorf("Stadt speichern: %w", err)
}Lesen kann nicht scheitern — dafür ist der Standardwert da.
Daten pro Chat und Nutzer
Setz den Schlüssel zusammen:
func chatKey(chatID int64) string {
return "chat:" + strconv.FormatInt(chatID, 10)
}
m.DB.SetString(m.Name(), chatKey(msg.ChatID), "Berlin")Bei vielen kleinen Werten ist eine Map unter einem Schlüssel günstiger als viele Schlüssel:
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("Städte speichern: %w", err)
}Was du speichern kannst
Nur JSON-serialisierbare Werte: Strings, Zahlen, Booleans sowie Slices und Maps davon. Etwas anderes zu speichern gibt einen Fehler zurück, statt die Datei zu beschädigen.
Getter geben eine Kopie zurück; die zurückgegebene Map zu verändern ändert also nichts am Gespeicherten — schreib sie zurück.
Kosten
Jeder Schreibvorgang serialisiert die Datenbank und schreibt die Datei neu. Bei üblichen Raten ist das in Ordnung, aber schreibe in einem Watcher nicht einmal pro eingehender Nachricht. Sammle im Speicher und schreibe periodisch:
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)
}Geheimnisse
Werte, die nie in einem Backup auftauchen dürfen, gehören in ein Konfigurationsfeld mit Secret: true — siehe Konfiguration. Gewöhnliche DB-Werte landen unverändert im Backup.