From 7e4dc2428d14b0ca6418801e4a3e6114cd5f3a5c Mon Sep 17 00:00:00 2001 From: Chris Schinnerl Date: Thu, 27 Apr 2023 11:34:23 +0200 Subject: [PATCH] stores: cache settings in memory --- internal/stores/settingsdb.go | 30 ++++++++++++++++++++++++++++-- internal/stores/settingsdb_test.go | 14 ++++++++++++++ internal/stores/sql.go | 5 +++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/internal/stores/settingsdb.go b/internal/stores/settingsdb.go index a7995f439..3b1eafc31 100644 --- a/internal/stores/settingsdb.go +++ b/internal/stores/settingsdb.go @@ -24,11 +24,26 @@ func (dbSetting) TableName() string { return "settings" } // DeleteSetting implements the bus.SettingStore interface. func (s *SQLStore) DeleteSetting(ctx context.Context, key string) error { + // Delete from cache. + s.settingsMu.Lock() + delete(s.settings, key) + s.settingsMu.Unlock() + + // Delete from database. return s.db.Where(&dbSetting{Key: key}).Delete(&dbSetting{}).Error } // Setting implements the bus.SettingStore interface. func (s *SQLStore) Setting(ctx context.Context, key string) (string, error) { + // Check cache first. + s.settingsMu.Lock() + defer s.settingsMu.Unlock() + value, ok := s.settings[key] + if ok { + return value, nil + } + + // Check database. var entry dbSetting err := s.db.Where(&dbSetting{Key: key}). Take(&entry).Error @@ -37,7 +52,7 @@ func (s *SQLStore) Setting(ctx context.Context, key string) (string, error) { } else if err != nil { return "", err } - + s.settings[key] = entry.Value return entry.Value, nil } @@ -50,11 +65,22 @@ func (s *SQLStore) Settings(ctx context.Context) ([]string, error) { // UpdateSetting implements the bus.SettingStore interface. func (s *SQLStore) UpdateSetting(ctx context.Context, key, value string) error { - return s.db.Clauses(clause.OnConflict{ + // Update db first. + s.settingsMu.Lock() + defer s.settingsMu.Unlock() + + err := s.db.Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "key"}}, DoUpdates: clause.AssignmentColumns([]string{"value"}), }).Create(&dbSetting{ Key: key, Value: value, }).Error + if err != nil { + return err + } + + // Update cache second. + s.settings[key] = value + return nil } diff --git a/internal/stores/settingsdb_test.go b/internal/stores/settingsdb_test.go index 228e433c2..e420e7b1c 100644 --- a/internal/stores/settingsdb_test.go +++ b/internal/stores/settingsdb_test.go @@ -2,7 +2,10 @@ package stores import ( "context" + "errors" "testing" + + "go.sia.tech/renterd/api" ) // TestSQLSettingStore tests the bus.SettingStore methods on the SQLSettingStore. @@ -49,4 +52,15 @@ func TestSQLSettingStore(t *testing.T) { } else if value != "barbaz" { t.Fatalf("unexpected value, %s != 'barbaz'", value) } + + // delete the setting + if err := ss.DeleteSetting(ctx, "foo"); err != nil { + t.Fatal(err) + } else if _, err := ss.Setting(ctx, "foo"); !errors.Is(err, api.ErrSettingNotFound) { + t.Fatal("should fail with gorm.ErrRecordNotFound", err) + } else if keys, err := ss.Settings(ctx); err != nil { + t.Fatal(err) + } else if len(keys) != 0 { + t.Fatalf("unexpected number of settings, %v != 0", len(keys)) + } } diff --git a/internal/stores/sql.go b/internal/stores/sql.go index 1f4f352c8..515cc04ea 100644 --- a/internal/stores/sql.go +++ b/internal/stores/sql.go @@ -40,6 +40,10 @@ type ( unappliedRevisions map[types.FileContractID]revisionUpdate unappliedProofs map[types.FileContractID]uint64 + // SettingsDB related fields. + settingsMu sync.Mutex + settings map[string]string + mu sync.Mutex hasAllowlist bool hasBlocklist bool @@ -207,6 +211,7 @@ func NewSQLStore(conn gorm.Dialector, migrate bool, persistInterval time.Duratio persistInterval: persistInterval, hasAllowlist: allowlistCnt > 0, hasBlocklist: blocklistCnt > 0, + settings: make(map[string]string), unappliedHostKeys: make(map[types.PublicKey]struct{}), unappliedRevisions: make(map[types.FileContractID]revisionUpdate), unappliedProofs: make(map[types.FileContractID]uint64),