CI / changes (push) Successful in 8s
CI / commitlint (push) Has been skipped
CI / openapi (push) Successful in 25s
CI / web (push) Successful in 32s
CI / go (push) Successful in 57s
CI / bird2 (push) Successful in 15s
CI / release (push) Successful in 3m18s
Implemented new endpoints for estimating and pruning revisions, including detailed schemas for requests and responses. The `RevisionPruneEstimate` and `RevisionPruneResult` components were added to the OpenAPI documentation, enhancing the API's functionality for managing revision retention. Updated the backend to support these operations and integrated them into the tenant settings UI for improved user interaction.
119 lines
2.9 KiB
Go
119 lines
2.9 KiB
Go
package store
|
|
|
|
import (
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
func (m *Memory) prunableRevisionIDsLocked(tenantID string, cutoff time.Time) []string {
|
|
var newest *Revision
|
|
for _, rev := range m.revisions {
|
|
if rev == nil || rev.TenantID != tenantID {
|
|
continue
|
|
}
|
|
if newest == nil || rev.CreatedAt.After(newest.CreatedAt) {
|
|
newest = rev
|
|
}
|
|
}
|
|
if newest == nil {
|
|
return nil
|
|
}
|
|
protected := map[string]struct{}{newest.ID: {}}
|
|
for _, sp := range m.speakers {
|
|
if sp == nil || sp.TenantID != tenantID {
|
|
continue
|
|
}
|
|
if sp.LastAppliedRevisionID != nil && strings.TrimSpace(*sp.LastAppliedRevisionID) != "" {
|
|
protected[strings.TrimSpace(*sp.LastAppliedRevisionID)] = struct{}{}
|
|
}
|
|
}
|
|
for speakerID, info := range m.publishedRevision {
|
|
sp, ok := m.speakers[speakerID]
|
|
if !ok || sp == nil || sp.TenantID != tenantID {
|
|
continue
|
|
}
|
|
if strings.TrimSpace(info.RevisionID) != "" {
|
|
protected[strings.TrimSpace(info.RevisionID)] = struct{}{}
|
|
}
|
|
}
|
|
var ids []string
|
|
for id, rev := range m.revisions {
|
|
if rev == nil || rev.TenantID != tenantID {
|
|
continue
|
|
}
|
|
if !rev.CreatedAt.Before(cutoff) {
|
|
continue
|
|
}
|
|
if _, keep := protected[id]; keep {
|
|
continue
|
|
}
|
|
ids = append(ids, id)
|
|
}
|
|
return ids
|
|
}
|
|
|
|
func revisionBytesEstimate(rev *Revision, prefixes []PrefixRow) int64 {
|
|
if rev == nil {
|
|
return 0
|
|
}
|
|
var n int64
|
|
n += int64(len(rev.ContentHash))
|
|
for _, v := range rev.PreviewFragments {
|
|
n += int64(len(v))
|
|
}
|
|
for _, pr := range prefixes {
|
|
n += int64(len(pr.Prefix) + len(pr.Source))
|
|
}
|
|
return n
|
|
}
|
|
|
|
func (m *Memory) EstimateRevisionPrune(tenantID string, cutoff time.Time, retentionMinutes int) (RevisionPruneEstimate, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
ids := m.prunableRevisionIDsLocked(tenantID, cutoff)
|
|
var bytes int64
|
|
for _, id := range ids {
|
|
bytes += revisionBytesEstimate(m.revisions[id], m.revPrefixes[id])
|
|
}
|
|
prefixRows := 0
|
|
for _, id := range ids {
|
|
prefixRows += len(m.revPrefixes[id])
|
|
}
|
|
return RevisionPruneEstimate{
|
|
RetentionMinutes: retentionMinutes,
|
|
CutoffAt: cutoff.UTC(),
|
|
RevisionCount: len(ids),
|
|
PrefixRowCount: prefixRows,
|
|
OrphanSnapshotCount: 0,
|
|
BytesEstimate: bytes,
|
|
}, nil
|
|
}
|
|
|
|
func (m *Memory) PruneRevisionsWithStats(tenantID string, cutoff time.Time) (RevisionPruneResult, error) {
|
|
est, err := m.EstimateRevisionPrune(tenantID, cutoff, 0)
|
|
if err != nil {
|
|
return RevisionPruneResult{}, err
|
|
}
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
ids := m.prunableRevisionIDsLocked(tenantID, cutoff)
|
|
for _, id := range ids {
|
|
delete(m.revisions, id)
|
|
delete(m.revPrefixes, id)
|
|
}
|
|
return RevisionPruneResult{
|
|
DeletedRevisions: len(ids),
|
|
DeletedPrefixSnapshots: 0,
|
|
DeletedPrefixRows: est.PrefixRowCount,
|
|
BytesEstimate: est.BytesEstimate,
|
|
}, nil
|
|
}
|
|
|
|
func (m *Memory) PruneRevisionsBefore(tenantID string, cutoff time.Time) (int, error) {
|
|
res, err := m.PruneRevisionsWithStats(tenantID, cutoff)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return res.DeletedRevisions, nil
|
|
}
|