Skip to content

Commit 49dbe08

Browse files
committed
fix: fill remaining replicas from ready spare slots
1 parent 03b17fd commit 49dbe08

9 files changed

Lines changed: 828 additions & 115 deletions

File tree

internal/worker/manager.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ func (m *Manager) reconcileUnavailableDataSets(ctx context.Context) {
161161
}
162162
for i := range bindings {
163163
binding := &bindings[i]
164-
if err := ensureReplicaRepairTask(ctx, m.repos, binding, m.uploadMaxRetries); err != nil {
164+
if _, err := ensureReplicaRepairTask(ctx, m.repos, binding, m.uploadMaxRetries); err != nil {
165165
m.logger.Error("failed to ensure unavailable data set repair", "dataSetID", binding.ID, "error", err)
166166
}
167167
afterID = binding.ID
@@ -370,7 +370,7 @@ func (m *Manager) reconcileIngressUpload(ctx context.Context, version model.Obje
370370
return
371371
}
372372
if binding.Status == model.StorageDataSetStatusUnavailable {
373-
if err := ensureReplicaRepairTask(ctx, m.repos, binding, m.uploadMaxRetries); err != nil {
373+
if _, err := ensureReplicaRepairTask(ctx, m.repos, binding, m.uploadMaxRetries); err != nil {
374374
m.logger.Error("failed to ensure recovered ingress repair", "dataSetID", binding.ID, "error", err)
375375
}
376376
}
@@ -458,7 +458,7 @@ func (m *Manager) reconcileReplicatingUpload(ctx context.Context, version model.
458458
continue
459459
}
460460
if binding != nil && binding.Status == model.StorageDataSetStatusUnavailable {
461-
if err := ensureReplicaRepairTask(ctx, m.repos, binding, m.uploadMaxRetries); err != nil {
461+
if _, err := ensureReplicaRepairTask(ctx, m.repos, binding, m.uploadMaxRetries); err != nil {
462462
m.logger.Error("failed to ensure recovered peer repair", "dataSetID", binding.ID, "error", err)
463463
}
464464
continue

internal/worker/uploader.go

Lines changed: 76 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -534,10 +534,19 @@ func (u *Uploader) processDurableUploadTask(
534534
u.ensureUploadDataSet(ctx, task, version, bucket, uploadID, copyIndex, logger)
535535
return
536536
}
537-
if err := u.ensureReplicaRepairTask(ctx, binding, task.MaxRetries); err != nil {
537+
if binding.Status == model.StorageDataSetStatusReady {
538+
u.processStagedTask(ctx, task, version, bucket, stage, logger)
539+
return
540+
}
541+
queued, err := ensureReplicaRepairTask(ctx, u.repos, binding, task.MaxRetries)
542+
if err != nil {
538543
u.handleTaskFailure(ctx, task, logger, "handoff durable upload repair", err)
539544
return
540545
}
546+
if !queued {
547+
u.waitForStorageDependency(ctx, task, logger, "Waiting for in-place replica recovery")
548+
return
549+
}
541550
completeWorkerTask(ctx, u.repos, task, "uploader", logger)
542551
}
543552

@@ -989,23 +998,40 @@ func (u *Uploader) ensureBucketProviderBindings(ctx context.Context, bucket *mod
989998
targetCopies = boundedTargetCopies(targetCopies)
990999
selected := make([]model.StorageDataSet, 0, targetCopies)
9911000
excluded := make([]sdktypes.BigInt, 0, len(bindings))
992-
for _, binding := range bindings {
1001+
inFlight := 0
1002+
for i := range bindings {
1003+
binding := bindings[i]
9931004
existing[binding.CopyIndex] = binding
9941005
excluded = append(excluded, binding.ProviderID.SDK())
995-
}
996-
missingIndexes := make([]int, 0, targetCopies)
997-
for copyIndex := range targetCopies {
998-
if binding, ok := existing[copyIndex]; ok {
999-
selected = append(selected, binding)
1000-
} else {
1001-
missingIndexes = append(missingIndexes, copyIndex)
1006+
if dataSetBindingCanOccupyUploadSlot(uploadID, &binding) {
1007+
if len(selected) < targetCopies {
1008+
selected = append(selected, binding)
1009+
}
1010+
continue
1011+
}
1012+
if dataSetBindingInFlightForOtherUpload(uploadID, &binding) {
1013+
inFlight++
10021014
}
10031015
}
1004-
if len(missingIndexes) == 0 {
1016+
if len(selected) >= targetCopies {
10051017
plan := newBucketBindingPlan(selected, uploadID)
10061018
plan.complete = true
10071019
return plan, nil
10081020
}
1021+
if inFlight > 0 {
1022+
return newBucketBindingPlan(selected, uploadID), nil
1023+
}
1024+
need := targetCopies - len(selected)
1025+
missingIndexes := make([]int, 0, need)
1026+
for copyIndex := 0; len(missingIndexes) < need && copyIndex < model.StorageCopiesMax; copyIndex++ {
1027+
if _, occupied := existing[copyIndex]; occupied {
1028+
continue
1029+
}
1030+
missingIndexes = append(missingIndexes, copyIndex)
1031+
}
1032+
if len(missingIndexes) == 0 {
1033+
return newBucketBindingPlan(selected, uploadID), nil
1034+
}
10091035
contexts, err := u.storage.CreateContexts(ctx, &storage.CreateContextsOptions{
10101036
Copies: len(missingIndexes),
10111037
ExcludeProviderIDs: excluded,
@@ -1313,23 +1339,6 @@ func (u *Uploader) ensureUploadDataSet(ctx context.Context, task *model.Task, ve
13131339
}
13141340
}
13151341
}
1316-
if durableObjectState(version.State) {
1317-
bindingID := binding.ID
1318-
binding, err = u.repos.Uploads.GetDataSetBindingByID(ctx, bindingID)
1319-
if err != nil || binding == nil {
1320-
if err == nil {
1321-
err = fmt.Errorf("dataset binding %d not found", bindingID)
1322-
}
1323-
u.handleTaskFailure(ctx, task, logger, "reload durable upload data set", err)
1324-
return
1325-
}
1326-
if err := u.ensureReplicaRepairTask(ctx, binding, task.MaxRetries); err != nil {
1327-
u.handleTaskFailure(ctx, task, logger, "handoff durable upload repair", err)
1328-
return
1329-
}
1330-
completeWorkerTask(ctx, u.repos, task, "uploader", logger)
1331-
return
1332-
}
13331342
nextStage := uploadStagePeerPull
13341343
if copyRow.TransferMethod == model.StorageCopyTransferMethodIngress {
13351344
nextStage = uploadStageIngressStore
@@ -2002,6 +2011,18 @@ func (u *Uploader) finishPeerCopy(ctx context.Context, task *model.Task, version
20022011
if !u.repairReadableBinding(ctx, task, version, uploadID, logger, "repair readable binding") {
20032012
return
20042013
}
2014+
ref := repository.ObjectVersionRef{ObjectID: version.ObjectID, VersionID: version.VersionID}
2015+
_, needsPreparation, err := u.scheduleRemainingPeerCopies(ctx, ref, version.BucketID, uploadID, task.MaxRetries)
2016+
if err != nil {
2017+
u.handleTaskFailure(ctx, task, logger, "schedule remaining upload copies", err)
2018+
return
2019+
}
2020+
if needsPreparation {
2021+
if err := u.enqueueRepairUploadForVersion(ctx, ref, task.MaxRetries, uploadID); err != nil {
2022+
u.handleTaskFailure(ctx, task, logger, "schedule upload preparation", err)
2023+
return
2024+
}
2025+
}
20052026
if _, _, err := u.repos.Uploads.FinalizeUploadIfTargetCopiesMet(ctx, u.finalizeUploadInput(uploadID)); err != nil {
20062027
u.handleTaskFailure(ctx, task, logger, "finalize upload", err)
20072028
return
@@ -2820,6 +2841,34 @@ func uploadCanUseDataSetBinding(uploadID int64, binding *model.StorageDataSet) b
28202841
dataSetBindingCanEnsureWrite(binding)
28212842
}
28222843

2844+
func dataSetBindingCanOccupyUploadSlot(uploadID int64, binding *model.StorageDataSet) bool {
2845+
if binding == nil {
2846+
return false
2847+
}
2848+
switch binding.Status {
2849+
case model.StorageDataSetStatusReady:
2850+
return true
2851+
case model.StorageDataSetStatusUnavailable:
2852+
return dataSetBindingEstablished(binding)
2853+
case model.StorageDataSetStatusPending, model.StorageDataSetStatusCreating, model.StorageDataSetStatusFailed:
2854+
return uploadCanUseDataSetBinding(uploadID, binding)
2855+
default:
2856+
return false
2857+
}
2858+
}
2859+
2860+
func dataSetBindingInFlightForOtherUpload(uploadID int64, binding *model.StorageDataSet) bool {
2861+
if binding == nil || dataSetBindingCanOccupyUploadSlot(uploadID, binding) {
2862+
return false
2863+
}
2864+
switch binding.Status {
2865+
case model.StorageDataSetStatusPending, model.StorageDataSetStatusCreating:
2866+
return true
2867+
default:
2868+
return false
2869+
}
2870+
}
2871+
28232872
func uploadTracksDataSetBinding(uploadID int64, binding *model.StorageDataSet) bool {
28242873
if binding == nil {
28252874
return false

internal/worker/uploader_replica_repair.go

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -60,35 +60,36 @@ func parseReplicaRepairPayload(task *model.Task) (replicaRepairPayload, error) {
6060
}
6161

6262
func (u *Uploader) ensureReplicaRepairTask(ctx context.Context, binding *model.StorageDataSet, maxRetries int) error {
63-
return ensureReplicaRepairTask(ctx, u.repos, binding, maxRetries)
63+
_, err := ensureReplicaRepairTask(ctx, u.repos, binding, maxRetries)
64+
return err
6465
}
6566

66-
func ensureReplicaRepairTask(ctx context.Context, repos *repository.Repositories, binding *model.StorageDataSet, maxRetries int) error {
67+
func ensureReplicaRepairTask(ctx context.Context, repos *repository.Repositories, binding *model.StorageDataSet, maxRetries int) (bool, error) {
6768
if binding == nil || binding.ID <= 0 || !dataSetBindingEstablished(binding) {
68-
return nil
69+
return false, nil
6970
}
7071
if binding.Status != model.StorageDataSetStatusUnavailable && binding.Status != model.StorageDataSetStatusReady {
71-
return nil
72+
return false, nil
7273
}
7374
copyRow, err := repos.Uploads.NextFinalizableCopyForDataSet(ctx, binding.ID)
7475
if err != nil {
75-
return fmt.Errorf("select replica finalization copy for data set %d: %w", binding.ID, err)
76+
return false, fmt.Errorf("select replica finalization copy for data set %d: %w", binding.ID, err)
7677
}
7778
if copyRow == nil {
7879
copyRow, err = repos.Uploads.NextIncompleteCopyForDataSet(ctx, binding.ID)
7980
if err != nil {
80-
return fmt.Errorf("select replica repair copy for data set %d: %w", binding.ID, err)
81+
return false, fmt.Errorf("select replica repair copy for data set %d: %w", binding.ID, err)
8182
}
8283
}
8384
if copyRow == nil {
84-
return nil
85+
return false, nil
8586
}
8687
upload, err := repos.Uploads.GetByID(ctx, copyRow.UploadID)
8788
if err != nil {
88-
return fmt.Errorf("load replica repair upload %d: %w", copyRow.UploadID, err)
89+
return false, fmt.Errorf("load replica repair upload %d: %w", copyRow.UploadID, err)
8990
}
9091
if upload == nil || upload.SourceVersionID == "" {
91-
return fmt.Errorf("load replica repair upload %d: %w", copyRow.UploadID, repository.ErrNotFound)
92+
return false, fmt.Errorf("load replica repair upload %d: %w", copyRow.UploadID, repository.ErrNotFound)
9293
}
9394
stage := uploadStageRepairReplica
9495
task := &model.Task{
@@ -104,9 +105,9 @@ func ensureReplicaRepairTask(ctx context.Context, repos *repository.Repositories
104105
ScheduledAt: time.Now(),
105106
}
106107
if _, err := repos.Tasks.EnsureRecurring(ctx, task); err != nil {
107-
return fmt.Errorf("ensure replica repair task for data set %d: %w", binding.ID, err)
108+
return false, fmt.Errorf("ensure replica repair task for data set %d: %w", binding.ID, err)
108109
}
109-
return nil
110+
return true, nil
110111
}
111112

112113
func (u *Uploader) processReplicaRepairTask(ctx context.Context, task *model.Task, logger *slog.Logger) {

0 commit comments

Comments
 (0)