diff --git a/go.sum b/go.sum index 63273b4115..3501200dda 100644 --- a/go.sum +++ b/go.sum @@ -292,8 +292,6 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/heroiclabs/nakama-common v1.28.1 h1:GOH8r27NBSdiBf7xjy2R/Ov7CSr9zp4DVJt9Y2LIbqg= -github.com/heroiclabs/nakama-common v1.28.1/go.mod h1:Os8XeXGvHAap/p6M/8fQ3gle4eEXDGRQmoRNcPQTjXs= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= diff --git a/main.go b/main.go index 9dee261b04..ab5d504084 100644 --- a/main.go +++ b/main.go @@ -147,7 +147,7 @@ func main() { tracker.SetMatchLeaveListener(matchRegistry.Leave) streamManager := server.NewLocalStreamManager(config, sessionRegistry, tracker) - storageIndex, err := server.NewLocalStorageIndex(logger, db) + storageIndex, err := server.NewLocalStorageIndex(logger, db, config.GetStorageIndex()) if err != nil { logger.Fatal("Failed to initialize storage index", zap.Error(err)) } diff --git a/server/api_test.go b/server/api_test.go index ed2fe14d24..578156994a 100644 --- a/server/api_test.go +++ b/server/api_test.go @@ -53,7 +53,7 @@ var ( DiscardUnknown: false, } metrics = NewLocalMetrics(logger, logger, nil, cfg) - storageIdx, _ = NewLocalStorageIndex(logger, nil) + storageIdx, _ = NewLocalStorageIndex(logger, nil, &StorageIndexConfig{DisableIndexOnly: false}) _ = CheckConfig(logger, cfg) ) diff --git a/server/config.go b/server/config.go index dc15f8ad96..babe355afe 100644 --- a/server/config.go +++ b/server/config.go @@ -51,6 +51,7 @@ type Config interface { GetIAP() *IAPConfig GetGoogleAuth() *GoogleAuthConfig GetSatori() *SatoriConfig + GetStorageIndex() *StorageIndexConfig Clone() (Config, error) } @@ -437,25 +438,26 @@ func convertRuntimeEnv(logger *zap.Logger, existingEnv map[string]string, mergeE } type config struct { - Name string `yaml:"name" json:"name" usage:"Nakama server’s node name - must be unique."` - Config []string `yaml:"config" json:"config" usage:"The absolute file path to configuration YAML file."` - ShutdownGraceSec int `yaml:"shutdown_grace_sec" json:"shutdown_grace_sec" usage:"Maximum number of seconds to wait for the server to complete work before shutting down. Default is 0 seconds. If 0 the server will shut down immediately when it receives a termination signal."` - Datadir string `yaml:"data_dir" json:"data_dir" usage:"An absolute path to a writeable folder where Nakama will store its data."` - Logger *LoggerConfig `yaml:"logger" json:"logger" usage:"Logger levels and output."` - Metrics *MetricsConfig `yaml:"metrics" json:"metrics" usage:"Metrics settings."` - Session *SessionConfig `yaml:"session" json:"session" usage:"Session authentication settings."` - Socket *SocketConfig `yaml:"socket" json:"socket" usage:"Socket configuration."` - Database *DatabaseConfig `yaml:"database" json:"database" usage:"Database connection settings."` - Social *SocialConfig `yaml:"social" json:"social" usage:"Properties for social provider integrations."` - Runtime *RuntimeConfig `yaml:"runtime" json:"runtime" usage:"Script Runtime properties."` - Match *MatchConfig `yaml:"match" json:"match" usage:"Authoritative realtime match properties."` - Tracker *TrackerConfig `yaml:"tracker" json:"tracker" usage:"Presence tracker properties."` - Console *ConsoleConfig `yaml:"console" json:"console" usage:"Console settings."` - Leaderboard *LeaderboardConfig `yaml:"leaderboard" json:"leaderboard" usage:"Leaderboard settings."` - Matchmaker *MatchmakerConfig `yaml:"matchmaker" json:"matchmaker" usage:"Matchmaker settings."` - IAP *IAPConfig `yaml:"iap" json:"iap" usage:"In-App Purchase settings."` - GoogleAuth *GoogleAuthConfig `yaml:"google_auth" json:"google_auth" usage:"Google's auth settings."` - Satori *SatoriConfig `yaml:"satori" json:"satori" usage:"Satori integration settings."` + Name string `yaml:"name" json:"name" usage:"Nakama server’s node name - must be unique."` + Config []string `yaml:"config" json:"config" usage:"The absolute file path to configuration YAML file."` + ShutdownGraceSec int `yaml:"shutdown_grace_sec" json:"shutdown_grace_sec" usage:"Maximum number of seconds to wait for the server to complete work before shutting down. Default is 0 seconds. If 0 the server will shut down immediately when it receives a termination signal."` + Datadir string `yaml:"data_dir" json:"data_dir" usage:"An absolute path to a writeable folder where Nakama will store its data."` + Logger *LoggerConfig `yaml:"logger" json:"logger" usage:"Logger levels and output."` + Metrics *MetricsConfig `yaml:"metrics" json:"metrics" usage:"Metrics settings."` + Session *SessionConfig `yaml:"session" json:"session" usage:"Session authentication settings."` + Socket *SocketConfig `yaml:"socket" json:"socket" usage:"Socket configuration."` + Database *DatabaseConfig `yaml:"database" json:"database" usage:"Database connection settings."` + Social *SocialConfig `yaml:"social" json:"social" usage:"Properties for social provider integrations."` + Runtime *RuntimeConfig `yaml:"runtime" json:"runtime" usage:"Script Runtime properties."` + Match *MatchConfig `yaml:"match" json:"match" usage:"Authoritative realtime match properties."` + Tracker *TrackerConfig `yaml:"tracker" json:"tracker" usage:"Presence tracker properties."` + Console *ConsoleConfig `yaml:"console" json:"console" usage:"Console settings."` + Leaderboard *LeaderboardConfig `yaml:"leaderboard" json:"leaderboard" usage:"Leaderboard settings."` + Matchmaker *MatchmakerConfig `yaml:"matchmaker" json:"matchmaker" usage:"Matchmaker settings."` + IAP *IAPConfig `yaml:"iap" json:"iap" usage:"In-App Purchase settings."` + GoogleAuth *GoogleAuthConfig `yaml:"google_auth" json:"google_auth" usage:"Google's auth settings."` + Satori *SatoriConfig `yaml:"satori" json:"satori" usage:"Satori integration settings."` + StorageIndex *StorageIndexConfig `yaml:"storage_index" json:"storage_index" usage:"Storage index settings."` } // NewConfig constructs a Config struct which represents server settings, and populates it with default values. @@ -483,6 +485,7 @@ func NewConfig(logger *zap.Logger) *config { IAP: NewIAPConfig(), GoogleAuth: NewGoogleAuthConfig(), Satori: NewSatoriConfig(), + StorageIndex: NewStorageIndexConfig(), } } @@ -501,6 +504,7 @@ func (c *config) Clone() (Config, error) { configMatchmaker := *(c.Matchmaker) configIAP := *(c.IAP) configSatori := *(c.Satori) + configStorageIndex := *(c.StorageIndex) configGoogleAuth := *(c.GoogleAuth) nc := &config{ Name: c.Name, @@ -521,6 +525,7 @@ func (c *config) Clone() (Config, error) { IAP: &configIAP, Satori: &configSatori, GoogleAuth: &configGoogleAuth, + StorageIndex: &configStorageIndex, } nc.Socket.CertPEMBlock = make([]byte, len(c.Socket.CertPEMBlock)) copy(nc.Socket.CertPEMBlock, c.Socket.CertPEMBlock) @@ -619,6 +624,10 @@ func (c *config) GetSatori() *SatoriConfig { return c.Satori } +func (c *config) GetStorageIndex() *StorageIndexConfig { + return c.StorageIndex +} + // LoggerConfig is configuration relevant to logging levels and output. type LoggerConfig struct { Level string `yaml:"level" json:"level" usage:"Log level to set. Valid values are 'debug', 'info', 'warn', 'error'. Default 'info'."` @@ -803,7 +812,7 @@ func NewSocialConfig() *SocialConfig { } } -// RuntimeConfig is configuration relevant to the Runtime Lua VM. +// RuntimeConfig is configuration relevant to the Runtimes. type RuntimeConfig struct { Environment map[string]string `yaml:"-" json:"-"` Env []string `yaml:"env" json:"env" usage:"Values to pass into Runtime as environment variables."` @@ -1073,3 +1082,11 @@ func NewGoogleAuthConfig() *GoogleAuthConfig { OAuthConfig: nil, } } + +type StorageIndexConfig struct { + DisableIndexOnly bool `yaml:"disable_index_only" json:"disable_index_only" usage:"Override and disable 'index_only' storage indices config and fallback to reading from the database."` +} + +func NewStorageIndexConfig() *StorageIndexConfig { + return &StorageIndexConfig{} +} diff --git a/server/storage_index.go b/server/storage_index.go index bddd28901e..cc7514cccb 100644 --- a/server/storage_index.go +++ b/server/storage_index.go @@ -57,15 +57,17 @@ type LocalStorageIndex struct { indexByName map[string]*storageIndex indicesByCollection map[string][]*storageIndex customFilterFunctions map[string]RuntimeStorageIndexFilterFunction + config *StorageIndexConfig } -func NewLocalStorageIndex(logger *zap.Logger, db *sql.DB) (StorageIndex, error) { +func NewLocalStorageIndex(logger *zap.Logger, db *sql.DB, config *StorageIndexConfig) (StorageIndex, error) { si := &LocalStorageIndex{ logger: logger, db: db, indexByName: make(map[string]*storageIndex), indicesByCollection: make(map[string][]*storageIndex), customFilterFunctions: make(map[string]RuntimeStorageIndexFilterFunction), + config: config, } return si, nil @@ -250,7 +252,7 @@ func (si *LocalStorageIndex) List(ctx context.Context, indexName, query string, return &api.StorageObjects{Objects: []*api.StorageObject{}}, nil } - if idx.IndexOnly { + if !si.config.DisableIndexOnly && idx.IndexOnly { objects := make([]*api.StorageObject, 0, len(indexResults)) for _, idxResult := range indexResults { objects = append(objects, &api.StorageObject{ @@ -292,8 +294,7 @@ func (si *LocalStorageIndex) List(ctx context.Context, indexName, query string, sortedObjects := make([]*api.StorageObject, 0, len(objects.Objects)) for _, r := range indexResults { - index, ok := objectIdToIdx[fmt.Sprintf("%s.%s.%s", r.Collection, r.Key, r.UserID)] - if ok { + if index, ok := objectIdToIdx[fmt.Sprintf("%s.%s.%s", r.Collection, r.Key, r.UserID)]; ok { sortedObjects = append(sortedObjects, objects.Objects[index]) } } @@ -469,7 +470,7 @@ func (si *LocalStorageIndex) mapIndexStorageFields(userID, collection, key, vers rv.AddField(bluge.NewNumericField("read", float64(read)).StoreValue()) rv.AddField(bluge.NewNumericField("write", float64(write)).StoreValue()) - if indexOnly { + if !si.config.DisableIndexOnly && indexOnly { json, err := json.Marshal(mapValue) if err != nil { return nil, err diff --git a/server/storage_index_test.go b/server/storage_index_test.go index 04c167319c..46a27b0fea 100644 --- a/server/storage_index_test.go +++ b/server/storage_index_test.go @@ -54,7 +54,7 @@ func TestLocalStorageIndex_Write(t *testing.T) { }) valueThree := string(valueThreeBytes) - storageIdx, err := NewLocalStorageIndex(logger, db) + storageIdx, err := NewLocalStorageIndex(logger, db, &StorageIndexConfig{}) if err != nil { t.Fatal(err.Error()) } @@ -206,7 +206,7 @@ func TestLocalStorageIndex_Write(t *testing.T) { UpdateTime: timestamppb.New(ts), } - storageIdx, err := NewLocalStorageIndex(logger, db) + storageIdx, err := NewLocalStorageIndex(logger, db, &StorageIndexConfig{}) if err != nil { t.Fatal(err.Error()) } @@ -330,7 +330,7 @@ func TestLocalStorageIndex_List(t *testing.T) { }) valueThree := string(valueThreeBytes) - storageIdx, err := NewLocalStorageIndex(logger, db) + storageIdx, err := NewLocalStorageIndex(logger, db, &StorageIndexConfig{}) if err != nil { t.Fatal(err.Error()) } @@ -423,7 +423,7 @@ func TestLocalStorageIndex_List(t *testing.T) { }) valueThree := string(valueThreeBytes) - storageIdx, err := NewLocalStorageIndex(logger, db) + storageIdx, err := NewLocalStorageIndex(logger, db, &StorageIndexConfig{}) if err != nil { t.Fatal(err.Error()) } @@ -507,7 +507,7 @@ func TestLocalStorageIndex_Delete(t *testing.T) { }) valueOne := string(valueOneBytes) - storageIdx, err := NewLocalStorageIndex(logger, db) + storageIdx, err := NewLocalStorageIndex(logger, db, &StorageIndexConfig{}) if err != nil { t.Fatal(err.Error()) }