mirror of
https://github.com/kopia/kopia.git
synced 2026-05-11 16:25:13 -04:00
* feat(server): reduce server refreshes of the repository Previously each source would refresh itself from the repository very frequently to determine the upcoming snapshot time. This change refactors source manager so it does not own the repository connection on its own but instead delegates all policy reads through the server. Also introduces a new server scheduler that is responsible for centrally managing the snapshot schedule and triggering snapshots when they are due. * Update cli/command_server_start.go Co-authored-by: Shikhar Mall <mall.shikhar.in@gmail.com> * Update internal/server/server.go Co-authored-by: Shikhar Mall <mall.shikhar.in@gmail.com> * Update internal/server/server_maintenance.go Co-authored-by: Shikhar Mall <mall.shikhar.in@gmail.com> * pr feedback --------- Co-authored-by: Shikhar Mall <mall.shikhar.in@gmail.com>
87 lines
2.6 KiB
Go
87 lines
2.6 KiB
Go
package policy
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/kopia/kopia/repo"
|
|
"github.com/kopia/kopia/repo/manifest"
|
|
"github.com/kopia/kopia/snapshot"
|
|
)
|
|
|
|
// ApplyRetentionPolicy applies retention policy to a given source by deleting expired snapshots.
|
|
func ApplyRetentionPolicy(ctx context.Context, rep repo.RepositoryWriter, sourceInfo snapshot.SourceInfo, reallyDelete bool) ([]manifest.ID, error) {
|
|
// it is desired to not allow snapshots to be deleted by repository clients,
|
|
// while still maintain the ability to apply snapshot retention policies server-side.
|
|
if remote, ok := rep.(repo.RemoteRetentionPolicy); ok {
|
|
if sourceInfo.UserName == rep.ClientOptions().Username && sourceInfo.Host == rep.ClientOptions().Hostname {
|
|
// for repository clients, apply retention policy on the server.
|
|
log(ctx).Debug("applying retention policy on the server")
|
|
|
|
//nolint:wrapcheck
|
|
return remote.ApplyRetentionPolicy(ctx, sourceInfo.Path, reallyDelete)
|
|
}
|
|
}
|
|
|
|
snapshots, err := snapshot.ListSnapshots(ctx, rep, sourceInfo)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error listing snapshots")
|
|
}
|
|
|
|
toDelete, err := getExpiredSnapshots(ctx, rep, snapshots)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "unable to compute snapshots to delete")
|
|
}
|
|
|
|
if reallyDelete {
|
|
for _, manifestID := range toDelete {
|
|
if err := rep.DeleteManifest(ctx, manifestID); err != nil {
|
|
return toDelete, errors.Wrapf(err, "error deleting manifest %v", manifestID)
|
|
}
|
|
}
|
|
}
|
|
|
|
return toDelete, nil
|
|
}
|
|
|
|
func getExpiredSnapshots(ctx context.Context, rep repo.Repository, snapshots []*snapshot.Manifest) ([]manifest.ID, error) {
|
|
var toDelete []manifest.ID
|
|
|
|
for _, snapshotGroup := range snapshot.GroupBySource(snapshots) {
|
|
td, err := getExpiredSnapshotsForSource(ctx, rep, snapshotGroup)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
toDelete = append(toDelete, td...)
|
|
}
|
|
|
|
return toDelete, nil
|
|
}
|
|
|
|
func getExpiredSnapshotsForSource(ctx context.Context, rep repo.Repository, snapshots []*snapshot.Manifest) ([]manifest.ID, error) {
|
|
src := snapshots[0].Source
|
|
|
|
pol, _, _, err := GetEffectivePolicy(ctx, rep, src)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
pol.RetentionPolicy.ComputeRetentionReasons(snapshots)
|
|
|
|
var toDelete []manifest.ID
|
|
|
|
for _, s := range snapshots {
|
|
if len(s.RetentionReasons) == 0 && len(s.Pins) == 0 {
|
|
log(ctx).Debugf(" deleting %v", s.StartTime)
|
|
toDelete = append(toDelete, s.ID)
|
|
} else {
|
|
log(ctx).Debugf(" keeping %v retention: [%v] pins: [%v]", s.StartTime.ToTime(), strings.Join(s.RetentionReasons, ","), strings.Join(s.Pins, ","))
|
|
}
|
|
}
|
|
|
|
return toDelete, nil
|
|
}
|