github.com/siglens/siglens@v0.0.0-20240328180423-f7ce9ae441ed/pkg/segment/memory/limit/memorylimit.go (about) 1 /* 2 Copyright 2023. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package limit 18 19 import ( 20 "errors" 21 "sync/atomic" 22 "time" 23 24 "github.com/siglens/siglens/pkg/config" 25 "github.com/siglens/siglens/pkg/segment/memory" 26 "github.com/siglens/siglens/pkg/segment/query/metadata" 27 "github.com/siglens/siglens/pkg/segment/structs" 28 "github.com/siglens/siglens/pkg/segment/utils" 29 "github.com/siglens/siglens/pkg/segment/writer" 30 log "github.com/sirupsen/logrus" 31 ) 32 33 const MINUTES_UPDATE_METADATA_MEM_ALLOC = 1 34 35 var LOG_GLOBAL_MEM_FREQUENCY = 5 36 37 func InitMemoryLimiter() { 38 totalAvailableSizeBytes := config.GetTotalMemoryAvailable() 39 log.Infof("InitQueryNode: Total available memory %+v MB", utils.ConvertUintBytesToMB(totalAvailableSizeBytes)) 40 41 maxBlockMetaInMemory := uint64(0) 42 maxSearchAvailableSize := uint64(0) 43 maxBlockMicroRuntime := uint64(0) 44 maxSsmInMemory := uint64(0) 45 metricsInMemory := uint64(0) 46 47 maxSearchAvailableSize = uint64(float64(totalAvailableSizeBytes) * utils.RAW_SEARCH_MEM_PERCENT / 100) 48 maxBlockMicroRuntime = uint64(float64(totalAvailableSizeBytes) * utils.MICRO_IDX_CHECK_MEM_PERCENT / 100) 49 maxBlockMetaInMemory = uint64(float64(totalAvailableSizeBytes) * utils.MICRO_IDX_MEM_PERCENT / 100) 50 maxSsmInMemory = uint64(float64(totalAvailableSizeBytes) * utils.SSM_MEM_PERCENT / 100) 51 metricsInMemory = uint64(float64(totalAvailableSizeBytes) * utils.METRICS_MEMORY_MEM_PERCENT / 100) 52 53 if config.IsDebugMode() { 54 LOG_GLOBAL_MEM_FREQUENCY = 1 55 } 56 57 // Total available memory should not include block runtime so rebalancing is still accurate 58 totalAvailableSizeBytes = totalAvailableSizeBytes - maxBlockMicroRuntime 59 memory.GlobalMemoryTracker = &structs.MemoryTracker{ 60 TotalAllocatableBytes: totalAvailableSizeBytes, 61 62 CmiInMemoryAllocatedBytes: maxBlockMetaInMemory, 63 CmiRuntimeAllocatedBytes: maxBlockMicroRuntime, 64 65 SegSearchRequestedBytes: maxSearchAvailableSize, 66 67 SegWriterUsageBytes: 0, 68 SegStoreSummary: metadata.GlobalSegStoreSummary, 69 SsmInMemoryAllocatedBytes: maxSsmInMemory, 70 71 MetricsSegmentMaxSize: metricsInMemory, 72 } 73 74 metadata.InitBlockMetaCheckLimiter(int64(maxBlockMicroRuntime)) 75 go rebalanceMemoryAllocationLoop() 76 } 77 78 func printMemoryManagerSummary() { 79 numLoadedUnrotated, totalUnrotated := writer.GetUnrotatedMetadataInfo() 80 unrotaedSize := writer.GetSizeOfUnrotatedMetadata() 81 log.Infof("GlobalMemoryTracker: Total amount of memory available is %+v MB", utils.ConvertUintBytesToMB(memory.GlobalMemoryTracker.TotalAllocatableBytes)) 82 log.Infof("GlobalMemoryTracker: AllSegReadStores has %v total segment files across %v tables. Microindices have been allocated %+v MB", 83 memory.GlobalMemoryTracker.SegStoreSummary.TotalSegmentCount, memory.GlobalMemoryTracker.SegStoreSummary.TotalTableCount, utils.ConvertUintBytesToMB(memory.GlobalMemoryTracker.CmiInMemoryAllocatedBytes)) 84 85 log.Infof("GlobalMemoryTracker: AllSegReadStores has %v CMI entries in memory. This accounts for %v MB", 86 memory.GlobalMemoryTracker.SegStoreSummary.InMemoryCMICount, 87 memory.GlobalMemoryTracker.SegStoreSummary.InMemoryBlockMicroIndexSizeMB) 88 89 log.Infof("GlobalMemoryTracker: AllSegReadStores %v SSM entries in memory. This accounts for %v MB", 90 memory.GlobalMemoryTracker.SegStoreSummary.InMemorySearchMetadataCount, 91 memory.GlobalMemoryTracker.SegStoreSummary.InMemorySsmSizeMB) 92 93 log.Infof("GlobalMemoryTracker: MetricsMetadata has %v segments in memory. Out of which %v segment have SSMs loaded. This accounts for %v MB", 94 memory.GlobalMemoryTracker.SegStoreSummary.TotalMetricsSegmentCount, 95 memory.GlobalMemoryTracker.SegStoreSummary.InMemoryMetricsSearchMetadataCount, 96 memory.GlobalMemoryTracker.SegStoreSummary.InMemoryMetricsBSumSizeMB) 97 98 log.Infof("GlobalMemoryTracker: Unrotated metadata has %v total segKeys. %+v have loaded metadata in memory. This accounts for %v MB", 99 totalUnrotated, numLoadedUnrotated, utils.ConvertUintBytesToMB(unrotaedSize)) 100 log.Infof("GlobalMemoryTracker: SegSearch has been allocated %v MB.", utils.ConvertUintBytesToMB(memory.GlobalMemoryTracker.SegSearchRequestedBytes)) 101 log.Infof("GlobalMemoryTracker: SegWriter has been allocated %v MB. MetricsWriter has been allocated %v MB.", 102 utils.ConvertUintBytesToMB(memory.GlobalMemoryTracker.SegWriterUsageBytes), utils.ConvertUintBytesToMB(memory.GetAvailableMetricsIngestMemory())) 103 } 104 105 func rebalanceMemoryAllocationLoop() { 106 count := 0 107 for { 108 rebalanceMemoryAllocation() 109 if count%LOG_GLOBAL_MEM_FREQUENCY == 0 { 110 printMemoryManagerSummary() 111 } 112 count++ 113 count = count % LOG_GLOBAL_MEM_FREQUENCY 114 time.Sleep(MINUTES_UPDATE_METADATA_MEM_ALLOC * time.Minute) 115 } 116 } 117 118 /* 119 Main function that rebalances all memory limits with the following logic 120 121 1. Get memory that we can allocate / move around 122 - memoryAvailable = TotalAvailableBytes - size of writer segstores 123 124 2. Allocate memory for segsearch. This will be the max memory of any single segment raw search we have seen 125 3. From available memory, use percentages to get max size of metadata 126 4. First, use as much metadata size as possible for unrotated data 127 - if unrotated data is bigger than max metadata size, then use all metadata memory for unrotated data 128 - when we remove unrotated data, we currently have no way to add it back so we will raw search the entire file 129 130 5. After allocating for unrotated data, use remaining metadata size for rotated data 131 - set global var & rebalance in metadata package 132 */ 133 func rebalanceMemoryAllocation() { 134 rawWriterSize := writer.GetInMemorySize() 135 var memoryAvailable uint64 136 if rawWriterSize > memory.GlobalMemoryTracker.TotalAllocatableBytes { 137 memoryAvailable = 0 138 } else { 139 memoryAvailable = memory.GlobalMemoryTracker.TotalAllocatableBytes - rawWriterSize 140 } 141 if memoryAvailable < memory.GlobalMemoryTracker.CmiRuntimeAllocatedBytes { 142 memoryAvailable = 0 143 } else { 144 memoryAvailable = memoryAvailable - memory.GlobalMemoryTracker.CmiRuntimeAllocatedBytes 145 } 146 147 totalSsmMemory := uint64(float64(memoryAvailable) * utils.SSM_MEM_PERCENT / 100) 148 metadata.RebalanceInMemorySsm(totalSsmMemory) 149 150 if memory.GlobalMemoryTracker.SegSearchRequestedBytes > memoryAvailable { 151 memoryAvailable = 0 152 memory.GlobalMemoryTracker.SegSearchRequestedBytes = memoryAvailable 153 } else { 154 memoryAvailable = memoryAvailable - memory.GlobalMemoryTracker.SegSearchRequestedBytes 155 } 156 157 totalMetadataMemory := uint64(float64(memoryAvailable) * utils.MICRO_IDX_MEM_PERCENT / 100) 158 unrotatedMetadataMemory := writer.GetSizeOfUnrotatedMetadata() 159 if unrotatedMetadataMemory >= totalMetadataMemory { 160 unrotatedMetadataMemory = writer.RebalanceUnrotatedMetadata(totalMetadataMemory) 161 } 162 163 var blockMetadataMemory uint64 164 if unrotatedMetadataMemory > totalMetadataMemory { 165 blockMetadataMemory = 0 166 } else { 167 blockMetadataMemory = totalMetadataMemory - unrotatedMetadataMemory 168 } 169 170 metadata.RebalanceInMemoryCmi(blockMetadataMemory) 171 memory.GlobalMemoryTracker.CmiInMemoryAllocatedBytes = blockMetadataMemory 172 memory.GlobalMemoryTracker.SegWriterUsageBytes = rawWriterSize 173 } 174 175 // Creates space for search by removing cmi if needed. Returns error if no space can be found. 176 // This function assumes only one segment is run at a time, and will not verify if >1 segment are run in parallel 177 func RequestSearchMemory(sLimit uint64) error { 178 179 if sLimit <= memory.GlobalMemoryTracker.SegSearchRequestedBytes { 180 return nil 181 } 182 atomic.StoreUint64(&memory.GlobalMemoryTracker.SegSearchRequestedBytes, sLimit) 183 rebalanceMemoryAllocation() 184 185 if sLimit <= memory.GlobalMemoryTracker.SegSearchRequestedBytes { 186 return nil 187 } 188 // If try to rebalance and SegSearchAllocatedBytes did not change, then we could not allocate what was requested 189 log.Infof("Unable to allocate memory for segsearch! Current breakdown:") 190 printMemoryManagerSummary() 191 return errors.New("failed to allocate resources for segment search") 192 }