You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							215 lines
						
					
					
						
							6.3 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							215 lines
						
					
					
						
							6.3 KiB
						
					
					
				
								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)
							 | 
						|
									}
							 | 
						|
								}
							 |