Reduce hot run polling pressure
This commit is contained in:
+112
-14
@@ -38,10 +38,17 @@ type StoreSnapshot struct {
|
||||
PluginDataRecords []domain.PluginDataRecord `json:"pluginDataRecords"`
|
||||
}
|
||||
|
||||
type runtimeSnapshot struct {
|
||||
RunControlSessions []domain.RunControlSession `json:"runControlSessions"`
|
||||
RunEndpoints []domain.RunEndpoint `json:"runEndpoints"`
|
||||
}
|
||||
|
||||
type FileStore struct {
|
||||
*MemoryStore
|
||||
path string
|
||||
persistMu sync.Mutex
|
||||
path string
|
||||
runtimePath string
|
||||
persistMu sync.Mutex
|
||||
runtimePersistMu sync.Mutex
|
||||
}
|
||||
|
||||
func NewFileStore(path string) (*FileStore, error) {
|
||||
@@ -52,6 +59,7 @@ func NewFileStore(path string) (*FileStore, error) {
|
||||
store := &FileStore{
|
||||
MemoryStore: NewMemoryStore(),
|
||||
path: path,
|
||||
runtimePath: runtimeMetadataPath(path),
|
||||
}
|
||||
if err := store.load(); err != nil {
|
||||
return nil, err
|
||||
@@ -72,7 +80,7 @@ func (store *FileStore) AuthSessions() AuthSessionRepository {
|
||||
}
|
||||
|
||||
func (store *FileStore) RunControlSessions() RunControlSessionRepository {
|
||||
return &persistentRepository[domain.RunControlSession, struct{}]{repository: store.MemoryStore.runSessions, persist: store.persist}
|
||||
return &persistentRepository[domain.RunControlSession, struct{}]{repository: store.MemoryStore.runSessions, persist: store.persistRuntime}
|
||||
}
|
||||
|
||||
func (store *FileStore) AIProviders() AIProviderRepository {
|
||||
@@ -88,7 +96,7 @@ func (store *FileStore) ServerInstances() ServerInstanceRepository {
|
||||
}
|
||||
|
||||
func (store *FileStore) RunEndpoints() RunEndpointRepository {
|
||||
return &persistentRepository[domain.RunEndpoint, domain.RunEndpointFilter]{repository: store.MemoryStore.runEndpoints, persist: store.persist}
|
||||
return &persistentRepository[domain.RunEndpoint, domain.RunEndpointFilter]{repository: store.MemoryStore.runEndpoints, persist: store.persistRuntime}
|
||||
}
|
||||
|
||||
func (store *FileStore) Jobs() JobRepository {
|
||||
@@ -163,19 +171,18 @@ func (store *FileStore) load() error {
|
||||
data, err := os.ReadFile(store.path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
return store.loadRuntime()
|
||||
}
|
||||
return fmt.Errorf("read metadata snapshot: %w", err)
|
||||
}
|
||||
if len(strings.TrimSpace(string(data))) == 0 {
|
||||
return nil
|
||||
if len(strings.TrimSpace(string(data))) != 0 {
|
||||
var snapshot StoreSnapshot
|
||||
if err := json.Unmarshal(data, &snapshot); err != nil {
|
||||
return fmt.Errorf("decode metadata snapshot: %w", err)
|
||||
}
|
||||
store.loadSnapshot(snapshot)
|
||||
}
|
||||
var snapshot StoreSnapshot
|
||||
if err := json.Unmarshal(data, &snapshot); err != nil {
|
||||
return fmt.Errorf("decode metadata snapshot: %w", err)
|
||||
}
|
||||
store.loadSnapshot(snapshot)
|
||||
return nil
|
||||
return store.loadRuntime()
|
||||
}
|
||||
|
||||
func (store *FileStore) persist() error {
|
||||
@@ -183,7 +190,7 @@ func (store *FileStore) persist() error {
|
||||
defer store.persistMu.Unlock()
|
||||
|
||||
snapshot := store.snapshot()
|
||||
data, err := json.MarshalIndent(snapshot, "", " ")
|
||||
data, err := json.Marshal(snapshot)
|
||||
if err != nil {
|
||||
return fmt.Errorf("encode metadata snapshot: %w", err)
|
||||
}
|
||||
@@ -200,6 +207,85 @@ func (store *FileStore) persist() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *FileStore) loadRuntime() error {
|
||||
data, err := os.ReadFile(store.runtimePath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("read runtime metadata snapshot: %w", err)
|
||||
}
|
||||
if len(strings.TrimSpace(string(data))) == 0 {
|
||||
return nil
|
||||
}
|
||||
var snapshot runtimeSnapshot
|
||||
if err := json.Unmarshal(data, &snapshot); err != nil {
|
||||
return fmt.Errorf("decode runtime metadata snapshot: %w", err)
|
||||
}
|
||||
store.loadRuntimeSnapshot(snapshot)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *FileStore) persistRuntime() error {
|
||||
store.runtimePersistMu.Lock()
|
||||
defer store.runtimePersistMu.Unlock()
|
||||
|
||||
snapshot := store.runtimeSnapshot()
|
||||
data, err := json.Marshal(snapshot)
|
||||
if err != nil {
|
||||
return fmt.Errorf("encode runtime metadata snapshot: %w", err)
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Dir(store.runtimePath), 0o755); err != nil {
|
||||
return fmt.Errorf("create runtime metadata directory: %w", err)
|
||||
}
|
||||
if err := store.ensureMetadataFile(); err != nil {
|
||||
return err
|
||||
}
|
||||
tmpPath := store.runtimePath + ".tmp"
|
||||
if err := os.WriteFile(tmpPath, data, 0o600); err != nil {
|
||||
return fmt.Errorf("write runtime metadata snapshot: %w", err)
|
||||
}
|
||||
if err := os.Rename(tmpPath, store.runtimePath); err != nil {
|
||||
return fmt.Errorf("replace runtime metadata snapshot: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *FileStore) ensureMetadataFile() error {
|
||||
if _, err := os.Stat(store.path); err == nil {
|
||||
return nil
|
||||
} else if !os.IsNotExist(err) {
|
||||
return fmt.Errorf("stat metadata snapshot: %w", err)
|
||||
}
|
||||
|
||||
store.persistMu.Lock()
|
||||
defer store.persistMu.Unlock()
|
||||
if _, err := os.Stat(store.path); err == nil {
|
||||
return nil
|
||||
} else if !os.IsNotExist(err) {
|
||||
return fmt.Errorf("stat metadata snapshot: %w", err)
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Dir(store.path), 0o755); err != nil {
|
||||
return fmt.Errorf("create metadata directory: %w", err)
|
||||
}
|
||||
tmpPath := store.path + ".empty.tmp"
|
||||
if err := os.WriteFile(tmpPath, []byte("{}"), 0o600); err != nil {
|
||||
return fmt.Errorf("write empty metadata snapshot: %w", err)
|
||||
}
|
||||
if err := os.Rename(tmpPath, store.path); err != nil {
|
||||
return fmt.Errorf("replace empty metadata snapshot: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runtimeMetadataPath(path string) string {
|
||||
ext := filepath.Ext(path)
|
||||
if ext == "" {
|
||||
return path + ".runtime"
|
||||
}
|
||||
return strings.TrimSuffix(path, ext) + ".runtime" + ext
|
||||
}
|
||||
|
||||
func (store *FileStore) snapshot() StoreSnapshot {
|
||||
return StoreSnapshot{
|
||||
Users: snapshotRepository(store.MemoryStore.users),
|
||||
@@ -228,6 +314,13 @@ func (store *FileStore) snapshot() StoreSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
func (store *FileStore) runtimeSnapshot() runtimeSnapshot {
|
||||
return runtimeSnapshot{
|
||||
RunControlSessions: snapshotRepository(store.MemoryStore.runSessions),
|
||||
RunEndpoints: snapshotRepository(store.MemoryStore.runEndpoints),
|
||||
}
|
||||
}
|
||||
|
||||
func (store *FileStore) loadSnapshot(snapshot StoreSnapshot) {
|
||||
loadRepository(store.MemoryStore.users, snapshot.Users)
|
||||
loadRepository(store.MemoryStore.authSessions, snapshot.AuthSessions)
|
||||
@@ -254,6 +347,11 @@ func (store *FileStore) loadSnapshot(snapshot StoreSnapshot) {
|
||||
loadRepository(store.MemoryStore.pluginDataRecords, snapshot.PluginDataRecords)
|
||||
}
|
||||
|
||||
func (store *FileStore) loadRuntimeSnapshot(snapshot runtimeSnapshot) {
|
||||
loadRepository(store.MemoryStore.runSessions, snapshot.RunControlSessions)
|
||||
loadRepository(store.MemoryStore.runEndpoints, snapshot.RunEndpoints)
|
||||
}
|
||||
|
||||
type mutableRepository[T any, F any] interface {
|
||||
Create(T) error
|
||||
Get(string) (T, error)
|
||||
|
||||
Reference in New Issue
Block a user