Repository: kopia/kopia
Pull request #2853 (2023-03-27 03:19:26)

fix(repository): fixed connect broken since rclone v1.61.0
https://github.com/rclone/rclone/commit/4444d2d102cc187f8cc4907f3e06611576c10a36#diff-3bfe93b28218b8e93a7480a5684d21ac566795a0fa8e540ec1bc6cbe7028439eR295

Fix https://github.com/kopia/kopia/issues/2655.

e.g. `rclone -v serve webdav Disk/Kopia_test --addr 127.0.0.1:0`
old: NOTICE: Local file system at .../rclone-v1.59.2-windows-amd64: WebDav Server started on http://127.0.0.1:41656/
new: NOTICE: Local file system at ...: WebDav Server started on [http://127.0.0.1:51898/]
p.s. They will be https in Kopia.

Note: 
```
if l[len(l)-1:] == "]" {
						rcloneAddressChan <- l[p:len(l)-1]
```
has been considered, but regexp permit more flexibility and reliability for future.

jkowalski: Thanks for the fix, looking at the regexp, it should work with both old and new rclone, but would be nice to check in both to make sure we don't break people.

While at it, can you upgrade rclone used for testing to the latest (update `tools.mk`, run `make regenerate-checksums` and paste the appropriate checksums into `tools/gettool/checksums.txt`)
codecov[bot]: ## [Codecov](https://codecov.io/gh/kopia/kopia/pull/2853?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia) Report
Patch coverage has no change and project coverage change: **`-0.04`** :warning:
> Comparison is base [(`c8d6112`)](https://codecov.io/gh/kopia/kopia/commit/c8d6112aaa5cda854f8606b2250c0956b7923e0f?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia) 75.25% compared to head [(`b420b67`)](https://codecov.io/gh/kopia/kopia/pull/2853?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia) 75.21%.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #2853      +/-   ##
==========================================
- Coverage   75.25%   75.21%   -0.04%     
==========================================
  Files         457      457              
  Lines       35865    35865              
==========================================
- Hits        26990    26977      -13     
- Misses       6980     6990      +10     
- Partials     1895     1898       +3     
```


[see 5 files with indirect coverage changes](https://codecov.io/gh/kopia/kopia/pull/2853/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia)

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia)

</details>

[:umbrella: View full report in Codecov by Sentry](https://codecov.io/gh/kopia/kopia/pull/2853?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia).


Patch:
diff --git a/Makefile b/Makefile
index 677f0d5d..d5ecf31f 100644
--- a/Makefile
+++ b/Makefile
@@ -480,3 +480,4 @@ perf-benchmark-results:
 	gcloud compute scp $(PERF_BENCHMARK_INSTANCE):psrecord-* tests/perf_benchmark --zone=$(PERF_BENCHMARK_INSTANCE_ZONE) 
 	gcloud compute scp $(PERF_BENCHMARK_INSTANCE):repo-size-* tests/perf_benchmark --zone=$(PERF_BENCHMARK_INSTANCE_ZONE)
 	(cd tests/perf_benchmark && go run process_results.go)
+
diff --git a/cli/command_repository_connect.go b/cli/command_repository_connect.go
index 60cd9e3b..d4a599ad 100644
--- a/cli/command_repository_connect.go
+++ b/cli/command_repository_connect.go
@@ -54,6 +54,7 @@ type connectOptions struct {
 	connectUsername               string
 	connectCheckForUpdates        bool
 	connectReadonly               bool
+	connectPermissiveCacheLoading bool
 	connectDescription            string
 	connectEnableActions          bool
 
@@ -72,6 +73,7 @@ func (c *connectOptions) setup(svc appServices, cmd *kingpin.CmdClause) {
 	cmd.Flag("override-username", "Override username used by this repository connection").Hidden().StringVar(&c.connectUsername)
 	cmd.Flag("check-for-updates", "Periodically check for Kopia updates on GitHub").Default("true").Envar(svc.EnvName(checkForUpdatesEnvar)).BoolVar(&c.connectCheckForUpdates)
 	cmd.Flag("readonly", "Make repository read-only to avoid accidental changes").BoolVar(&c.connectReadonly)
+	cmd.Flag("permissive-cache-loading", "Do not fail when loading bad cache index entries.  Repository must be opened in read-only mode").Hidden().BoolVar(&c.connectPermissiveCacheLoading)
 	cmd.Flag("description", "Human-readable description of the repository").StringVar(&c.connectDescription)
 	cmd.Flag("enable-actions", "Allow snapshot actions").BoolVar(&c.connectEnableActions)
 	cmd.Flag("repository-format-cache-duration", "Duration of kopia.repository format blob cache").Hidden().DurationVar(&c.formatBlobCacheDuration)
@@ -98,6 +100,7 @@ func (c *connectOptions) toRepoConnectOptions() *repo.ConnectOptions {
 			Hostname:                c.connectHostname,
 			Username:                c.connectUsername,
 			ReadOnly:                c.connectReadonly,
+			PermissiveCacheLoading:  c.connectPermissiveCacheLoading,
 			Description:             c.connectDescription,
 			EnableActions:           c.connectEnableActions,
 			FormatBlobCacheDuration: c.getFormatBlobCacheDuration(),
diff --git a/cli/command_repository_set_client.go b/cli/command_repository_set_client.go
index 94dce0b4..b691b4fc 100644
--- a/cli/command_repository_set_client.go
+++ b/cli/command_repository_set_client.go
@@ -10,11 +10,12 @@
 )
 
 type commandRepositorySetClient struct {
-	repoClientOptionsReadOnly    bool
-	repoClientOptionsReadWrite   bool
-	repoClientOptionsDescription []string
-	repoClientOptionsUsername    []string
-	repoClientOptionsHostname    []string
+	repoClientOptionsReadOnly               bool
+	repoClientOptionsReadWrite              bool
+	repoClientOptionsPermissiveCacheLoading bool
+	repoClientOptionsDescription            []string
+	repoClientOptionsUsername               []string
+	repoClientOptionsHostname               []string
 
 	formatBlobCacheDuration time.Duration
 	disableFormatBlobCache  bool
@@ -27,6 +28,7 @@ func (c *commandRepositorySetClient) setup(svc appServices, parent commandParent
 
 	cmd.Flag("read-only", "Set repository to read-only").BoolVar(&c.repoClientOptionsReadOnly)
 	cmd.Flag("read-write", "Set repository to read-write").BoolVar(&c.repoClientOptionsReadWrite)
+	cmd.Flag("permissive-cache-loading", "Do not fail when loading bad cache index entries.  Repository must be opened in read-only mode").Hidden().BoolVar(&c.repoClientOptionsPermissiveCacheLoading)
 	cmd.Flag("description", "Change description").StringsVar(&c.repoClientOptionsDescription)
 	cmd.Flag("username", "Change username").StringsVar(&c.repoClientOptionsUsername)
 	cmd.Flag("hostname", "Change hostname").StringsVar(&c.repoClientOptionsHostname)
@@ -64,6 +66,17 @@ func (c *commandRepositorySetClient) run(ctx context.Context, rep repo.Repositor
 		}
 	}
 
+	if c.repoClientOptionsPermissiveCacheLoading {
+		if !opt.PermissiveCacheLoading {
+			log(ctx).Infof("Repository fails on read of bad index blobs.")
+		} else {
+			opt.PermissiveCacheLoading = true
+			anyChange = true
+
+			log(ctx).Infof("Setting to load indicies into cache permissively.")
+		}
+	}
+
 	if v := c.repoClientOptionsDescription; len(v) > 0 {
 		opt.Description = v[0]
 		anyChange = true
diff --git a/go.mod b/go.mod
index 1af589d7..68071fef 100644
--- a/go.mod
+++ b/go.mod
@@ -31,7 +31,7 @@ require (
 	github.com/kopia/htmluibuild v0.0.0-20230326183719-f482ef17e2c9
 	github.com/kylelemons/godebug v1.1.0
 	github.com/mattn/go-colorable v0.1.13
-	github.com/minio/minio-go/v7 v7.0.49
+	github.com/minio/minio-go/v7 v7.0.50
 	github.com/natefinch/atomic v1.0.1
 	github.com/pierrec/lz4 v2.6.1+incompatible
 	github.com/pkg/errors v0.9.1
@@ -102,7 +102,7 @@ require (
 	github.com/jmespath/go-jmespath v0.4.0 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
-	github.com/klauspost/cpuid/v2 v2.2.3 // indirect
+	github.com/klauspost/cpuid/v2 v2.2.4 // indirect
 	github.com/kr/fs v0.1.0 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/mattn/go-ieproxy v0.0.1 // indirect
diff --git a/go.sum b/go.sum
index 848223fc..b26d9d52 100644
--- a/go.sum
+++ b/go.sum
@@ -178,8 +178,8 @@ github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQs
 github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
 github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
 github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
-github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU=
-github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
+github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
+github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
 github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
 github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
 github.com/klauspost/reedsolomon v1.11.7 h1:9uaHU0slncktTEEg4+7Vl7q7XUNMBUOK4R9gnKhMjAU=
@@ -214,8 +214,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk
 github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
 github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
 github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
-github.com/minio/minio-go/v7 v7.0.49 h1:dE5DfOtnXMXCjr/HWI6zN9vCrY6Sv666qhhiwUMvGV4=
-github.com/minio/minio-go/v7 v7.0.49/go.mod h1:UI34MvQEiob3Cf/gGExGMmzugkM/tNgbFypNDy5LMVc=
+github.com/minio/minio-go/v7 v7.0.50 h1:4IL4V8m/kI90ZL6GupCARZVrBv8/XrcKcJhaJ3iz68k=
+github.com/minio/minio-go/v7 v7.0.50/go.mod h1:IbbodHyjUAguneyucUaahv+VMNs/EOTV9du7A7/Z3HU=
 github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
 github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
diff --git a/repo/blob/rclone/rclone_storage.go b/repo/blob/rclone/rclone_storage.go
index efaf21db..0d6e1a5c 100644
--- a/repo/blob/rclone/rclone_storage.go
+++ b/repo/blob/rclone/rclone_storage.go
@@ -9,6 +9,7 @@
 	"os"
 	"os/exec"
 	"path/filepath"
+	"regexp"
 	"strings"
 	"sync/atomic"
 	"time"
@@ -161,12 +162,15 @@ func (r *rcloneStorage) runRCloneAndWaitForServerAddress(ctx context.Context, c
 
 			var lastOutput string
 
+			serverRegexp := regexp.MustCompile(`(?i)WebDav Server started on \[?(https://.+:\d{1,5}/)\]?`)
+
 			for s.Scan() {
 				l := s.Text()
 				lastOutput = l
 
-				if p := strings.Index(l, "https://"); p >= 0 {
-					rcloneAddressChan <- l[p:]
+				params := serverRegexp.FindStringSubmatch(l)
+				if params != nil {
+					rcloneAddressChan <- params[1]
 
 					go r.processStderrStatus(ctx, statsMarker, s)
 
diff --git a/repo/blob/retrying/retrying_storage.go b/repo/blob/retrying/retrying_storage.go
index 9c158703..dda05ccf 100644
--- a/repo/blob/retrying/retrying_storage.go
+++ b/repo/blob/retrying/retrying_storage.go
@@ -72,7 +72,7 @@ func isRetriable(err error) bool {
 	case errors.Is(err, blob.ErrBlobAlreadyExists):
 		return false
 
-	case errors.Is(err, repo.ErrRepositoryUnavailableDueToUpgrageInProgress):
+	case errors.Is(err, repo.ErrRepositoryUnavailableDueToUpgradeInProgress):
 		// hard-fail when upgrade is in progress
 		return false
 
diff --git a/repo/content/committed_content_index.go b/repo/content/committed_content_index.go
index 90d313c1..c4a872f9 100644
--- a/repo/content/committed_content_index.go
+++ b/repo/content/committed_content_index.go
@@ -26,8 +26,9 @@
 const smallIndexEntryCountThreshold = 100
 
 type committedContentIndex struct {
-	rev   atomic.Int64
-	cache committedContentIndexCache
+	rev                    atomic.Int64
+	cache                  committedContentIndexCache
+	permissiveCacheLoading bool
 
 	mu sync.RWMutex
 	// +checklocks:mu
@@ -168,6 +169,10 @@ func (c *committedContentIndex) merge(ctx context.Context, indexFiles []blob.ID)
 
 			ndx, err = c.cache.openIndex(ctx, e)
 			if err != nil {
+				if c.permissiveCacheLoading {
+					continue
+				}
+
 				newlyOpened.Close() //nolint:errcheck
 
 				return nil, nil, errors.Wrapf(err, "unable to open pack index %q", e)
@@ -291,7 +296,7 @@ func (c *committedContentIndex) close() error {
 	return nil
 }
 
-func (c *committedContentIndex) fetchIndexBlobs(ctx context.Context, indexBlobs []blob.ID) error {
+func (c *committedContentIndex) fetchIndexBlobs(ctx context.Context, isPermissiveCacheLoading bool, indexBlobs []blob.ID) error {
 	ch, err := c.missingIndexBlobs(ctx, indexBlobs)
 	if err != nil {
 		return err
@@ -304,6 +309,7 @@ func (c *committedContentIndex) fetchIndexBlobs(ctx context.Context, indexBlobs
 	c.log.Debugf("Downloading %v new index blobs...", len(indexBlobs))
 
 	eg, ctx := errgroup.WithContext(ctx)
+
 	for i := 0; i < parallelFetches; i++ {
 		eg.Go(func() error {
 			var data gather.WriteBuffer
@@ -313,6 +319,10 @@ func (c *committedContentIndex) fetchIndexBlobs(ctx context.Context, indexBlobs
 				data.Reset()
 
 				if err := c.fetchOne(ctx, indexBlobID, &data); err != nil {
+					if isPermissiveCacheLoading {
+						c.log.Errorf("skipping bad read of index blob %v", indexBlobID)
+						continue
+					}
 					return errors.Wrapf(err, "error loading index blob %v", indexBlobID)
 				}
 
@@ -355,6 +365,7 @@ func (c *committedContentIndex) missingIndexBlobs(ctx context.Context, blobs []b
 func newCommittedContentIndex(caching *CachingOptions,
 	v1PerContentOverhead func() int,
 	formatProvider format.Provider,
+	permissiveCacheLoading bool,
 	fetchOne func(ctx context.Context, blobID blob.ID, output *gather.WriteBuffer) error,
 	log logging.Logger,
 	minSweepAge time.Duration,
@@ -372,11 +383,12 @@ func newCommittedContentIndex(caching *CachingOptions,
 	}
 
 	return &committedContentIndex{
-		cache:                cache,
-		inUse:                map[blob.ID]index.Index{},
-		v1PerContentOverhead: v1PerContentOverhead,
-		formatProvider:       formatProvider,
-		fetchOne:             fetchOne,
-		log:                  log,
+		cache:                  cache,
+		permissiveCacheLoading: permissiveCacheLoading,
+		inUse:                  map[blob.ID]index.Index{},
+		v1PerContentOverhead:   v1PerContentOverhead,
+		formatProvider:         formatProvider,
+		fetchOne:               fetchOne,
+		log:                    log,
 	}
 }
diff --git a/repo/content/committed_read_manager.go b/repo/content/committed_read_manager.go
index 4c3b940c..bca87cc0 100644
--- a/repo/content/committed_read_manager.go
+++ b/repo/content/committed_read_manager.go
@@ -87,7 +87,8 @@ type SharedManager struct {
 	// lock to protect the set of committed indexes
 	// shared lock will be acquired when writing new content to allow it to happen in parallel
 	// exclusive lock will be acquired during compaction or refresh.
-	indexesLock sync.RWMutex
+	indexesLock            sync.RWMutex
+	permissiveCacheLoading bool
 
 	// maybeRefreshIndexes() will call Refresh() after this point in ime.
 	// +checklocks:indexesLock
@@ -228,7 +229,7 @@ func (sm *SharedManager) loadPackIndexesLocked(ctx context.Context) error {
 			indexBlobIDs = append(indexBlobIDs, b.BlobID)
 		}
 
-		err = sm.committedContents.fetchIndexBlobs(ctx, indexBlobIDs)
+		err = sm.committedContents.fetchIndexBlobs(ctx, sm.permissiveCacheLoading, indexBlobIDs)
 		if err == nil {
 			err = sm.committedContents.use(ctx, indexBlobIDs, ignoreDeletedBefore)
 			if err != nil {
@@ -260,17 +261,19 @@ func (sm *SharedManager) getCacheForContentID(id ID) cache.ContentCache {
 	return sm.contentCache
 }
 
+// indexBlobManager return the index manager for content.
 func (sm *SharedManager) indexBlobManager() (indexblob.Manager, error) {
 	mp, mperr := sm.format.GetMutableParameters()
 	if mperr != nil {
 		return nil, errors.Wrap(mperr, "mutable parameters")
 	}
 
+	var q indexblob.Manager = sm.indexBlobManagerV0
 	if mp.EpochParameters.Enabled {
-		return sm.indexBlobManagerV1, nil
+		q = sm.indexBlobManagerV1
 	}
 
-	return sm.indexBlobManagerV0, nil
+	return q, nil
 }
 
 func (sm *SharedManager) decryptContentAndVerify(payload gather.Bytes, bi Info, output *gather.WriteBuffer) error {
@@ -505,6 +508,7 @@ func(ctx context.Context, blobIDs []blob.ID, outputPrefix blob.ID) error {
 	sm.committedContents = newCommittedContentIndex(caching,
 		sm.format.Encryptor().Overhead,
 		sm.format,
+		sm.permissiveCacheLoading,
 		enc.GetEncryptedBlob,
 		sm.namedLogger("committed-content-index"),
 		caching.MinIndexSweepAge.DurationOrDefault(DefaultIndexCacheSweepAge))
@@ -598,6 +602,7 @@ func NewSharedManager(ctx context.Context, st blob.Storage, prov format.Provider
 		Stats:                   new(Stats),
 		timeNow:                 opts.TimeNow,
 		format:                  prov,
+		permissiveCacheLoading:  opts.PermissiveCacheLoading,
 		minPreambleLength:       defaultMinPreambleLength,
 		maxPreambleLength:       defaultMaxPreambleLength,
 		paddingUnit:             defaultPaddingUnit,
diff --git a/repo/content/content_manager.go b/repo/content/content_manager.go
index 5d66695c..4d4b5c6b 100644
--- a/repo/content/content_manager.go
+++ b/repo/content/content_manager.go
@@ -161,6 +161,10 @@ func (bm *WriteManager) DeleteContent(ctx context.Context, contentID ID) error {
 }
 
 func (bm *WriteManager) maybeRefreshIndexes(ctx context.Context) error {
+	if bm.permissiveCacheLoading {
+		return nil
+	}
+
 	if !bm.disableIndexRefresh.Load() && bm.shouldRefreshIndexes() {
 		if err := bm.Refresh(ctx); err != nil {
 			return errors.Wrap(err, "error refreshing indexes")
@@ -943,10 +947,11 @@ func (bm *WriteManager) MetadataCache() cache.ContentCache {
 
 // ManagerOptions are the optional parameters for manager creation.
 type ManagerOptions struct {
-	TimeNow            func() time.Time // Time provider
-	DisableInternalLog bool
-	RetentionMode      string
-	RetentionPeriod    time.Duration
+	TimeNow                func() time.Time // Time provider
+	DisableInternalLog     bool
+	RetentionMode          string
+	RetentionPeriod        time.Duration
+	PermissiveCacheLoading bool
 }
 
 // CloneOrDefault returns a clone of provided ManagerOptions or default empty struct if nil.
diff --git a/repo/local_config.go b/repo/local_config.go
index 4efcc1ef..a5c672a1 100644
--- a/repo/local_config.go
+++ b/repo/local_config.go
@@ -20,12 +20,16 @@
 
 const configDirMode = 0o700
 
+// ErrCannotWriteToRepoConnectionWithPermissiveCacheLoading error to indicate.
+var ErrCannotWriteToRepoConnectionWithPermissiveCacheLoading = errors.Errorf("cannot write to repo connection with permissive cache loading")
+
 // ClientOptions contains client-specific options that are persisted in local configuration file.
 type ClientOptions struct {
 	Hostname string `json:"hostname"`
 	Username string `json:"username"`
 
-	ReadOnly bool `json:"readonly,omitempty"`
+	ReadOnly               bool `json:"readonly,omitempty"`
+	PermissiveCacheLoading bool `json:"permissiveCacheLoading,omitempty"`
 
 	// Description is human-readable description of the repository to use in the UI.
 	Description string `json:"description,omitempty"`
@@ -149,5 +153,9 @@ func LoadConfigFromFile(fileName string) (*LocalConfig, error) {
 		}
 	}
 
+	if lc.PermissiveCacheLoading && os.Getenv("KOPIA_UPGRADE_LOCK_ENABLED") == "" {
+		return nil, errors.New("must have set KOPIA_UPGRADE_LOCK_ENABLED when connecting to repository with permissive cache loading")
+	}
+
 	return &lc, nil
 }
diff --git a/repo/open.go b/repo/open.go
index d262662d..472912bd 100644
--- a/repo/open.go
+++ b/repo/open.go
@@ -85,9 +85,9 @@ type Options struct {
 // ErrAlreadyInitialized is returned when repository is already initialized in the provided storage.
 var ErrAlreadyInitialized = format.ErrAlreadyInitialized
 
-// ErrRepositoryUnavailableDueToUpgrageInProgress is returned when repository
+// ErrRepositoryUnavailableDueToUpgradeInProgress is returned when repository
 // is undergoing upgrade that requires exclusive access.
-var ErrRepositoryUnavailableDueToUpgrageInProgress = errors.Errorf("repository upgrade in progress")
+var ErrRepositoryUnavailableDueToUpgradeInProgress = errors.Errorf("repository upgrade in progress")
 
 // Open opens a Repository specified in the configuration file.
 func Open(ctx context.Context, configFile, password string, options *Options) (rep Repository, err error) {
@@ -121,6 +121,10 @@ func Open(ctx context.Context, configFile, password string, options *Options) (r
 		return nil, err
 	}
 
+	if lc.PermissiveCacheLoading && !lc.ReadOnly {
+		return nil, ErrCannotWriteToRepoConnectionWithPermissiveCacheLoading
+	}
+
 	if lc.APIServer != nil {
 		return openAPIServer(ctx, lc.APIServer, lc.ClientOptions, lc.Caching, password, options)
 	}
@@ -235,8 +239,9 @@ func openDirect(ctx context.Context, configFile string, lc *LocalConfig, passwor
 func openWithConfig(ctx context.Context, st blob.Storage, cliOpts ClientOptions, password string, options *Options, cacheOpts *content.CachingOptions, configFile string) (DirectRepository, error) {
 	cacheOpts = cacheOpts.CloneOrDefault()
 	cmOpts := &content.ManagerOptions{
-		TimeNow:            defaultTime(options.TimeNowFunc),
-		DisableInternalLog: options.DisableInternalLog,
+		TimeNow:                defaultTime(options.TimeNowFunc),
+		DisableInternalLog:     options.DisableInternalLog,
+		PermissiveCacheLoading: cliOpts.PermissiveCacheLoading,
 	}
 
 	mr := metrics.NewRegistry()
@@ -247,25 +252,7 @@ func openWithConfig(ctx context.Context, st blob.Storage, cliOpts ClientOptions,
 		return nil, errors.Wrap(ferr, "unable to create format manager")
 	}
 
-	if _, err := retry.WithExponentialBackoffMaxRetries(ctx, -1, "wait for upgrade", func() (interface{}, error) {
-		uli, err := fmgr.UpgradeLockIntent()
-		if err != nil {
-			//nolint:wrapcheck
-			return nil, err
-		}
-
-		// retry if upgrade lock has been taken
-		if locked, _ := uli.IsLocked(cmOpts.TimeNow()); locked && options.UpgradeOwnerID != uli.OwnerID {
-			return nil, ErrRepositoryUnavailableDueToUpgrageInProgress
-		}
-
-		return false, nil
-	}, func(internalErr error) bool {
-		return !options.DoNotWaitForUpgrade && errors.Is(internalErr, ErrRepositoryUnavailableDueToUpgrageInProgress)
-	}); err != nil {
-		return nil, err
-	}
-
+	// check features before and perform configuration before performing IO
 	if err := handleMissingRequiredFeatures(ctx, fmgr, options.TestOnlyIgnoreMissingRequiredFeatures); err != nil {
 		return nil, err
 	}
@@ -307,8 +294,33 @@ func openWithConfig(ctx context.Context, st blob.Storage, cliOpts ClientOptions,
 		st = wrapLockingStorage(st, blobcfg)
 	}
 
-	// background/interleaving upgrade lock storage monitor
-	st = upgradeLockMonitor(fmgr, options.UpgradeOwnerID, st, cmOpts.TimeNow, options.OnFatalError, options.TestOnlyIgnoreMissingRequiredFeatures)
+	_, err = retry.WithExponentialBackoffMaxRetries(ctx, -1, "wait for upgrade", func() (interface{}, error) {
+		//nolint:govet
+		uli, err := fmgr.UpgradeLockIntent()
+		if err != nil {
+			//nolint:wrapcheck
+			return nil, err
+		}
+
+		// retry if upgrade lock has been taken
+		if !cliOpts.PermissiveCacheLoading {
+			if locked, _ := uli.IsLocked(cmOpts.TimeNow()); locked && options.UpgradeOwnerID != uli.OwnerID {
+				return nil, ErrRepositoryUnavailableDueToUpgradeInProgress
+			}
+		}
+
+		return false, nil
+	}, func(internalErr error) bool {
+		return !options.DoNotWaitForUpgrade && errors.Is(internalErr, ErrRepositoryUnavailableDueToUpgradeInProgress)
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	if !cliOpts.PermissiveCacheLoading {
+		// background/interleaving upgrade lock storage monitor
+		st = upgradeLockMonitor(fmgr, options.UpgradeOwnerID, st, cmOpts.TimeNow, options.OnFatalError, options.TestOnlyIgnoreMissingRequiredFeatures)
+	}
 
 	scm, ferr := content.NewSharedManager(ctx, st, fmgr, cacheOpts, cmOpts, mr)
 	if ferr != nil {
@@ -456,7 +468,7 @@ func upgradeLockMonitor(
 		if uli != nil {
 			// only allow the upgrade owner to perform storage operations
 			if locked, _ := uli.IsLocked(now()); locked && upgradeOwnerID != uli.OwnerID {
-				return ErrRepositoryUnavailableDueToUpgrageInProgress
+				return ErrRepositoryUnavailableDueToUpgradeInProgress
 			}
 		}
 
diff --git a/tools/gettool/checksums.txt b/tools/gettool/checksums.txt
index 456c1143..5e16082c 100644
--- a/tools/gettool/checksums.txt
+++ b/tools/gettool/checksums.txt
@@ -32,12 +32,12 @@ https://github.com/kopia/kopia/releases/download/v0.8.4/kopia-0.8.4-linux-x64.ta
 https://github.com/kopia/kopia/releases/download/v0.8.4/kopia-0.8.4-macOS-arm64.tar.gz: 1b4e2f151ca0db80a7e0ee7b164697af7c6aaeae58f0846952693da327e46af7
 https://github.com/kopia/kopia/releases/download/v0.8.4/kopia-0.8.4-macOS-x64.tar.gz: 818e466f8404d9d4805a4b86386d8388e90979b54ffa87f1858890cf13311902
 https://github.com/kopia/kopia/releases/download/v0.8.4/kopia-0.8.4-windows-x64.zip: 25529dffea8ecfd1206cd6e8eb76e45bdcdd334fc99ccb14683fe56c34426837
-https://github.com/rclone/rclone/releases/download/v1.59.0/rclone-v1.59.0-linux-amd64.zip: cb7edcda37ef188dd5461a626f7b66d4c76676bc4cf05cba3bb4850dff3d8a2b
-https://github.com/rclone/rclone/releases/download/v1.59.0/rclone-v1.59.0-linux-arm.zip: 4bbfb1e757467a2601bd97984990f52183623293f20e2c03bfe4a744af2742e3
-https://github.com/rclone/rclone/releases/download/v1.59.0/rclone-v1.59.0-linux-arm64.zip: c95ebf48bcef81e9ee296a803ca77244d111e35a55db9680c78b407ed99bb054
-https://github.com/rclone/rclone/releases/download/v1.59.0/rclone-v1.59.0-osx-amd64.zip: e308c72138c1dc9e72e28a47cbf7bfaaed2cf37c3e9e97cc5a597b2cc06ac85d
-https://github.com/rclone/rclone/releases/download/v1.59.0/rclone-v1.59.0-osx-arm64.zip: 3b0d7d1a140835725d11b4044a9f83f76b9b02281d2b907b16255d73ccdccaab
-https://github.com/rclone/rclone/releases/download/v1.59.0/rclone-v1.59.0-windows-amd64.zip: 82aa8a39e1cc14668a60048c7375ebd45f1bb5734863ad2cac1309c63f05c57f
+https://github.com/rclone/rclone/releases/download/v1.62.2/rclone-v1.62.2-linux-amd64.zip: 6c8676dc56e3d2e26358b5bae616ab3ec95e26181cd9b8692e101dcc0fc966a1
+https://github.com/rclone/rclone/releases/download/v1.62.2/rclone-v1.62.2-linux-arm.zip: a6adb2db09d7d3a546e55248375ec27eb235caff4707c3e5c5c669f5365edbb1
+https://github.com/rclone/rclone/releases/download/v1.62.2/rclone-v1.62.2-linux-arm64.zip: e921f0ac3edb45ea8f1c6b8110ed0be263aaedfb6a5ee98968d5836d3f1aadfc
+https://github.com/rclone/rclone/releases/download/v1.62.2/rclone-v1.62.2-osx-amd64.zip: afef35513c7ce89e9ed9962e2c44c604587de1faa317d9fd3bf6590dc3be8658
+https://github.com/rclone/rclone/releases/download/v1.62.2/rclone-v1.62.2-osx-arm64.zip: efb17668ff5bc7cb632ddc85ad0d38b020bed85ca6a2b798a31a61abb32b0516
+https://github.com/rclone/rclone/releases/download/v1.62.2/rclone-v1.62.2-windows-amd64.zip: 85c623d7808f9d2cf51945e02e98d02b94f9f32ea892237f9a58b544c7a4f4f9
 https://nodejs.org/dist/v16.13.0/node-v16.13.0-darwin-arm64.tar.gz: 46d83fc0bd971db5050ef1b15afc44a6665dee40bd6c1cbaec23e1b40fa49e6d
 https://nodejs.org/dist/v16.13.0/node-v16.13.0-darwin-x64.tar.gz: 37e09a8cf2352f340d1204c6154058d81362fef4ec488b0197b2ce36b3f0367a
 https://nodejs.org/dist/v16.13.0/node-v16.13.0-linux-arm64.tar.gz: 46e3857f5552abd36d9548380d795b043a3ceec2504e69fe1a754fa76012daaf
diff --git a/tools/tools.mk b/tools/tools.mk
index 346dbf94..b586376f 100644
--- a/tools/tools.mk
+++ b/tools/tools.mk
@@ -108,7 +108,7 @@ NODE_VERSION=16.13.0
 HUGO_VERSION=0.89.2
 GOTESTSUM_VERSION=1.7.0
 GORELEASER_VERSION=v0.176.0
-RCLONE_VERSION=1.59.0
+RCLONE_VERSION=1.62.2
 GITCHGLOG_VERSION=0.15.1
 
 # nodejs / npm


Test patch:
diff --git a/internal/epoch/epoch_manager_test.go b/internal/epoch/epoch_manager_test.go
index 808c24fd..6b31cedb 100644
--- a/internal/epoch/epoch_manager_test.go
+++ b/internal/epoch/epoch_manager_test.go
@@ -173,14 +173,11 @@ func TestIndexEpochManager_Parallel(t *testing.T) {
 
 				indexNum++
 
-				var rnd [8]byte
-
-				rand.Read(rnd[:])
-
+				rnd := rand.Uint64()
 				ndx := newFakeIndexWithEntries(indexNum)
 
 				if _, err := te2.mgr.WriteIndex(ctx, map[blob.ID]blob.Bytes{
-					blob.ID(fmt.Sprintf("w%vr%x", worker, rnd)): gather.FromSlice(ndx.Bytes()),
+					blob.ID(fmt.Sprintf("w%vr%0x", worker, rnd)): gather.FromSlice(ndx.Bytes()),
 				}); err != nil {
 					if errors.Is(err, ErrVerySlowIndexWrite) {
 						indexNum--
@@ -713,17 +710,12 @@ func (te *epochManagerTestEnv) getMergedIndexContents(ctx context.Context, blobI
 
 func (te *epochManagerTestEnv) writeIndexFiles(ctx context.Context, ndx ...*fakeIndex) ([]blob.Metadata, error) {
 	shards := map[blob.ID]blob.Bytes{}
-
-	var sessionID [8]byte
-
-	rand.Read(sessionID[:])
+	sessionID := rand.Uint64()
 
 	for _, n := range ndx {
-		var rnd [8]byte
-
-		rand.Read(rnd[:])
+		rnd := rand.Uint64()
 
-		shards[blob.ID(fmt.Sprintf("%x-c%v-s%x", rnd[:], len(ndx), sessionID))] = gather.FromSlice(n.Bytes())
+		shards[blob.ID(fmt.Sprintf("%0x-c%v-s%0x", rnd, len(ndx), sessionID))] = gather.FromSlice(n.Bytes())
 	}
 
 	return te.mgr.WriteIndex(ctx, shards)
diff --git a/repo/blob/s3/s3_versioned_test.go b/repo/blob/s3/s3_versioned_test.go
index 160c4f2d..70548242 100644
--- a/repo/blob/s3/s3_versioned_test.go
+++ b/repo/blob/s3/s3_versioned_test.go
@@ -9,6 +9,7 @@
 	"math/rand"
 	"path"
 	"sort"
+	"sync"
 	"testing"
 	"time"
 
@@ -640,12 +641,29 @@ func randHex(tb testing.TB, length int) string {
 		return fmt.Sprintf("%0x", rand.Uint64())[0:length]
 	}
 
+	return randLongHex(tb, length)
+}
+
+// Protects the initialization of a random number generator for byte sequences
+// that are larger than 16 characters (8 bytes)
+// Notice that this is not a crypto RNG.
+var (
+	rMu sync.Mutex
+	r   = rand.New(rand.NewSource(clock.Now().UnixNano()))
+)
+
+func randLongHex(tb testing.TB, length int) string {
+	tb.Helper()
+
 	byteLength := (length + 1) / 2
 	b := make([]byte, byteLength)
-	n, err := rand.Read(b)
+
+	rMu.Lock()
+	n, err := r.Read(b)
+	rMu.Unlock()
 
 	require.NoError(tb, err)
-	require.Equal(tb, byteLength, n, "unexpected number of bytes from rand.Read")
+	require.Equal(tb, byteLength, n, "unexpected number of bytes while reading RNG")
 
 	return hex.EncodeToString(b)[:length]
 }
diff --git a/repo/content/content_manager_test.go b/repo/content/content_manager_test.go
index 09b775ea..bfdefb0b 100644
--- a/repo/content/content_manager_test.go
+++ b/repo/content/content_manager_test.go
@@ -2083,7 +2083,7 @@ func (s *contentManagerSuite) TestCompression_NonCompressibleData(t *testing.T)
 	nonCompressibleData := make([]byte, 65000)
 	headerID := compression.ByName["pgzip"].HeaderID()
 
-	rand.Read(nonCompressibleData)
+	randRead(nonCompressibleData)
 
 	cid, err := bm.WriteContent(ctx, gather.FromSlice(nonCompressibleData), "", headerID)
 	require.NoError(t, err)
@@ -2283,6 +2283,110 @@ func (s *contentManagerSuite) TestPrefetchContent(t *testing.T) {
 	}
 }
 
+// TestContentPermissiveCacheLoading check that permissive reads read content as recorded.
+func (s *contentManagerSuite) TestContentPermissiveCacheLoading(t *testing.T) {
+	data := blobtesting.DataMap{}
+	timeNow := faketime.AutoAdvance(fakeTime, 1*time.Second)
+	st := blobtesting.NewMapStorage(data, nil, timeNow)
+
+	ctx := testlogging.Context(t)
+
+	tweaks := &contentManagerTestTweaks{
+		ManagerOptions: ManagerOptions{
+			TimeNow: timeNow,
+		},
+	}
+
+	bm := s.newTestContentManagerWithTweaks(t, st, tweaks)
+
+	ids := make([]ID, 100)
+	for i := 0; i < len(ids); i++ {
+		ids[i] = writeContentAndVerify(ctx, t, bm, seededRandomData(i, maxPackCapacity/2))
+
+		for j := 0; j < i; j++ {
+			// verify all contents written so far
+			verifyContent(ctx, t, bm, ids[j], seededRandomData(j, maxPackCapacity/2))
+		}
+
+		// every 10 contents, create new content manager
+		if i%10 == 0 {
+			t.Logf("------- flushing & reopening -----")
+			require.NoError(t, bm.Flush(ctx))
+			require.NoError(t, bm.CloseShared(ctx))
+			bm = s.newTestContentManagerWithTweaks(t, st, tweaks)
+		}
+	}
+
+	require.NoError(t, bm.Flush(ctx))
+	require.NoError(t, bm.CloseShared(ctx))
+
+	tweaks = &contentManagerTestTweaks{
+		ManagerOptions: ManagerOptions{
+			TimeNow:                timeNow,
+			PermissiveCacheLoading: true,
+		},
+	}
+
+	bm = s.newTestContentManagerWithTweaks(t, st, tweaks)
+
+	for i := 0; i < len(ids); i++ {
+		verifyContent(ctx, t, bm, ids[i], seededRandomData(i, maxPackCapacity/2))
+	}
+}
+
+// TestContentIndexPermissiveReadsWithFault check that permissive reads read content as recorded.
+func (s *contentManagerSuite) TestContentIndexPermissiveReadsWithFault(t *testing.T) {
+	data := blobtesting.DataMap{}
+	timeNow := faketime.AutoAdvance(fakeTime, 1*time.Second)
+	st := blobtesting.NewMapStorage(data, nil, timeNow)
+
+	ctx := testlogging.Context(t)
+
+	tweaks := &contentManagerTestTweaks{
+		ManagerOptions: ManagerOptions{
+			TimeNow: timeNow,
+		},
+	}
+
+	bm := s.newTestContentManagerWithTweaks(t, st, tweaks)
+
+	ids := make([]ID, 100)
+	for i := 0; i < len(ids); i++ {
+		ids[i] = writeContentAndVerify(ctx, t, bm, seededRandomData(i, maxPackCapacity/2))
+
+		for j := 0; j < i; j++ {
+			// verify all contents written so far
+			verifyContent(ctx, t, bm, ids[j], seededRandomData(j, maxPackCapacity/2))
+		}
+
+		// every 10 contents, create new content manager
+		if i%10 == 0 {
+			t.Logf("------- flushing & reopening -----")
+			require.NoError(t, bm.Flush(ctx))
+			require.NoError(t, bm.CloseShared(ctx))
+			bm = s.newTestContentManagerWithTweaks(t, st, tweaks)
+		}
+	}
+
+	require.NoError(t, format.WriteLegacyIndexPoisonBlob(ctx, st))
+
+	require.NoError(t, bm.Flush(ctx))
+	require.NoError(t, bm.CloseShared(ctx))
+
+	tweaks = &contentManagerTestTweaks{
+		ManagerOptions: ManagerOptions{
+			TimeNow:                timeNow,
+			PermissiveCacheLoading: true,
+		},
+	}
+
+	bm = s.newTestContentManagerWithTweaks(t, st, tweaks)
+
+	for i := 0; i < len(ids); i++ {
+		verifyContent(ctx, t, bm, ids[i], seededRandomData(i, maxPackCapacity/2))
+	}
+}
+
 func wipeCache(t *testing.T, st cache.Storage) {
 	t.Helper()
 
@@ -2526,7 +2630,7 @@ func makeRandomHexID(t *testing.T, length int) index.ID {
 	t.Helper()
 
 	b := make([]byte, length/2)
-	if _, err := rand.Read(b); err != nil {
+	if _, err := randRead(b); err != nil {
 		t.Fatal("Could not read random bytes", err)
 	}
 
@@ -2577,3 +2681,16 @@ type withDeleted struct {
 func (o withDeleted) GetDeleted() bool {
 	return o.deleted
 }
+
+var (
+	r   = rand.New(rand.NewSource(rand.Int63()))
+	rMu sync.Mutex
+)
+
+func randRead(b []byte) (n int, err error) {
+	rMu.Lock()
+	n, err = r.Read(b)
+	rMu.Unlock()
+
+	return
+}
diff --git a/repo/content/indexblob/index_blob_manager_v0_test.go b/repo/content/indexblob/index_blob_manager_v0_test.go
index 2ca9f6f3..23efad31 100644
--- a/repo/content/indexblob/index_blob_manager_v0_test.go
+++ b/repo/content/indexblob/index_blob_manager_v0_test.go
@@ -167,8 +167,6 @@ func TestIndexBlobManagerStress(t *testing.T) {
 		return
 	}
 
-	rand.Seed(clock.Now().UnixNano())
-
 	for i := range actionsTestIndexBlobManagerStress {
 		actionsTestIndexBlobManagerStress[i].weight = rand.Intn(100)
 		t.Logf("weight[%v] = %v", i, actionsTestIndexBlobManagerStress[i].weight)
@@ -268,8 +266,6 @@ func TestIndexBlobManagerStress(t *testing.T) {
 }
 
 func TestIndexBlobManagerPreventsResurrectOfDeletedContents(t *testing.T) {
-	rand.Seed(clock.Now().UnixNano())
-
 	// the test is randomized and runs very quickly, run it lots of times
 	failed := false
 	for i := 0; i < 100 && !failed; i++ {
@@ -282,8 +278,6 @@ func TestIndexBlobManagerPreventsResurrectOfDeletedContents(t *testing.T) {
 }
 
 func TestCompactionCreatesPreviousIndex(t *testing.T) {
-	rand.Seed(clock.Now().UnixNano())
-
 	storageData := blobtesting.DataMap{}
 
 	fakeTime := faketime.NewTimeAdvance(fakeLocalStartTime, 0)
@@ -333,8 +327,6 @@ func TestCompactionCreatesPreviousIndex(t *testing.T) {
 }
 
 func TestIndexBlobManagerPreventsResurrectOfDeletedContents_RandomizedTimings(t *testing.T) {
-	rand.Seed(clock.Now().UnixNano())
-
 	numAttempts := 1000
 	if testutil.ShouldReduceTestComplexity() {
 		numAttempts = 100
diff --git a/repo/format/upgrade_lock_test.go b/repo/format/upgrade_lock_test.go
index 99420228..6088716c 100644
--- a/repo/format/upgrade_lock_test.go
+++ b/repo/format/upgrade_lock_test.go
@@ -391,11 +391,11 @@ func TestFormatUpgradeDuringOngoingWriteSessions(t *testing.T) {
 	curTime = curTime.Add(formatBlockCacheDuration + time.Second)
 
 	// ongoing writes should get interrupted this time
-	require.ErrorIs(t, w1.Flush(ctx), repo.ErrRepositoryUnavailableDueToUpgrageInProgress)
+	require.ErrorIs(t, w1.Flush(ctx), repo.ErrRepositoryUnavailableDueToUpgradeInProgress)
 
-	require.ErrorIs(t, w2.Flush(ctx), repo.ErrRepositoryUnavailableDueToUpgrageInProgress)
-	require.ErrorIs(t, w3.Flush(ctx), repo.ErrRepositoryUnavailableDueToUpgrageInProgress)
-	require.ErrorIs(t, lw.Flush(ctx), repo.ErrRepositoryUnavailableDueToUpgrageInProgress)
+	require.ErrorIs(t, w2.Flush(ctx), repo.ErrRepositoryUnavailableDueToUpgradeInProgress)
+	require.ErrorIs(t, w3.Flush(ctx), repo.ErrRepositoryUnavailableDueToUpgradeInProgress)
+	require.ErrorIs(t, lw.Flush(ctx), repo.ErrRepositoryUnavailableDueToUpgradeInProgress)
 }
 
 func writeObject(ctx context.Context, t *testing.T, rep repo.RepositoryWriter, data []byte, testCaseID string) {
diff --git a/repo/object/object_manager_test.go b/repo/object/object_manager_test.go
index 43eedaf6..70285031 100644
--- a/repo/object/object_manager_test.go
+++ b/repo/object/object_manager_test.go
@@ -18,7 +18,6 @@
 	"github.com/stretchr/testify/require"
 	"golang.org/x/sync/errgroup"
 
-	"github.com/kopia/kopia/internal/clock"
 	"github.com/kopia/kopia/internal/gather"
 	"github.com/kopia/kopia/internal/impossible"
 	"github.com/kopia/kopia/internal/testlogging"
@@ -273,8 +272,6 @@ func TestCheckpointing(t *testing.T) {
 }
 
 func TestObjectWriterRaceBetweenCheckpointAndResult(t *testing.T) {
-	rand.Seed(clock.Now().UnixNano())
-
 	ctx := testlogging.Context(t)
 	data := map[content.ID][]byte{}
 	fcm := &fakeContentManager{
diff --git a/tests/end_to_end_test/server_start_test.go b/tests/end_to_end_test/server_start_test.go
index 63473bab..d08dca42 100644
--- a/tests/end_to_end_test/server_start_test.go
+++ b/tests/end_to_end_test/server_start_test.go
@@ -20,6 +20,7 @@
 	"github.com/kopia/kopia/internal/testlogging"
 	"github.com/kopia/kopia/internal/testutil"
 	"github.com/kopia/kopia/internal/uitask"
+	"github.com/kopia/kopia/repo"
 	"github.com/kopia/kopia/repo/blob"
 	"github.com/kopia/kopia/repo/blob/filesystem"
 	"github.com/kopia/kopia/snapshot"
@@ -253,6 +254,9 @@ func TestServerStartAsyncRepoConnect(t *testing.T) {
 func TestServerCreateAndConnectViaAPI(t *testing.T) {
 	t.Parallel()
 
+	//nolint:tenv
+	os.Setenv("KOPIA_UPGRADE_LOCK_ENABLED", "true")
+
 	ctx := testlogging.Context(t)
 
 	runner := testenv.NewInProcRunner(t)
@@ -309,6 +313,9 @@ func TestServerCreateAndConnectViaAPI(t *testing.T) {
 		ConnectRepositoryRequest: serverapi.ConnectRepositoryRequest{
 			Password: "foofoo",
 			Storage:  connInfo,
+			ClientOptions: repo.ClientOptions{
+				PermissiveCacheLoading: true,
+			},
 		},
 	}); err != nil {
 		t.Fatalf("create error: %v", err)
diff --git a/tests/repository_stress_test/repository_stress_test.go b/tests/repository_stress_test/repository_stress_test.go
index 445343fb..02dff792 100644
--- a/tests/repository_stress_test/repository_stress_test.go
+++ b/tests/repository_stress_test/repository_stress_test.go
@@ -78,10 +78,6 @@ type StressOptions struct {
 
 const masterPassword = "foo-bar-baz-1234"
 
-func init() {
-	rand.Seed(clock.Now().UnixNano())
-}
-
 func TestStressRepositoryMixAll(t *testing.T) {
 	runStress(t, &StressOptions{
 		ConfigsPerRepository:      2,