capacity_reservation_test.go 6.3 KB

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