2026-04-04 20:24:21 +00:00
|
|
|
// Package store provides SQLite-backed grouped key-value storage with TTL,
|
|
|
|
|
// namespace isolation, quota enforcement, reactive events, journal writes,
|
|
|
|
|
// workspace buffering, cold archive compaction, and orphan recovery.
|
|
|
|
|
//
|
2026-04-04 20:46:40 +00:00
|
|
|
// Prefer `store.NewConfigured(store.StoreConfig{...})` and
|
|
|
|
|
// `store.NewScopedConfigured(store.ScopedStoreConfig{...})` when the
|
|
|
|
|
// configuration is already known:
|
2026-04-04 20:24:21 +00:00
|
|
|
//
|
|
|
|
|
// configuredStore, err := store.NewConfigured(store.StoreConfig{
|
|
|
|
|
// DatabasePath: ":memory:",
|
|
|
|
|
// Journal: store.JournalConfiguration{
|
|
|
|
|
// EndpointURL: "http://127.0.0.1:8086",
|
|
|
|
|
// Organisation: "core",
|
|
|
|
|
// BucketName: "events",
|
|
|
|
|
// },
|
|
|
|
|
// PurgeInterval: 20 * time.Millisecond,
|
|
|
|
|
// WorkspaceStateDirectory: "/tmp/core-state",
|
|
|
|
|
// })
|
|
|
|
|
//
|
|
|
|
|
// Workspace files live under `.core/state/` by default and can be recovered
|
|
|
|
|
// with `configuredStore.RecoverOrphans(".core/state/")` after a crash.
|
|
|
|
|
// Use `StoreConfig.Normalised()` when you want the default purge interval and
|
|
|
|
|
// workspace state directory filled in before passing the config onward.
|
2026-04-03 08:54:19 +00:00
|
|
|
//
|
2026-03-30 15:16:16 +00:00
|
|
|
// Usage example:
|
|
|
|
|
//
|
2026-03-30 16:41:56 +00:00
|
|
|
// func main() {
|
2026-04-04 16:09:14 +00:00
|
|
|
// configuredStore, err := store.NewConfigured(store.StoreConfig{
|
|
|
|
|
// DatabasePath: ":memory:",
|
|
|
|
|
// Journal: store.JournalConfiguration{
|
|
|
|
|
// EndpointURL: "http://127.0.0.1:8086",
|
|
|
|
|
// Organisation: "core",
|
|
|
|
|
// BucketName: "events",
|
|
|
|
|
// },
|
|
|
|
|
// PurgeInterval: 20 * time.Millisecond,
|
2026-04-04 19:45:52 +00:00
|
|
|
// WorkspaceStateDirectory: "/tmp/core-state",
|
2026-04-04 16:09:14 +00:00
|
|
|
// })
|
2026-04-03 06:47:39 +00:00
|
|
|
// if err != nil {
|
|
|
|
|
// return
|
|
|
|
|
// }
|
2026-04-04 16:09:14 +00:00
|
|
|
// defer configuredStore.Close()
|
2026-04-03 06:47:39 +00:00
|
|
|
//
|
2026-04-04 16:09:14 +00:00
|
|
|
// if err := configuredStore.Set("config", "colour", "blue"); err != nil {
|
2026-03-30 16:41:56 +00:00
|
|
|
// return
|
|
|
|
|
// }
|
2026-04-04 16:09:14 +00:00
|
|
|
// if err := configuredStore.SetWithTTL("session", "token", "abc123", 5*time.Minute); err != nil {
|
2026-03-30 16:41:56 +00:00
|
|
|
// return
|
|
|
|
|
// }
|
2026-03-30 16:33:07 +00:00
|
|
|
//
|
2026-04-04 16:09:14 +00:00
|
|
|
// colourValue, err := configuredStore.Get("config", "colour")
|
2026-03-30 16:41:56 +00:00
|
|
|
// if err != nil {
|
|
|
|
|
// return
|
|
|
|
|
// }
|
2026-03-30 19:29:48 +00:00
|
|
|
// fmt.Println(colourValue)
|
2026-03-30 16:33:07 +00:00
|
|
|
//
|
2026-04-04 16:09:14 +00:00
|
|
|
// for entry, err := range configuredStore.All("config") {
|
2026-03-30 19:29:48 +00:00
|
|
|
// if err != nil {
|
|
|
|
|
// return
|
|
|
|
|
// }
|
|
|
|
|
// fmt.Println(entry.Key, entry.Value)
|
2026-03-30 16:41:56 +00:00
|
|
|
// }
|
2026-03-30 17:02:21 +00:00
|
|
|
//
|
2026-04-04 16:09:14 +00:00
|
|
|
// events := configuredStore.Watch("config")
|
|
|
|
|
// defer configuredStore.Unwatch("config", events)
|
2026-03-30 17:02:21 +00:00
|
|
|
// go func() {
|
2026-04-03 04:44:45 +00:00
|
|
|
// for event := range events {
|
2026-03-30 17:32:09 +00:00
|
|
|
// fmt.Println(event.Type, event.Group, event.Key, event.Value)
|
2026-03-30 17:02:21 +00:00
|
|
|
// }
|
|
|
|
|
// }()
|
|
|
|
|
//
|
2026-04-04 16:09:14 +00:00
|
|
|
// unregister := configuredStore.OnChange(func(event store.Event) {
|
2026-03-30 17:32:09 +00:00
|
|
|
// fmt.Println("changed", event.Group, event.Key, event.Value)
|
2026-03-30 17:02:21 +00:00
|
|
|
// })
|
|
|
|
|
// defer unregister()
|
2026-03-30 19:29:48 +00:00
|
|
|
//
|
2026-04-04 09:34:54 +00:00
|
|
|
// scopedStore, err := store.NewScopedConfigured(
|
2026-04-04 16:09:14 +00:00
|
|
|
// configuredStore,
|
2026-04-04 09:34:54 +00:00
|
|
|
// store.ScopedStoreConfig{
|
|
|
|
|
// Namespace: "tenant-a",
|
|
|
|
|
// Quota: store.QuotaConfig{MaxKeys: 100, MaxGroups: 10},
|
|
|
|
|
// },
|
2026-03-30 19:29:48 +00:00
|
|
|
// )
|
|
|
|
|
// if err != nil {
|
|
|
|
|
// return
|
|
|
|
|
// }
|
2026-04-03 05:45:24 +00:00
|
|
|
// if err := scopedStore.SetIn("preferences", "locale", "en-GB"); err != nil {
|
2026-03-30 19:29:48 +00:00
|
|
|
// return
|
|
|
|
|
// }
|
|
|
|
|
//
|
2026-04-04 16:09:14 +00:00
|
|
|
// for groupName, err := range configuredStore.GroupsSeq("tenant-a:") {
|
2026-03-30 19:29:48 +00:00
|
|
|
// if err != nil {
|
|
|
|
|
// return
|
|
|
|
|
// }
|
|
|
|
|
// fmt.Println(groupName)
|
|
|
|
|
// }
|
2026-04-04 16:09:14 +00:00
|
|
|
//
|
|
|
|
|
// workspace, err := configuredStore.NewWorkspace("scroll-session")
|
|
|
|
|
// if err != nil {
|
|
|
|
|
// return
|
|
|
|
|
// }
|
|
|
|
|
// defer workspace.Discard()
|
|
|
|
|
//
|
|
|
|
|
// if err := workspace.Put("like", map[string]any{"user": "@alice"}); err != nil {
|
|
|
|
|
// return
|
|
|
|
|
// }
|
|
|
|
|
// if err := workspace.Put("profile_match", map[string]any{"user": "@charlie"}); err != nil {
|
|
|
|
|
// return
|
|
|
|
|
// }
|
|
|
|
|
// if result := workspace.Commit(); !result.OK {
|
|
|
|
|
// return
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// orphans := configuredStore.RecoverOrphans(".core/state")
|
|
|
|
|
// for _, orphanWorkspace := range orphans {
|
|
|
|
|
// fmt.Println(orphanWorkspace.Name(), orphanWorkspace.Aggregate())
|
|
|
|
|
// orphanWorkspace.Discard()
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// journalResult := configuredStore.QueryJournal(`from(bucket: "events") |> range(start: -24h)`)
|
|
|
|
|
// if !journalResult.OK {
|
|
|
|
|
// return
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// archiveResult := configuredStore.Compact(store.CompactOptions{
|
|
|
|
|
// Before: time.Now().Add(-30 * 24 * time.Hour),
|
|
|
|
|
// Output: "/tmp/archive",
|
|
|
|
|
// Format: "gzip",
|
|
|
|
|
// })
|
|
|
|
|
// if !archiveResult.OK {
|
|
|
|
|
// return
|
|
|
|
|
// }
|
2026-03-30 16:41:56 +00:00
|
|
|
// }
|
2026-03-26 11:17:06 +00:00
|
|
|
package store
|