Skip to content

ストレージ

m.DB は永続的なキーバリューストアです。ディスクに保存され、再起動をまたいで 残ります。

オーナーとキー

すべての値はオーナーの下に置かれます。自分のデータを自分のものに保つため 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{"jp": "東京"})
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.