Skip to content

Commit a40f399

Browse files
committed
Fix HA volume placement in scale command
When scaling apps with volumes, flyctl was hardcoding RequireUniqueZone=false, causing all volumes (and their attached machines) to be placed on the same host. This defeated the purpose of HA when scaling with volumes. The fix detects HA scenarios by checking if total machines in a region > 1: - Single machine (existingCount + delta == 1): RequireUniqueZone=false - HA scenario (existingCount + delta > 1): RequireUniqueZone=true This ensures volumes are distributed across different hosts, and since machines follow their volumes via RequireVolumeID, the machines will also be distributed for proper HA. Examples: - Scale 1→3: Existing=1, Delta=2, Total=3 → RequireUniqueZone=true ✅ - Deploy with 1: Existing=0, Delta=1, Total=1 → RequireUniqueZone=false ✅ - Scale 0→3: Existing=0, Delta=3, Total=3 → RequireUniqueZone=true ✅ Fixes: TestFlyScaleTo3/With_Volume where all 3 machines ended up on one host
1 parent 0ac8487 commit a40f399

File tree

2 files changed

+12
-5
lines changed

2 files changed

+12
-5
lines changed

internal/command/scale/count_machines.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -317,14 +317,15 @@ func computeActions(appName string, machines []*fly.Machine, expectedGroupCounts
317317
delete(mConfig.Env, "FLY_STANDBY_FOR")
318318

319319
for region, delta := range regionDiffs {
320+
existingMachinesInRegion := perRegionMachines[region]
320321
actions = append(actions, &planItem{
321322
GroupName: groupName,
322323
Region: region,
323324
Delta: delta,
324-
Machines: perRegionMachines[region],
325+
Machines: existingMachinesInRegion,
325326
LaunchMachineInput: &fly.LaunchMachineInput{Region: region, Config: mConfig, MinSecretsVersion: minvers},
326327
Volumes: defaults.PopAvailableVolumes(mConfig, region, delta),
327-
CreateVolumeRequest: defaults.CreateVolumeRequest(mConfig, region, delta),
328+
CreateVolumeRequest: defaults.CreateVolumeRequest(mConfig, region, delta, len(existingMachinesInRegion)),
328329
})
329330
}
330331
}
@@ -352,7 +353,7 @@ func computeActions(appName string, machines []*fly.Machine, expectedGroupCounts
352353
Delta: delta,
353354
LaunchMachineInput: &fly.LaunchMachineInput{Region: region, Config: mConfig, MinSecretsVersion: minvers},
354355
Volumes: defaults.PopAvailableVolumes(mConfig, region, delta),
355-
CreateVolumeRequest: defaults.CreateVolumeRequest(mConfig, region, delta),
356+
CreateVolumeRequest: defaults.CreateVolumeRequest(mConfig, region, delta, 0), // No existing machines for new groups
356357
})
357358
}
358359
}

internal/command/scale/machine_defaults.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,17 +118,23 @@ func (d *defaultValues) PopAvailableVolumes(mConfig *fly.MachineConfig, region s
118118
return availableVolumes
119119
}
120120

121-
func (d *defaultValues) CreateVolumeRequest(mConfig *fly.MachineConfig, region string, delta int) *fly.CreateVolumeRequest {
121+
func (d *defaultValues) CreateVolumeRequest(mConfig *fly.MachineConfig, region string, delta int, existingMachineCount int) *fly.CreateVolumeRequest {
122122
if len(mConfig.Mounts) == 0 || delta <= 0 {
123123
return nil
124124
}
125125
mount := mConfig.Mounts[0]
126+
127+
// Enable RequireUniqueZone for HA scenarios (when total machines in region > 1)
128+
// This ensures volumes (and their attached machines) are distributed across different hosts
129+
totalMachinesInRegion := existingMachineCount + delta
130+
requireUniqueZone := totalMachinesInRegion > 1
131+
126132
return &fly.CreateVolumeRequest{
127133
Name: mount.Name,
128134
Region: region,
129135
SizeGb: &mount.SizeGb,
130136
Encrypted: fly.Pointer(mount.Encrypted),
131-
RequireUniqueZone: fly.Pointer(false),
137+
RequireUniqueZone: fly.Pointer(requireUniqueZone),
132138
SnapshotID: d.snapshotID,
133139
ComputeRequirements: mConfig.Guest,
134140
ComputeImage: mConfig.Image,

0 commit comments

Comments
 (0)