Reduce hot run polling pressure

This commit is contained in:
npc0-hue
2026-09-07 20:26:20 +08:00
parent 4ab7bb820d
commit fd9dbab669
16 changed files with 335 additions and 56 deletions
+112 -14
View File
@@ -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)