Files
archivmail/internal/index/tenant_manager.go
T
sysopsandClaude Sonnet 4.6 5f63bfe8d4 chore(PROJ-60): Xapian-Legacy-Backend vollständig aus dem Code entfernt
Xapian war seit der Manticore-Migration (PROJ-30) nur noch ein
build-tag-gatetes, in Produktion nie genutztes Legacy-Backend.
internal/index/xapian.go, xapian_stub.go, xapian_wrapper.cpp/.h entfernt;
index.New() unterstützt jetzt nur noch "manticore". Default-Fallbacks in
Nebenwerkzeugen (archivmail-import/-export, cmd_export/cmd_reindex/...)
von "xapian" auf "manticore" korrigiert, Xapian-spezifische Tests entfernt,
Kommentare bereinigt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 00:12:06 +02:00

112 lines
2.8 KiB
Go

package index
import (
"fmt"
"os"
"path/filepath"
"sync"
)
// TenantIndexManager manages a pool of single-Indexer instances, one per tenant,
// for filesystem-backed backends created via New().
// Tenant 0 / nil maps to the global index used by superadmin and as a fallback.
type TenantIndexManager struct {
basePath string
batchSize int
backend string
mu sync.RWMutex
pool map[int64]Indexer // tenant_id -> Indexer
global Indexer // tenant_id == 0 / nil (superadmin, fallback)
}
// NewTenantIndexManager creates a new TenantIndexManager.
// The global index lives at basePath directly; per-tenant indexes at basePath/tenant-<id>/.
func NewTenantIndexManager(basePath string, batchSize int, backend string) (*TenantIndexManager, error) {
// Ensure the base path directory exists.
if err := os.MkdirAll(basePath, 0o750); err != nil {
return nil, fmt.Errorf("tenant index manager: mkdir base: %w", err)
}
global, err := New(basePath, batchSize, backend)
if err != nil {
return nil, fmt.Errorf("tenant index manager: open global: %w", err)
}
return &TenantIndexManager{
basePath: basePath,
batchSize: batchSize,
backend: backend,
pool: make(map[int64]Indexer),
global: global,
}, nil
}
// ForTenant returns the Indexer for the given tenant.
// nil or pointer to 0 returns the global index.
// Other tenant IDs lazily open a per-tenant index at basePath/tenant-<id>/.
func (m *TenantIndexManager) ForTenant(tenantID *int64) Indexer {
if tenantID == nil || *tenantID == 0 {
return m.global
}
id := *tenantID
// Fast path: read lock.
m.mu.RLock()
if idx, ok := m.pool[id]; ok {
m.mu.RUnlock()
return idx
}
m.mu.RUnlock()
// Slow path: write lock, create if not exists.
m.mu.Lock()
defer m.mu.Unlock()
// Double check after acquiring write lock.
if idx, ok := m.pool[id]; ok {
return idx
}
dir := filepath.Join(m.basePath, fmt.Sprintf("tenant-%d", id))
if err := os.MkdirAll(dir, 0o750); err != nil {
// Return global as fallback on error.
return m.global
}
idx, err := New(dir, m.batchSize, m.backend)
if err != nil {
// Return global as fallback on error.
return m.global
}
m.pool[id] = idx
return idx
}
// Global returns the global (non-tenant) index.
func (m *TenantIndexManager) Global() Indexer {
return m.global
}
// Close closes all indexes (global + per-tenant).
func (m *TenantIndexManager) Close() error {
m.mu.Lock()
defer m.mu.Unlock()
var firstErr error
for id, idx := range m.pool {
if err := idx.Close(); err != nil && firstErr == nil {
firstErr = fmt.Errorf("close tenant-%d index: %w", id, err)
}
delete(m.pool, id)
}
if m.global != nil {
if err := m.global.Close(); err != nil && firstErr == nil {
firstErr = fmt.Errorf("close global index: %w", err)
}
}
return firstErr
}