1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
|
package topology
import (
"sync"
"testing"
"time"
"github.com/seaweedfs/seaweedfs/weed/storage/types"
)
func TestCapacityReservations_BasicOperations(t *testing.T) {
cr := newCapacityReservations()
diskType := types.HardDriveType
// Test initial state
if count := cr.getReservedCount(diskType); count != 0 {
t.Errorf("Expected 0 reserved count initially, got %d", count)
}
// Test add reservation
reservationId := cr.addReservation(diskType, 5)
if reservationId == "" {
t.Error("Expected non-empty reservation ID")
}
if count := cr.getReservedCount(diskType); count != 5 {
t.Errorf("Expected 5 reserved count, got %d", count)
}
// Test multiple reservations
cr.addReservation(diskType, 3)
if count := cr.getReservedCount(diskType); count != 8 {
t.Errorf("Expected 8 reserved count after second reservation, got %d", count)
}
// Test remove reservation
success := cr.removeReservation(reservationId)
if !success {
t.Error("Expected successful removal of existing reservation")
}
if count := cr.getReservedCount(diskType); count != 3 {
t.Errorf("Expected 3 reserved count after removal, got %d", count)
}
// Test remove non-existent reservation
success = cr.removeReservation("non-existent-id")
if success {
t.Error("Expected failure when removing non-existent reservation")
}
}
func TestCapacityReservations_ExpiredCleaning(t *testing.T) {
cr := newCapacityReservations()
diskType := types.HardDriveType
// Add reservations and manipulate their creation time
reservationId1 := cr.addReservation(diskType, 3)
reservationId2 := cr.addReservation(diskType, 2)
// Make one reservation "old"
cr.Lock()
if reservation, exists := cr.reservations[reservationId1]; exists {
reservation.createdAt = time.Now().Add(-10 * time.Minute) // 10 minutes ago
}
cr.Unlock()
// Clean expired reservations (5 minute expiration)
cr.cleanExpiredReservations(5 * time.Minute)
// Only the non-expired reservation should remain
if count := cr.getReservedCount(diskType); count != 2 {
t.Errorf("Expected 2 reserved count after cleaning, got %d", count)
}
// Verify the right reservation was kept
if !cr.removeReservation(reservationId2) {
t.Error("Expected recent reservation to still exist")
}
if cr.removeReservation(reservationId1) {
t.Error("Expected old reservation to be cleaned up")
}
}
func TestCapacityReservations_DifferentDiskTypes(t *testing.T) {
cr := newCapacityReservations()
// Add reservations for different disk types
cr.addReservation(types.HardDriveType, 5)
cr.addReservation(types.SsdType, 3)
// Check counts are separate
if count := cr.getReservedCount(types.HardDriveType); count != 5 {
t.Errorf("Expected 5 HDD reserved count, got %d", count)
}
if count := cr.getReservedCount(types.SsdType); count != 3 {
t.Errorf("Expected 3 SSD reserved count, got %d", count)
}
}
func TestNodeImpl_ReservationMethods(t *testing.T) {
// Create a test data node
dn := NewDataNode("test-node")
diskType := types.HardDriveType
// Set up some capacity
diskUsage := dn.diskUsages.getOrCreateDisk(diskType)
diskUsage.maxVolumeCount = 10
diskUsage.volumeCount = 5 // 5 volumes free initially
option := &VolumeGrowOption{DiskType: diskType}
// Test available space calculation
available := dn.AvailableSpaceFor(option)
if available != 5 {
t.Errorf("Expected 5 available slots, got %d", available)
}
availableForReservation := dn.AvailableSpaceForReservation(option)
if availableForReservation != 5 {
t.Errorf("Expected 5 available slots for reservation, got %d", availableForReservation)
}
// Test successful reservation
reservationId, success := dn.TryReserveCapacity(diskType, 3)
if !success {
t.Error("Expected successful reservation")
}
if reservationId == "" {
t.Error("Expected non-empty reservation ID")
}
// Available space should be reduced by reservations
availableForReservation = dn.AvailableSpaceForReservation(option)
if availableForReservation != 2 {
t.Errorf("Expected 2 available slots after reservation, got %d", availableForReservation)
}
// Base available space should remain unchanged
available = dn.AvailableSpaceFor(option)
if available != 5 {
t.Errorf("Expected base available to remain 5, got %d", available)
}
// Test reservation failure when insufficient capacity
_, success = dn.TryReserveCapacity(diskType, 3)
if success {
t.Error("Expected reservation failure due to insufficient capacity")
}
// Test release reservation
dn.ReleaseReservedCapacity(reservationId)
availableForReservation = dn.AvailableSpaceForReservation(option)
if availableForReservation != 5 {
t.Errorf("Expected 5 available slots after release, got %d", availableForReservation)
}
}
func TestNodeImpl_ConcurrentReservations(t *testing.T) {
dn := NewDataNode("test-node")
diskType := types.HardDriveType
// Set up capacity
diskUsage := dn.diskUsages.getOrCreateDisk(diskType)
diskUsage.maxVolumeCount = 10
diskUsage.volumeCount = 0 // 10 volumes free initially
// Test concurrent reservations using goroutines
var wg sync.WaitGroup
var reservationIds sync.Map
concurrentRequests := 10
wg.Add(concurrentRequests)
for i := 0; i < concurrentRequests; i++ {
go func(i int) {
defer wg.Done()
if reservationId, success := dn.TryReserveCapacity(diskType, 1); success {
reservationIds.Store(reservationId, true)
t.Logf("goroutine %d: Successfully reserved %s", i, reservationId)
} else {
t.Errorf("goroutine %d: Expected successful reservation", i)
}
}(i)
}
wg.Wait()
// Should have no more capacity
option := &VolumeGrowOption{DiskType: diskType}
if available := dn.AvailableSpaceForReservation(option); available != 0 {
t.Errorf("Expected 0 available slots after all reservations, got %d", available)
// Debug: check total reserved
reservedCount := dn.capacityReservations.getReservedCount(diskType)
t.Logf("Debug: Total reserved count: %d", reservedCount)
}
// Next reservation should fail
_, success := dn.TryReserveCapacity(diskType, 1)
if success {
t.Error("Expected reservation failure when at capacity")
}
// Release all reservations
reservationIds.Range(func(key, value interface{}) bool {
dn.ReleaseReservedCapacity(key.(string))
return true
})
// Should have full capacity back
if available := dn.AvailableSpaceForReservation(option); available != 10 {
t.Errorf("Expected 10 available slots after releasing all, got %d", available)
}
}
|