Compare commits

...

3 Commits

Author SHA1 Message Date
Pijus Kamandulis 598f2837af Fix issues with persist flag; Use custom logger for badger 2025-04-03 23:48:20 +03:00
Pijus Kamandulis 28e3c0c3d8 Rename 'MapDS' to 'JsonDS'; Added some docs 2025-03-14 22:40:12 +02:00
Pijus Kamandulis 97eea30c97 Use msgpack instead of gob; Added data persistance for badger data store 2025-03-13 23:59:07 +02:00
24 changed files with 156 additions and 90 deletions
+13
View File
@@ -86,6 +86,7 @@ To disable SSL and run Cosmium on HTTP instead, you can use the `-DisableTls` fl
- **-Persist**: Saves data to the given path on application exit (When `-InitialData` argument is not supplied, it will try to load data from path supplied in `-Persist`) - **-Persist**: Saves data to the given path on application exit (When `-InitialData` argument is not supplied, it will try to load data from path supplied in `-Persist`)
- **-Port**: Listen port (default 8081) - **-Port**: Listen port (default 8081)
- **-LogLevel**: Sets the logging level (one of: debug, info, error, silent) (default info) - **-LogLevel**: Sets the logging level (one of: debug, info, error, silent) (default info)
- **-DataStore**: Allows selecting [storage backend](#data-storage-backends) (default "json")
These arguments allow you to configure various aspects of Cosmium's behavior according to your requirements. These arguments allow you to configure various aspects of Cosmium's behavior according to your requirements.
@@ -99,6 +100,18 @@ All mentioned arguments can also be set using environment variables:
- **COSMIUM_PORT** for `-Port` - **COSMIUM_PORT** for `-Port`
- **COSMIUM_LOGLEVEL** for `-LogLevel` - **COSMIUM_LOGLEVEL** for `-LogLevel`
### Data Storage Backends
Cosmium supports multiple storage backends for saving, loading, and managing data at runtime.
| Backend | Storage Location | Write Behavior | Memory Usage | Supports Initial JSON Load |
|----------|--------------------------|--------------------------|----------------------|----------------------------|
| `json` (default) | JSON file on disk 📄 | On application exit ⏳ | 🛑 More than Badger | ✅ Yes |
| `badger` | BadgerDB database on disk ⚡ | Immediately on write 🚀 | ✅ Less than JSON | ❌ No |
The `badger` backend is generally recommended as it uses less memory and writes data to disk immediately. However, if you need to load initial data from a JSON file, use the `json` backend.
# License # License
This project is [MIT licensed](./LICENSE). This project is [MIT licensed](./LICENSE).
+24 -8
View File
@@ -16,7 +16,7 @@ const (
) )
const ( const (
DataStoreMap = "map" DataStoreJson = "json"
DataStoreBadger = "badger" DataStoreBadger = "badger"
) )
@@ -33,8 +33,8 @@ func ParseFlags() ServerConfig {
persistDataPath := flag.String("Persist", "", "Saves data to given path on application exit") persistDataPath := flag.String("Persist", "", "Saves data to given path on application exit")
logLevel := NewEnumValue("info", []string{"debug", "info", "error", "silent"}) logLevel := NewEnumValue("info", []string{"debug", "info", "error", "silent"})
flag.Var(logLevel, "LogLevel", fmt.Sprintf("Sets the logging level %s", logLevel.AllowedValuesList())) flag.Var(logLevel, "LogLevel", fmt.Sprintf("Sets the logging level %s", logLevel.AllowedValuesList()))
dataStore := NewEnumValue("map", []string{DataStoreMap, DataStoreBadger}) dataStore := NewEnumValue("json", []string{DataStoreJson, DataStoreBadger})
flag.Var(dataStore, "DataStore", fmt.Sprintf("Sets the data store %s, (badger is currently in the experimental phase)", dataStore.AllowedValuesList())) flag.Var(dataStore, "DataStore", fmt.Sprintf("Sets the data store %s", dataStore.AllowedValuesList()))
flag.Parse() flag.Parse()
setFlagsFromEnvironment() setFlagsFromEnvironment()
@@ -77,11 +77,27 @@ func (c *ServerConfig) PopulateCalculatedFields() {
logger.SetLogLevel(logger.LogLevelInfo) logger.SetLogLevel(logger.LogLevelInfo)
} }
if c.DataStore == DataStoreBadger && fileInfo, err := os.Stat(c.PersistDataFilePath)
(c.InitialDataFilePath != "" || c.PersistDataFilePath != "") { if c.PersistDataFilePath != "" && !os.IsNotExist(err) {
logger.ErrorLn("InitialData and Persist options are currently not supported with Badger data store") if err != nil {
c.InitialDataFilePath = "" logger.ErrorLn("Failed to get file info for persist path:", err)
c.PersistDataFilePath = "" os.Exit(1)
}
if c.DataStore == DataStoreJson && fileInfo.IsDir() {
logger.ErrorLn("--Persist cannot be a directory when using json data store")
os.Exit(1)
}
if c.DataStore == DataStoreBadger && !fileInfo.IsDir() {
logger.ErrorLn("--Persist must be a directory when using Badger data store")
os.Exit(1)
}
}
if c.DataStore == DataStoreBadger && c.InitialDataFilePath != "" {
logger.ErrorLn("InitialData option is currently not supported with Badger data store")
os.Exit(1)
} }
} }
+1 -1
View File
@@ -13,7 +13,7 @@ import (
) )
func Test_Collections(t *testing.T) { func Test_Collections(t *testing.T) {
presets := []testPreset{PresetMapStore, PresetBadgerStore} presets := []testPreset{PresetJsonStore, PresetBadgerStore}
setUp := func(ts *TestServer, client *azcosmos.Client) *azcosmos.DatabaseClient { setUp := func(ts *TestServer, client *azcosmos.Client) *azcosmos.DatabaseClient {
ts.DataStore.CreateDatabase(datastore.Database{ID: testDatabaseName}) ts.DataStore.CreateDatabase(datastore.Database{ID: testDatabaseName})
+7 -7
View File
@@ -10,7 +10,7 @@ import (
"github.com/pikami/cosmium/api/config" "github.com/pikami/cosmium/api/config"
"github.com/pikami/cosmium/internal/datastore" "github.com/pikami/cosmium/internal/datastore"
badgerdatastore "github.com/pikami/cosmium/internal/datastore/badger_datastore" badgerdatastore "github.com/pikami/cosmium/internal/datastore/badger_datastore"
mapdatastore "github.com/pikami/cosmium/internal/datastore/map_datastore" jsondatastore "github.com/pikami/cosmium/internal/datastore/json_datastore"
"github.com/pikami/cosmium/internal/logger" "github.com/pikami/cosmium/internal/logger"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -26,7 +26,7 @@ func getDefaultTestServerConfig() *config.ServerConfig {
AccountKey: config.DefaultAccountKey, AccountKey: config.DefaultAccountKey,
ExplorerPath: "/tmp/nothing", ExplorerPath: "/tmp/nothing",
ExplorerBaseUrlLocation: config.ExplorerBaseUrlLocation, ExplorerBaseUrlLocation: config.ExplorerBaseUrlLocation,
DataStore: "map", DataStore: "json",
} }
} }
@@ -34,9 +34,9 @@ func runTestServerCustomConfig(configuration *config.ServerConfig) *TestServer {
var dataStore datastore.DataStore var dataStore datastore.DataStore
switch configuration.DataStore { switch configuration.DataStore {
case config.DataStoreBadger: case config.DataStoreBadger:
dataStore = badgerdatastore.NewBadgerDataStore() dataStore = badgerdatastore.NewBadgerDataStore(badgerdatastore.BadgerDataStoreOptions{})
default: default:
dataStore = mapdatastore.NewMapDataStore(mapdatastore.MapDataStoreOptions{}) dataStore = jsondatastore.NewJsonDataStore(jsondatastore.JsonDataStoreOptions{})
} }
api := api.NewApiServer(dataStore, configuration) api := api.NewApiServer(dataStore, configuration)
@@ -71,7 +71,7 @@ type testFunc func(t *testing.T, ts *TestServer, cosmosClient *azcosmos.Client)
type testPreset string type testPreset string
const ( const (
PresetMapStore testPreset = "MapDS" PresetJsonStore testPreset = "JsonDS"
PresetBadgerStore testPreset = "BadgerDS" PresetBadgerStore testPreset = "BadgerDS"
) )
@@ -84,8 +84,8 @@ func runTestsWithPreset(t *testing.T, name string, testPreset testPreset, f test
switch testPreset { switch testPreset {
case PresetBadgerStore: case PresetBadgerStore:
serverConfig.DataStore = config.DataStoreBadger serverConfig.DataStore = config.DataStoreBadger
case PresetMapStore: case PresetJsonStore:
serverConfig.DataStore = config.DataStoreMap serverConfig.DataStore = config.DataStoreJson
} }
ts := runTestServerCustomConfig(serverConfig) ts := runTestServerCustomConfig(serverConfig)
+1 -1
View File
@@ -13,7 +13,7 @@ import (
) )
func Test_Databases(t *testing.T) { func Test_Databases(t *testing.T) {
presets := []testPreset{PresetMapStore, PresetBadgerStore} presets := []testPreset{PresetJsonStore, PresetBadgerStore}
runTestsWithPresets(t, "Database Create", presets, func(t *testing.T, ts *TestServer, client *azcosmos.Client) { runTestsWithPresets(t, "Database Create", presets, func(t *testing.T, ts *TestServer, client *azcosmos.Client) {
t.Run("Should create database", func(t *testing.T) { t.Run("Should create database", func(t *testing.T) {
+1 -1
View File
@@ -81,7 +81,7 @@ func documents_InitializeDb(t *testing.T, ts *TestServer) *azcosmos.ContainerCli
} }
func Test_Documents(t *testing.T) { func Test_Documents(t *testing.T) {
presets := []testPreset{PresetMapStore, PresetBadgerStore} presets := []testPreset{PresetJsonStore, PresetBadgerStore}
runTestsWithPresets(t, "Test_Documents", presets, func(t *testing.T, ts *TestServer, client *azcosmos.Client) { runTestsWithPresets(t, "Test_Documents", presets, func(t *testing.T, ts *TestServer, client *azcosmos.Client) {
collectionClient := documents_InitializeDb(t, ts) collectionClient := documents_InitializeDb(t, ts)
+5 -3
View File
@@ -9,7 +9,7 @@ import (
"github.com/pikami/cosmium/api/config" "github.com/pikami/cosmium/api/config"
"github.com/pikami/cosmium/internal/datastore" "github.com/pikami/cosmium/internal/datastore"
badgerdatastore "github.com/pikami/cosmium/internal/datastore/badger_datastore" badgerdatastore "github.com/pikami/cosmium/internal/datastore/badger_datastore"
mapdatastore "github.com/pikami/cosmium/internal/datastore/map_datastore" jsondatastore "github.com/pikami/cosmium/internal/datastore/json_datastore"
"github.com/pikami/cosmium/internal/logger" "github.com/pikami/cosmium/internal/logger"
) )
@@ -19,10 +19,12 @@ func main() {
var dataStore datastore.DataStore var dataStore datastore.DataStore
switch configuration.DataStore { switch configuration.DataStore {
case config.DataStoreBadger: case config.DataStoreBadger:
dataStore = badgerdatastore.NewBadgerDataStore() dataStore = badgerdatastore.NewBadgerDataStore(badgerdatastore.BadgerDataStoreOptions{
PersistDataFilePath: configuration.PersistDataFilePath,
})
logger.InfoLn("Using Badger data store") logger.InfoLn("Using Badger data store")
default: default:
dataStore = mapdatastore.NewMapDataStore(mapdatastore.MapDataStoreOptions{ dataStore = jsondatastore.NewJsonDataStore(jsondatastore.JsonDataStoreOptions{
InitialDataFilePath: configuration.InitialDataFilePath, InitialDataFilePath: configuration.InitialDataFilePath,
PersistDataFilePath: configuration.PersistDataFilePath, PersistDataFilePath: configuration.PersistDataFilePath,
}) })
+2
View File
@@ -10,6 +10,7 @@ require (
github.com/gin-gonic/gin v1.10.0 github.com/gin-gonic/gin v1.10.0
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0
github.com/vmihailenco/msgpack/v5 v5.4.1
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 golang.org/x/exp v0.0.0-20250305212735-054e65f0b394
) )
@@ -44,6 +45,7 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect github.com/ugorji/go/codec v1.2.12 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect go.opentelemetry.io/otel/metric v1.35.0 // indirect
+4
View File
@@ -112,6 +112,10 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
@@ -1,8 +1,6 @@
package badgerdatastore package badgerdatastore
import ( import (
"encoding/gob"
"github.com/dgraph-io/badger/v4" "github.com/dgraph-io/badger/v4"
"github.com/pikami/cosmium/internal/logger" "github.com/pikami/cosmium/internal/logger"
) )
@@ -11,10 +9,16 @@ type BadgerDataStore struct {
db *badger.DB db *badger.DB
} }
func NewBadgerDataStore() *BadgerDataStore { type BadgerDataStoreOptions struct {
gob.Register([]interface{}{}) PersistDataFilePath string
}
badgerOpts := badger.DefaultOptions("").WithInMemory(true) func NewBadgerDataStore(options BadgerDataStoreOptions) *BadgerDataStore {
badgerOpts := badger.DefaultOptions(options.PersistDataFilePath)
badgerOpts = badgerOpts.WithLogger(newBadgerLogger())
if options.PersistDataFilePath == "" {
badgerOpts = badgerOpts.WithInMemory(true)
}
db, err := badger.Open(badgerOpts) db, err := badger.Open(badgerOpts)
if err != nil { if err != nil {
@@ -0,0 +1,28 @@
package badgerdatastore
import (
"github.com/dgraph-io/badger/v4"
"github.com/pikami/cosmium/internal/logger"
)
type badgerLogger struct{}
func newBadgerLogger() badger.Logger {
return &badgerLogger{}
}
func (l *badgerLogger) Errorf(format string, v ...interface{}) {
logger.Errorf(format, v...)
}
func (l *badgerLogger) Warningf(format string, v ...interface{}) {
logger.Infof(format, v...)
}
func (l *badgerLogger) Infof(format string, v ...interface{}) {
logger.Infof(format, v...)
}
func (l *badgerLogger) Debugf(format string, v ...interface{}) {
logger.Debugf(format, v...)
}
@@ -1,13 +1,11 @@
package badgerdatastore package badgerdatastore
import ( import (
"bytes"
"encoding/gob"
"github.com/dgraph-io/badger/v4" "github.com/dgraph-io/badger/v4"
"github.com/pikami/cosmium/internal/datastore" "github.com/pikami/cosmium/internal/datastore"
"github.com/pikami/cosmium/internal/logger" "github.com/pikami/cosmium/internal/logger"
"github.com/pikami/cosmium/internal/resourceid" "github.com/pikami/cosmium/internal/resourceid"
"github.com/vmihailenco/msgpack/v5"
) )
const ( const (
@@ -92,14 +90,13 @@ func insertKey(txn *badger.Txn, key string, value interface{}) datastore.DataSto
return datastore.Unknown return datastore.Unknown
} }
var buf bytes.Buffer buf, err := msgpack.Marshal(value)
err = gob.NewEncoder(&buf).Encode(value)
if err != nil { if err != nil {
logger.ErrorLn("Error while encoding value:", err) logger.ErrorLn("Error while encoding value:", err)
return datastore.Unknown return datastore.Unknown
} }
err = txn.Set([]byte(key), buf.Bytes()) err = txn.Set([]byte(key), buf)
if err != nil { if err != nil {
logger.ErrorLn("Error while setting key:", err) logger.ErrorLn("Error while setting key:", err)
return datastore.Unknown return datastore.Unknown
@@ -135,7 +132,7 @@ func getKey(txn *badger.Txn, key string, value interface{}) datastore.DataStoreS
return datastore.Unknown return datastore.Unknown
} }
err = gob.NewDecoder(bytes.NewReader(val)).Decode(value) err = msgpack.Unmarshal(val, &value)
if err != nil { if err != nil {
logger.ErrorLn("Error while decoding value:", err) logger.ErrorLn("Error while decoding value:", err)
return datastore.Unknown return datastore.Unknown
@@ -158,7 +155,7 @@ func keyExists(txn *badger.Txn, key string) (bool, error) {
} }
func listByPrefix[T any](db *badger.DB, prefix string) ([]T, datastore.DataStoreStatus) { func listByPrefix[T any](db *badger.DB, prefix string) ([]T, datastore.DataStoreStatus) {
var results []T results := make([]T, 0)
err := db.View(func(txn *badger.Txn) error { err := db.View(func(txn *badger.Txn) error {
opts := badger.DefaultIteratorOptions opts := badger.DefaultIteratorOptions
@@ -1,12 +1,10 @@
package badgerdatastore package badgerdatastore
import ( import (
"bytes"
"encoding/gob"
"github.com/dgraph-io/badger/v4" "github.com/dgraph-io/badger/v4"
"github.com/pikami/cosmium/internal/datastore" "github.com/pikami/cosmium/internal/datastore"
"github.com/pikami/cosmium/internal/logger" "github.com/pikami/cosmium/internal/logger"
"github.com/vmihailenco/msgpack/v5"
) )
type BadgerDocumentIterator struct { type BadgerDocumentIterator struct {
@@ -43,7 +41,7 @@ func (i *BadgerDocumentIterator) Next() (datastore.Document, datastore.DataStore
} }
current := &datastore.Document{} current := &datastore.Document{}
err = gob.NewDecoder(bytes.NewReader(val)).Decode(current) err = msgpack.Unmarshal(val, &current)
if err != nil { if err != nil {
logger.ErrorLn("Error while decoding value:", err) logger.ErrorLn("Error while decoding value:", err)
return datastore.Document{}, datastore.Unknown return datastore.Document{}, datastore.Unknown
@@ -1,4 +1,4 @@
package mapdatastore package jsondatastore
import "github.com/pikami/cosmium/internal/datastore" import "github.com/pikami/cosmium/internal/datastore"
@@ -1,4 +1,4 @@
package mapdatastore package jsondatastore
import ( import (
"fmt" "fmt"
@@ -11,7 +11,7 @@ import (
"golang.org/x/exp/maps" "golang.org/x/exp/maps"
) )
func (r *MapDataStore) GetAllCollections(databaseId string) ([]datastore.Collection, datastore.DataStoreStatus) { func (r *JsonDataStore) GetAllCollections(databaseId string) ([]datastore.Collection, datastore.DataStoreStatus) {
r.storeState.RLock() r.storeState.RLock()
defer r.storeState.RUnlock() defer r.storeState.RUnlock()
@@ -22,7 +22,7 @@ func (r *MapDataStore) GetAllCollections(databaseId string) ([]datastore.Collect
return maps.Values(r.storeState.Collections[databaseId]), datastore.StatusOk return maps.Values(r.storeState.Collections[databaseId]), datastore.StatusOk
} }
func (r *MapDataStore) GetCollection(databaseId string, collectionId string) (datastore.Collection, datastore.DataStoreStatus) { func (r *JsonDataStore) GetCollection(databaseId string, collectionId string) (datastore.Collection, datastore.DataStoreStatus) {
r.storeState.RLock() r.storeState.RLock()
defer r.storeState.RUnlock() defer r.storeState.RUnlock()
@@ -37,7 +37,7 @@ func (r *MapDataStore) GetCollection(databaseId string, collectionId string) (da
return r.storeState.Collections[databaseId][collectionId], datastore.StatusOk return r.storeState.Collections[databaseId][collectionId], datastore.StatusOk
} }
func (r *MapDataStore) DeleteCollection(databaseId string, collectionId string) datastore.DataStoreStatus { func (r *JsonDataStore) DeleteCollection(databaseId string, collectionId string) datastore.DataStoreStatus {
r.storeState.Lock() r.storeState.Lock()
defer r.storeState.Unlock() defer r.storeState.Unlock()
@@ -58,7 +58,7 @@ func (r *MapDataStore) DeleteCollection(databaseId string, collectionId string)
return datastore.StatusOk return datastore.StatusOk
} }
func (r *MapDataStore) CreateCollection(databaseId string, newCollection datastore.Collection) (datastore.Collection, datastore.DataStoreStatus) { func (r *JsonDataStore) CreateCollection(databaseId string, newCollection datastore.Collection) (datastore.Collection, datastore.DataStoreStatus) {
r.storeState.Lock() r.storeState.Lock()
defer r.storeState.Unlock() defer r.storeState.Unlock()
@@ -1,4 +1,4 @@
package mapdatastore package jsondatastore
import ( import (
"fmt" "fmt"
@@ -10,14 +10,14 @@ import (
"golang.org/x/exp/maps" "golang.org/x/exp/maps"
) )
func (r *MapDataStore) GetAllDatabases() ([]datastore.Database, datastore.DataStoreStatus) { func (r *JsonDataStore) GetAllDatabases() ([]datastore.Database, datastore.DataStoreStatus) {
r.storeState.RLock() r.storeState.RLock()
defer r.storeState.RUnlock() defer r.storeState.RUnlock()
return maps.Values(r.storeState.Databases), datastore.StatusOk return maps.Values(r.storeState.Databases), datastore.StatusOk
} }
func (r *MapDataStore) GetDatabase(id string) (datastore.Database, datastore.DataStoreStatus) { func (r *JsonDataStore) GetDatabase(id string) (datastore.Database, datastore.DataStoreStatus) {
r.storeState.RLock() r.storeState.RLock()
defer r.storeState.RUnlock() defer r.storeState.RUnlock()
@@ -28,7 +28,7 @@ func (r *MapDataStore) GetDatabase(id string) (datastore.Database, datastore.Dat
return datastore.Database{}, datastore.StatusNotFound return datastore.Database{}, datastore.StatusNotFound
} }
func (r *MapDataStore) DeleteDatabase(id string) datastore.DataStoreStatus { func (r *JsonDataStore) DeleteDatabase(id string) datastore.DataStoreStatus {
r.storeState.Lock() r.storeState.Lock()
defer r.storeState.Unlock() defer r.storeState.Unlock()
@@ -46,7 +46,7 @@ func (r *MapDataStore) DeleteDatabase(id string) datastore.DataStoreStatus {
return datastore.StatusOk return datastore.StatusOk
} }
func (r *MapDataStore) CreateDatabase(newDatabase datastore.Database) (datastore.Database, datastore.DataStoreStatus) { func (r *JsonDataStore) CreateDatabase(newDatabase datastore.Database) (datastore.Database, datastore.DataStoreStatus) {
r.storeState.Lock() r.storeState.Lock()
defer r.storeState.Unlock() defer r.storeState.Unlock()
@@ -1,4 +1,4 @@
package mapdatastore package jsondatastore
import ( import (
"fmt" "fmt"
@@ -10,7 +10,7 @@ import (
"golang.org/x/exp/maps" "golang.org/x/exp/maps"
) )
func (r *MapDataStore) GetAllDocuments(databaseId string, collectionId string) ([]datastore.Document, datastore.DataStoreStatus) { func (r *JsonDataStore) GetAllDocuments(databaseId string, collectionId string) ([]datastore.Document, datastore.DataStoreStatus) {
r.storeState.RLock() r.storeState.RLock()
defer r.storeState.RUnlock() defer r.storeState.RUnlock()
@@ -25,7 +25,7 @@ func (r *MapDataStore) GetAllDocuments(databaseId string, collectionId string) (
return maps.Values(r.storeState.Documents[databaseId][collectionId]), datastore.StatusOk return maps.Values(r.storeState.Documents[databaseId][collectionId]), datastore.StatusOk
} }
func (r *MapDataStore) GetDocument(databaseId string, collectionId string, documentId string) (datastore.Document, datastore.DataStoreStatus) { func (r *JsonDataStore) GetDocument(databaseId string, collectionId string, documentId string) (datastore.Document, datastore.DataStoreStatus) {
r.storeState.RLock() r.storeState.RLock()
defer r.storeState.RUnlock() defer r.storeState.RUnlock()
@@ -44,7 +44,7 @@ func (r *MapDataStore) GetDocument(databaseId string, collectionId string, docum
return r.storeState.Documents[databaseId][collectionId][documentId], datastore.StatusOk return r.storeState.Documents[databaseId][collectionId][documentId], datastore.StatusOk
} }
func (r *MapDataStore) DeleteDocument(databaseId string, collectionId string, documentId string) datastore.DataStoreStatus { func (r *JsonDataStore) DeleteDocument(databaseId string, collectionId string, documentId string) datastore.DataStoreStatus {
r.storeState.Lock() r.storeState.Lock()
defer r.storeState.Unlock() defer r.storeState.Unlock()
@@ -65,7 +65,7 @@ func (r *MapDataStore) DeleteDocument(databaseId string, collectionId string, do
return datastore.StatusOk return datastore.StatusOk
} }
func (r *MapDataStore) CreateDocument(databaseId string, collectionId string, document map[string]interface{}) (datastore.Document, datastore.DataStoreStatus) { func (r *JsonDataStore) CreateDocument(databaseId string, collectionId string, document map[string]interface{}) (datastore.Document, datastore.DataStoreStatus) {
r.storeState.Lock() r.storeState.Lock()
defer r.storeState.Unlock() defer r.storeState.Unlock()
@@ -100,7 +100,7 @@ func (r *MapDataStore) CreateDocument(databaseId string, collectionId string, do
return document, datastore.StatusOk return document, datastore.StatusOk
} }
func (r *MapDataStore) GetDocumentIterator(databaseId string, collectionId string) (datastore.DocumentIterator, datastore.DataStoreStatus) { func (r *JsonDataStore) GetDocumentIterator(databaseId string, collectionId string) (datastore.DocumentIterator, datastore.DataStoreStatus) {
documents, status := r.GetAllDocuments(databaseId, collectionId) documents, status := r.GetAllDocuments(databaseId, collectionId)
if status != datastore.StatusOk { if status != datastore.StatusOk {
return nil, status return nil, status
@@ -1,21 +1,21 @@
package mapdatastore package jsondatastore
import "github.com/pikami/cosmium/internal/datastore" import "github.com/pikami/cosmium/internal/datastore"
type MapDataStore struct { type JsonDataStore struct {
storeState State storeState State
initialDataFilePath string initialDataFilePath string
persistDataFilePath string persistDataFilePath string
} }
type MapDataStoreOptions struct { type JsonDataStoreOptions struct {
InitialDataFilePath string InitialDataFilePath string
PersistDataFilePath string PersistDataFilePath string
} }
func NewMapDataStore(options MapDataStoreOptions) *MapDataStore { func NewJsonDataStore(options JsonDataStoreOptions) *JsonDataStore {
dataStore := &MapDataStore{ dataStore := &JsonDataStore{
storeState: State{ storeState: State{
Databases: make(map[string]datastore.Database), Databases: make(map[string]datastore.Database),
Collections: make(map[string]map[string]datastore.Collection), Collections: make(map[string]map[string]datastore.Collection),
@@ -1,4 +1,4 @@
package mapdatastore package jsondatastore
import ( import (
"fmt" "fmt"
@@ -9,7 +9,7 @@ import (
) )
// I have no idea what this is tbh // I have no idea what this is tbh
func (r *MapDataStore) GetPartitionKeyRanges(databaseId string, collectionId string) ([]datastore.PartitionKeyRange, datastore.DataStoreStatus) { func (r *JsonDataStore) GetPartitionKeyRanges(databaseId string, collectionId string) ([]datastore.PartitionKeyRange, datastore.DataStoreStatus) {
r.storeState.RLock() r.storeState.RLock()
defer r.storeState.RUnlock() defer r.storeState.RUnlock()
@@ -1,4 +1,4 @@
package mapdatastore package jsondatastore
import ( import (
"encoding/json" "encoding/json"
@@ -33,7 +33,7 @@ type State struct {
UserDefinedFunctions map[string]map[string]map[string]datastore.UserDefinedFunction `json:"udfs"` UserDefinedFunctions map[string]map[string]map[string]datastore.UserDefinedFunction `json:"udfs"`
} }
func (r *MapDataStore) InitializeDataStore() { func (r *JsonDataStore) InitializeDataStore() {
if r.initialDataFilePath != "" { if r.initialDataFilePath != "" {
r.LoadStateFS(r.initialDataFilePath) r.LoadStateFS(r.initialDataFilePath)
return return
@@ -55,7 +55,7 @@ func (r *MapDataStore) InitializeDataStore() {
} }
} }
func (r *MapDataStore) LoadStateFS(filePath string) { func (r *JsonDataStore) LoadStateFS(filePath string) {
data, err := os.ReadFile(filePath) data, err := os.ReadFile(filePath)
if err != nil { if err != nil {
log.Fatalf("Error reading state JSON file: %v", err) log.Fatalf("Error reading state JSON file: %v", err)
@@ -68,7 +68,7 @@ func (r *MapDataStore) LoadStateFS(filePath string) {
} }
} }
func (r *MapDataStore) LoadStateJSON(jsonData string) error { func (r *JsonDataStore) LoadStateJSON(jsonData string) error {
r.storeState.Lock() r.storeState.Lock()
defer r.storeState.Unlock() defer r.storeState.Unlock()
@@ -94,7 +94,7 @@ func (r *MapDataStore) LoadStateJSON(jsonData string) error {
return nil return nil
} }
func (r *MapDataStore) SaveStateFS(filePath string) { func (r *JsonDataStore) SaveStateFS(filePath string) {
r.storeState.RLock() r.storeState.RLock()
defer r.storeState.RUnlock() defer r.storeState.RUnlock()
@@ -115,7 +115,7 @@ func (r *MapDataStore) SaveStateFS(filePath string) {
logger.Infof("User defined functions: %d\n", getLength(r.storeState.UserDefinedFunctions)) logger.Infof("User defined functions: %d\n", getLength(r.storeState.UserDefinedFunctions))
} }
func (r *MapDataStore) DumpToJson() (string, error) { func (r *JsonDataStore) DumpToJson() (string, error) {
r.storeState.RLock() r.storeState.RLock()
defer r.storeState.RUnlock() defer r.storeState.RUnlock()
@@ -129,7 +129,7 @@ func (r *MapDataStore) DumpToJson() (string, error) {
} }
func (r *MapDataStore) Close() { func (r *JsonDataStore) Close() {
if r.persistDataFilePath != "" { if r.persistDataFilePath != "" {
r.SaveStateFS(r.persistDataFilePath) r.SaveStateFS(r.persistDataFilePath)
} }
@@ -163,7 +163,7 @@ func getLength(v interface{}) int {
return count return count
} }
func (r *MapDataStore) ensureStoreStateNoNullReferences() { func (r *JsonDataStore) ensureStoreStateNoNullReferences() {
if r.storeState.Databases == nil { if r.storeState.Databases == nil {
r.storeState.Databases = make(map[string]datastore.Database) r.storeState.Databases = make(map[string]datastore.Database)
} }
@@ -1,4 +1,4 @@
package mapdatastore package jsondatastore
import ( import (
"fmt" "fmt"
@@ -10,14 +10,14 @@ import (
"golang.org/x/exp/maps" "golang.org/x/exp/maps"
) )
func (r *MapDataStore) GetAllStoredProcedures(databaseId string, collectionId string) ([]datastore.StoredProcedure, datastore.DataStoreStatus) { func (r *JsonDataStore) GetAllStoredProcedures(databaseId string, collectionId string) ([]datastore.StoredProcedure, datastore.DataStoreStatus) {
r.storeState.RLock() r.storeState.RLock()
defer r.storeState.RUnlock() defer r.storeState.RUnlock()
return maps.Values(r.storeState.StoredProcedures[databaseId][collectionId]), datastore.StatusOk return maps.Values(r.storeState.StoredProcedures[databaseId][collectionId]), datastore.StatusOk
} }
func (r *MapDataStore) GetStoredProcedure(databaseId string, collectionId string, spId string) (datastore.StoredProcedure, datastore.DataStoreStatus) { func (r *JsonDataStore) GetStoredProcedure(databaseId string, collectionId string, spId string) (datastore.StoredProcedure, datastore.DataStoreStatus) {
r.storeState.RLock() r.storeState.RLock()
defer r.storeState.RUnlock() defer r.storeState.RUnlock()
@@ -36,7 +36,7 @@ func (r *MapDataStore) GetStoredProcedure(databaseId string, collectionId string
return datastore.StoredProcedure{}, datastore.StatusNotFound return datastore.StoredProcedure{}, datastore.StatusNotFound
} }
func (r *MapDataStore) DeleteStoredProcedure(databaseId string, collectionId string, spId string) datastore.DataStoreStatus { func (r *JsonDataStore) DeleteStoredProcedure(databaseId string, collectionId string, spId string) datastore.DataStoreStatus {
r.storeState.Lock() r.storeState.Lock()
defer r.storeState.Unlock() defer r.storeState.Unlock()
@@ -57,7 +57,7 @@ func (r *MapDataStore) DeleteStoredProcedure(databaseId string, collectionId str
return datastore.StatusOk return datastore.StatusOk
} }
func (r *MapDataStore) CreateStoredProcedure(databaseId string, collectionId string, sp datastore.StoredProcedure) (datastore.StoredProcedure, datastore.DataStoreStatus) { func (r *JsonDataStore) CreateStoredProcedure(databaseId string, collectionId string, sp datastore.StoredProcedure) (datastore.StoredProcedure, datastore.DataStoreStatus) {
r.storeState.Lock() r.storeState.Lock()
defer r.storeState.Unlock() defer r.storeState.Unlock()
@@ -1,4 +1,4 @@
package mapdatastore package jsondatastore
import ( import (
"fmt" "fmt"
@@ -10,14 +10,14 @@ import (
"golang.org/x/exp/maps" "golang.org/x/exp/maps"
) )
func (r *MapDataStore) GetAllTriggers(databaseId string, collectionId string) ([]datastore.Trigger, datastore.DataStoreStatus) { func (r *JsonDataStore) GetAllTriggers(databaseId string, collectionId string) ([]datastore.Trigger, datastore.DataStoreStatus) {
r.storeState.RLock() r.storeState.RLock()
defer r.storeState.RUnlock() defer r.storeState.RUnlock()
return maps.Values(r.storeState.Triggers[databaseId][collectionId]), datastore.StatusOk return maps.Values(r.storeState.Triggers[databaseId][collectionId]), datastore.StatusOk
} }
func (r *MapDataStore) GetTrigger(databaseId string, collectionId string, triggerId string) (datastore.Trigger, datastore.DataStoreStatus) { func (r *JsonDataStore) GetTrigger(databaseId string, collectionId string, triggerId string) (datastore.Trigger, datastore.DataStoreStatus) {
r.storeState.RLock() r.storeState.RLock()
defer r.storeState.RUnlock() defer r.storeState.RUnlock()
@@ -36,7 +36,7 @@ func (r *MapDataStore) GetTrigger(databaseId string, collectionId string, trigge
return datastore.Trigger{}, datastore.StatusNotFound return datastore.Trigger{}, datastore.StatusNotFound
} }
func (r *MapDataStore) DeleteTrigger(databaseId string, collectionId string, triggerId string) datastore.DataStoreStatus { func (r *JsonDataStore) DeleteTrigger(databaseId string, collectionId string, triggerId string) datastore.DataStoreStatus {
r.storeState.Lock() r.storeState.Lock()
defer r.storeState.Unlock() defer r.storeState.Unlock()
@@ -57,7 +57,7 @@ func (r *MapDataStore) DeleteTrigger(databaseId string, collectionId string, tri
return datastore.StatusOk return datastore.StatusOk
} }
func (r *MapDataStore) CreateTrigger(databaseId string, collectionId string, trigger datastore.Trigger) (datastore.Trigger, datastore.DataStoreStatus) { func (r *JsonDataStore) CreateTrigger(databaseId string, collectionId string, trigger datastore.Trigger) (datastore.Trigger, datastore.DataStoreStatus) {
r.storeState.Lock() r.storeState.Lock()
defer r.storeState.Unlock() defer r.storeState.Unlock()
@@ -1,4 +1,4 @@
package mapdatastore package jsondatastore
import ( import (
"fmt" "fmt"
@@ -10,14 +10,14 @@ import (
"golang.org/x/exp/maps" "golang.org/x/exp/maps"
) )
func (r *MapDataStore) GetAllUserDefinedFunctions(databaseId string, collectionId string) ([]datastore.UserDefinedFunction, datastore.DataStoreStatus) { func (r *JsonDataStore) GetAllUserDefinedFunctions(databaseId string, collectionId string) ([]datastore.UserDefinedFunction, datastore.DataStoreStatus) {
r.storeState.RLock() r.storeState.RLock()
defer r.storeState.RUnlock() defer r.storeState.RUnlock()
return maps.Values(r.storeState.UserDefinedFunctions[databaseId][collectionId]), datastore.StatusOk return maps.Values(r.storeState.UserDefinedFunctions[databaseId][collectionId]), datastore.StatusOk
} }
func (r *MapDataStore) GetUserDefinedFunction(databaseId string, collectionId string, udfId string) (datastore.UserDefinedFunction, datastore.DataStoreStatus) { func (r *JsonDataStore) GetUserDefinedFunction(databaseId string, collectionId string, udfId string) (datastore.UserDefinedFunction, datastore.DataStoreStatus) {
r.storeState.RLock() r.storeState.RLock()
defer r.storeState.RUnlock() defer r.storeState.RUnlock()
@@ -36,7 +36,7 @@ func (r *MapDataStore) GetUserDefinedFunction(databaseId string, collectionId st
return datastore.UserDefinedFunction{}, datastore.StatusNotFound return datastore.UserDefinedFunction{}, datastore.StatusNotFound
} }
func (r *MapDataStore) DeleteUserDefinedFunction(databaseId string, collectionId string, udfId string) datastore.DataStoreStatus { func (r *JsonDataStore) DeleteUserDefinedFunction(databaseId string, collectionId string, udfId string) datastore.DataStoreStatus {
r.storeState.Lock() r.storeState.Lock()
defer r.storeState.Unlock() defer r.storeState.Unlock()
@@ -57,7 +57,7 @@ func (r *MapDataStore) DeleteUserDefinedFunction(databaseId string, collectionId
return datastore.StatusOk return datastore.StatusOk
} }
func (r *MapDataStore) CreateUserDefinedFunction(databaseId string, collectionId string, udf datastore.UserDefinedFunction) (datastore.UserDefinedFunction, datastore.DataStoreStatus) { func (r *JsonDataStore) CreateUserDefinedFunction(databaseId string, collectionId string, udf datastore.UserDefinedFunction) (datastore.UserDefinedFunction, datastore.DataStoreStatus) {
r.storeState.Lock() r.storeState.Lock()
defer r.storeState.Unlock() defer r.storeState.Unlock()
+7 -5
View File
@@ -13,7 +13,7 @@ import (
"github.com/pikami/cosmium/api/config" "github.com/pikami/cosmium/api/config"
"github.com/pikami/cosmium/internal/datastore" "github.com/pikami/cosmium/internal/datastore"
badgerdatastore "github.com/pikami/cosmium/internal/datastore/badger_datastore" badgerdatastore "github.com/pikami/cosmium/internal/datastore/badger_datastore"
mapdatastore "github.com/pikami/cosmium/internal/datastore/map_datastore" jsondatastore "github.com/pikami/cosmium/internal/datastore/json_datastore"
) )
//export CreateServerInstance //export CreateServerInstance
@@ -37,9 +37,11 @@ func CreateServerInstance(serverName *C.char, configurationJSON *C.char) int {
var dataStore datastore.DataStore var dataStore datastore.DataStore
switch configuration.DataStore { switch configuration.DataStore {
case config.DataStoreBadger: case config.DataStoreBadger:
dataStore = badgerdatastore.NewBadgerDataStore() dataStore = badgerdatastore.NewBadgerDataStore(badgerdatastore.BadgerDataStoreOptions{
PersistDataFilePath: configuration.PersistDataFilePath,
})
default: default:
dataStore = mapdatastore.NewMapDataStore(mapdatastore.MapDataStoreOptions{ dataStore = jsondatastore.NewJsonDataStore(jsondatastore.JsonDataStoreOptions{
InitialDataFilePath: configuration.InitialDataFilePath, InitialDataFilePath: configuration.InitialDataFilePath,
PersistDataFilePath: configuration.PersistDataFilePath, PersistDataFilePath: configuration.PersistDataFilePath,
}) })
@@ -94,8 +96,8 @@ func LoadServerInstanceState(serverName *C.char, stateJSON *C.char) int {
stateJSONStr := C.GoString(stateJSON) stateJSONStr := C.GoString(stateJSON)
if serverInstance, ok := getInstance(serverNameStr); ok { if serverInstance, ok := getInstance(serverNameStr); ok {
if mapDS, ok := serverInstance.dataStore.(*mapdatastore.MapDataStore); ok { if jsonDS, ok := serverInstance.dataStore.(*jsondatastore.JsonDataStore); ok {
err := mapDS.LoadStateJSON(stateJSONStr) err := jsonDS.LoadStateJSON(stateJSONStr)
if err != nil { if err != nil {
return ResponseFailedToLoadState return ResponseFailedToLoadState
} }