mirror of
https://github.com/kopia/kopia.git
synced 2026-03-08 17:26:23 -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>
96 lines
2.2 KiB
Go
96 lines
2.2 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/kopia/kopia/internal/clock"
|
|
"github.com/kopia/kopia/internal/repotesting"
|
|
"github.com/kopia/kopia/repo"
|
|
"github.com/kopia/kopia/repo/maintenance"
|
|
)
|
|
|
|
type testServer struct {
|
|
mu sync.Mutex
|
|
|
|
runCounter atomic.Int32
|
|
refreshSchedulerCount atomic.Int32
|
|
|
|
log func(msg string, args ...any)
|
|
|
|
// +checklocks:mu
|
|
err error
|
|
}
|
|
|
|
func (s *testServer) runMaintenanceTask(ctx context.Context, dr repo.DirectRepository) error {
|
|
s.runCounter.Add(1)
|
|
|
|
if s.log != nil {
|
|
s.log("runMaintenanceTask")
|
|
}
|
|
|
|
s.mu.Lock()
|
|
ne := s.err
|
|
s.err = nil
|
|
s.mu.Unlock()
|
|
|
|
return ne
|
|
}
|
|
|
|
func (s *testServer) refreshScheduler(reason string) {
|
|
s.refreshSchedulerCount.Add(1)
|
|
}
|
|
|
|
func TestServerMaintenance(t *testing.T) {
|
|
ctx, env := repotesting.NewEnvironment(t, repotesting.FormatNotImportant)
|
|
|
|
require.NoError(t, repo.DirectWriteSession(ctx, env.RepositoryWriter, repo.WriteSessionOptions{}, func(ctx context.Context, dw repo.DirectRepositoryWriter) error {
|
|
return maintenance.SetParams(ctx, dw, &maintenance.Params{
|
|
Owner: env.Repository.ClientOptions().UsernameAtHost(),
|
|
QuickCycle: maintenance.CycleParams{
|
|
Enabled: true,
|
|
Interval: 5 * time.Second,
|
|
},
|
|
FullCycle: maintenance.CycleParams{
|
|
Enabled: true,
|
|
Interval: 10 * time.Second,
|
|
},
|
|
})
|
|
}))
|
|
|
|
ts := &testServer{log: t.Logf}
|
|
|
|
mm := startMaintenanceManager(ctx, env.RepositoryWriter, ts, time.Minute)
|
|
require.Equal(t, time.Time{}, mm.nextMaintenanceNoEarlierThan)
|
|
|
|
defer mm.stop(ctx)
|
|
|
|
// trigger and make sure it runs
|
|
mm.trigger()
|
|
require.Eventually(t, func() bool {
|
|
return ts.runCounter.Load() == 1 && ts.refreshSchedulerCount.Load() == 1
|
|
}, 3*time.Second, 10*time.Millisecond)
|
|
|
|
ts.mu.Lock()
|
|
ts.err = errors.Errorf("some error")
|
|
ts.mu.Unlock()
|
|
|
|
mm.trigger()
|
|
|
|
require.Eventually(t, func() bool {
|
|
mm.mu.Lock()
|
|
defer mm.mu.Unlock()
|
|
|
|
return ts.runCounter.Load() == 2 && !mm.nextMaintenanceNoEarlierThan.IsZero()
|
|
}, 3*time.Second, 10*time.Millisecond)
|
|
|
|
// after a failure next maintenance time should be deferred by a minute.
|
|
require.Greater(t, mm.nextMaintenanceTime().Sub(clock.Now()), 50*time.Second)
|
|
}
|