Skip to content

Commit ea8ca34

Browse files
committed
Handle disk io
1 parent 2915dc1 commit ea8ca34

3 files changed

Lines changed: 62 additions & 0 deletions

File tree

lib/instances/manager.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ func (m *manager) ListInstanceAllocations(ctx context.Context) ([]resources.Inst
340340
VolumeOverlayBytes: volumeOverlayBytes,
341341
NetworkDownloadBps: inst.NetworkBandwidthDownload,
342342
NetworkUploadBps: inst.NetworkBandwidthUpload,
343+
DiskIOBps: inst.DiskIOBps,
343344
State: string(inst.State),
344345
VolumeBytes: volumeBytes,
345346
})

lib/resources/disk.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,42 @@ func parseDiskIOLimit(limit string) (int64, error) {
126126

127127
return int64(ds.Bytes()), nil
128128
}
129+
130+
// DiskIOResource implements Resource for disk I/O bandwidth tracking.
131+
type DiskIOResource struct {
132+
capacity int64 // bytes per second
133+
instanceLister InstanceLister
134+
}
135+
136+
// NewDiskIOResource creates a disk I/O resource with the given capacity.
137+
func NewDiskIOResource(capacity int64, instLister InstanceLister) *DiskIOResource {
138+
return &DiskIOResource{capacity: capacity, instanceLister: instLister}
139+
}
140+
141+
// Type returns the resource type.
142+
func (d *DiskIOResource) Type() ResourceType {
143+
return ResourceDiskIO
144+
}
145+
146+
// Capacity returns the total disk I/O capacity in bytes per second.
147+
func (d *DiskIOResource) Capacity() int64 {
148+
return d.capacity
149+
}
150+
151+
// Allocated returns total disk I/O allocated across all active instances.
152+
func (d *DiskIOResource) Allocated(ctx context.Context) (int64, error) {
153+
if d.instanceLister == nil {
154+
return 0, nil
155+
}
156+
instances, err := d.instanceLister.ListInstanceAllocations(ctx)
157+
if err != nil {
158+
return 0, err
159+
}
160+
var total int64
161+
for _, inst := range instances {
162+
if isActiveState(inst.State) {
163+
total += inst.DiskIOBps
164+
}
165+
}
166+
return total, nil
167+
}

lib/resources/resource.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const (
2121
ResourceMemory ResourceType = "memory"
2222
ResourceDisk ResourceType = "disk"
2323
ResourceNetwork ResourceType = "network"
24+
ResourceDiskIO ResourceType = "disk_io"
2425
)
2526

2627
// SourceType identifies how a resource capacity was determined.
@@ -100,6 +101,7 @@ type InstanceAllocation struct {
100101
VolumeOverlayBytes int64 // Sum of volume overlay sizes
101102
NetworkDownloadBps int64 // Download rate limit (external→VM)
102103
NetworkUploadBps int64 // Upload rate limit (VM→external)
104+
DiskIOBps int64 // Disk I/O rate limit (bytes/sec)
103105
State string // Only count running/paused/created instances
104106
VolumeBytes int64 // Sum of attached volume base sizes (for per-instance reporting)
105107
}
@@ -211,6 +213,10 @@ func (m *Manager) Initialize(ctx context.Context) error {
211213
}
212214
m.resources[ResourceNetwork] = net
213215

216+
// Discover disk I/O (reuses existing DiskIOCapacity method)
217+
diskIO := NewDiskIOResource(m.DiskIOCapacity(), m.instanceLister)
218+
m.resources[ResourceDiskIO] = diskIO
219+
214220
return nil
215221
}
216222

@@ -227,6 +233,8 @@ func (m *Manager) GetOversubRatio(rt ResourceType) float64 {
227233
ratio = m.cfg.OversubDisk
228234
case ResourceNetwork:
229235
ratio = m.cfg.OversubNetwork
236+
case ResourceDiskIO:
237+
ratio = m.cfg.OversubDiskIO
230238
default:
231239
return 1.0
232240
}
@@ -413,6 +421,20 @@ func (m *Manager) ValidateAllocation(ctx context.Context, vcpus int, memoryBytes
413421
}
414422
}
415423

424+
// Check Disk I/O
425+
if diskIOBps > 0 {
426+
status, err := m.GetStatus(ctx, ResourceDiskIO)
427+
if err != nil {
428+
return fmt.Errorf("check disk I/O capacity: %w", err)
429+
}
430+
if diskIOBps > status.Available {
431+
return fmt.Errorf("insufficient disk I/O: requested %s/s, but only %s/s available (currently allocated: %s/s, effective limit: %s/s with %.1fx oversubscription)",
432+
datasize.ByteSize(diskIOBps).HR(), datasize.ByteSize(status.Available).HR(),
433+
datasize.ByteSize(status.Allocated).HR(), datasize.ByteSize(status.EffectiveLimit).HR(),
434+
status.OversubRatio)
435+
}
436+
}
437+
416438
// Check GPU if needed
417439
if needsGPU {
418440
gpuStatus := GetGPUStatus()

0 commit comments

Comments
 (0)