| 159 | |
| 160 | |
| 161 | void MemoryTracker::allocImpl(Int64 size, bool throw_if_memory_exceeded) |
| 162 | { |
| 163 | if (size < 0) |
| 164 | throw DB::Exception(DB::ErrorCodes::LOGICAL_ERROR, "Negative size ({}) is passed to MemoryTracker. It is a bug.", size); |
| 165 | |
| 166 | if (BlockerInThread::isBlocked(level)) |
| 167 | { |
| 168 | /// Since the BlockerInThread should respect the level, we should go to the next parent. |
| 169 | if (auto * loaded_next = parent.load(std::memory_order_relaxed)) |
| 170 | loaded_next->allocImpl(size, throw_if_memory_exceeded); |
| 171 | return; |
| 172 | } |
| 173 | |
| 174 | /** Using memory_order_relaxed means that if allocations are done simultaneously, |
| 175 | * we allow exception about memory limit exceeded to be thrown only on next allocation. |
| 176 | * So, we allow over-allocations. |
| 177 | */ |
| 178 | Int64 will_be = size + amount.fetch_add(size, std::memory_order_relaxed); |
| 179 | |
| 180 | auto metric_loaded = metric.load(std::memory_order_relaxed); |
| 181 | if (metric_loaded != CurrentMetrics::end()) |
| 182 | CurrentMetrics::add(metric_loaded, size); |
| 183 | |
| 184 | Int64 current_hard_limit = hard_limit.load(std::memory_order_relaxed); |
| 185 | Int64 current_profiler_limit = profiler_limit.load(std::memory_order_relaxed); |
| 186 | |
| 187 | /// Cap the limit to the total_memory_tracker, since it may include some drift |
| 188 | /// for user-level memory tracker. |
| 189 | /// |
| 190 | /// And since total_memory_tracker is reset to the process resident |
| 191 | /// memory peridically (in AsynchronousMetrics::update()), any limit can be |
| 192 | /// capped to it, to avoid possible drift. |
| 193 | if (unlikely(current_hard_limit |
| 194 | && will_be > current_hard_limit |
| 195 | && level == VariableContext::User)) |
| 196 | { |
| 197 | Int64 total_amount = total_memory_tracker.get(); |
| 198 | if (amount > total_amount) |
| 199 | { |
| 200 | set(total_amount); |
| 201 | will_be = size + total_amount; |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | #ifdef MEMORY_TRACKER_DEBUG_CHECKS |
| 206 | if (unlikely(memory_tracker_always_throw_logical_error_on_allocation)) |
| 207 | { |
| 208 | memory_tracker_always_throw_logical_error_on_allocation = false; |
| 209 | throw DB::Exception(DB::ErrorCodes::LOGICAL_ERROR, "Memory tracker: allocations not allowed."); |
| 210 | } |
| 211 | #endif |
| 212 | |
| 213 | std::bernoulli_distribution fault(fault_probability); |
| 214 | if (unlikely(fault_probability && fault(thread_local_rng)) && throw_if_memory_exceeded && memoryTrackerCanThrow(level, true)) |
| 215 | { |
| 216 | ProfileEvents::increment(ProfileEvents::QueryMemoryLimitExceeded); |
| 217 | amount.fetch_sub(size, std::memory_order_relaxed); |
| 218 |
no test coverage detected