123 lines
3.9 KiB
Go
123 lines
3.9 KiB
Go
// Package workspace verwaltet die offenen Workspaces eines laufenden
|
||
// Prozesses.
|
||
//
|
||
// Jede Abteilung hat ihren eigenen, abgeschotteten Bereich (siehe
|
||
// AUSBAUSTUFEN.md 4.4): eine SQLite-Datei oder ein PostgreSQL-Schema — welches
|
||
// von beiden, entscheidet der store.Speicher (AUSBAUSTUFEN.md 4.10). Ein
|
||
// Manager öffnet die Bereiche bei Bedarf und hält sie offen, statt bei jedem
|
||
// Request neu zu öffnen — das Öffnen (inklusive Migrationsprüfung) kostet
|
||
// spürbar mehr als ein Map-Zugriff.
|
||
package workspace
|
||
|
||
import (
|
||
"fmt"
|
||
"sort"
|
||
"sync"
|
||
|
||
"munin/internal/search"
|
||
"munin/internal/store"
|
||
)
|
||
|
||
// Workspace bündelt, was zu einer offenen Workspace-Datei gehört: die
|
||
// Datenbank und ihren Suchindex. Beide sind an dieselbe Datei gebunden und
|
||
// werden deshalb gemeinsam verwaltet — ein Registry, der aus Versehen an der
|
||
// falschen Datenbank hängt, wäre ein stiller Fehler.
|
||
type Workspace struct {
|
||
ID string
|
||
Store *store.Store
|
||
Search *search.Registry
|
||
}
|
||
|
||
// Manager öffnet Workspace-Dateien bei Bedarf und hält sie offen.
|
||
//
|
||
// Bei 10–40 Workspaces (siehe AUSBAUSTUFEN.md 4.4) ist das unproblematisch —
|
||
// Verdrängung selten gebrauchter Dateien würde erst bei einer deutlich
|
||
// höheren Zahl nötig, siehe AUSBAUSTUFEN.md 1.2.
|
||
type Manager struct {
|
||
speicher store.Speicher // SQLite-Dateien oder PostgreSQL-Schemas
|
||
|
||
mu sync.Mutex
|
||
open map[string]*Workspace
|
||
}
|
||
|
||
// NewManager legt einen Manager an, der seine Workspaces über speicher öffnet.
|
||
func NewManager(speicher store.Speicher) *Manager {
|
||
return &Manager{speicher: speicher, open: make(map[string]*Workspace)}
|
||
}
|
||
|
||
// Get liefert den Workspace zu einer ID und öffnet ihn beim ersten Zugriff.
|
||
func (m *Manager) Get(workspaceID string) (*Workspace, error) {
|
||
m.mu.Lock()
|
||
defer m.mu.Unlock()
|
||
|
||
if w, ok := m.open[workspaceID]; ok {
|
||
return w, nil
|
||
}
|
||
|
||
s, err := m.speicher.OpenWorkspace(workspaceID)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("Workspace %s konnte nicht geöffnet werden: %w", workspaceID, err)
|
||
}
|
||
|
||
w := &Workspace{ID: workspaceID, Store: s, Search: search.NewRegistry(s)}
|
||
m.open[workspaceID] = w
|
||
return w, nil
|
||
}
|
||
|
||
// Delete schließt den Workspace, falls offen, und entfernt seinen Bereich
|
||
// unwiederbringlich — die Datei samt Nebendateien bzw. das Schema samt
|
||
// Tabellen (siehe store.Speicher.DeleteWorkspace).
|
||
//
|
||
// Räumt nur den Bereich ab — die Kontroll-Datenbank (workspaces, key_index)
|
||
// muss getrennt bereinigt werden, siehe ControlStore.DeleteWorkspace.
|
||
// Reihenfolge ist wichtig: Erst dort löschen, dann hier — sonst verweist die
|
||
// Kontroll-Datenbank kurzzeitig auf einen Bereich, den es nicht mehr gibt.
|
||
func (m *Manager) Delete(workspaceID string) error {
|
||
m.mu.Lock()
|
||
if w, ok := m.open[workspaceID]; ok {
|
||
w.Store.Close()
|
||
delete(m.open, workspaceID)
|
||
}
|
||
m.mu.Unlock()
|
||
|
||
return m.speicher.DeleteWorkspace(workspaceID)
|
||
}
|
||
|
||
// Speicher gibt den Speicher zurück, über den der Manager öffnet — für die
|
||
// Systemseite (Beschreibung, Kapazität).
|
||
func (m *Manager) Speicher() store.Speicher { return m.speicher }
|
||
|
||
// Offen liefert die gerade geöffneten Workspaces, nach ID sortiert. Eine
|
||
// Momentaufnahme: Währenddessen kann ein weiterer geöffnet werden.
|
||
func (m *Manager) Offen() []*Workspace {
|
||
m.mu.Lock()
|
||
defer m.mu.Unlock()
|
||
|
||
liste := make([]*Workspace, 0, len(m.open))
|
||
for _, w := range m.open {
|
||
liste = append(liste, w)
|
||
}
|
||
sort.Slice(liste, func(i, j int) bool { return liste[i].ID < liste[j].ID })
|
||
return liste
|
||
}
|
||
|
||
// Ort nennt, wo die Daten eines Workspace liegen — Schema oder Datei (siehe
|
||
// store.Speicher.Ort). Öffnet dafür nichts.
|
||
func (m *Manager) Ort(workspaceID string) string {
|
||
return m.speicher.Ort(workspaceID)
|
||
}
|
||
|
||
// Close schließt alle offenen Workspace-Dateien.
|
||
func (m *Manager) Close() error {
|
||
m.mu.Lock()
|
||
defer m.mu.Unlock()
|
||
|
||
var erster error
|
||
for id, w := range m.open {
|
||
if err := w.Store.Close(); err != nil && erster == nil {
|
||
erster = err
|
||
}
|
||
delete(m.open, id)
|
||
}
|
||
return erster
|
||
}
|