Baymine opened a new pull request, #66513:
URL: https://github.com/apache/doris/pull/66513

   ### What problem does this PR solve?
   
   Issue Number: no issue
   
   Problem Summary:
   
   `Allocator::realloc` previously charged the full `new_size` to the memory
   tracker up front (`memory_check(new_size); consume_memory(new_size);`) and 
only
   released `old_size` after the underlying resize completed. This transiently
   double-counts the old region: for the window between 
`consume_memory(new_size)`
   and `release_memory(old_size)` the tracker reflects `old_size + new_size`. 
When
   a query sits near its `mem_limit` and a container (e.g. a hash table) 
doubles at
   the boundary, that transient peak trips the limit check and surfaces as a
   spurious `MEM_LIMIT_EXCEEDED` for queries that have no spill fallback, even
   though the steady-state footprint after the resize is only `new_size`.
   
   The fix tracks the net delta instead of the gross size:
   
   - Malloc / small-mmap `realloc` path: compute `grow = new_size > old_size` 
and
     `delta = |new_size - old_size|`. On grow, `memory_check(delta)` +
     `consume_memory(delta)` before the resize and `release_memory(delta)` on 
the
     failure path; on shrink, `release_memory(delta)` after success. These paths
     overwhelmingly resolve in place (tcmalloc/jemalloc grow the region without
     copying), so the net delta matches the real peak. If the allocator 
internally
     falls back to alloc+copy+free, the tracker is briefly under-counted by
     `old_size` for the duration of a single `memcpy`, then self-corrects.
   - mremap path: same net-delta pattern. Native Linux `mremap(MREMAP_MAYMOVE)`
     rewires page tables in place without duplicating physical pages, so the 
peak
     equals `new_size` and net-delta accounting is exact. The apple/freebsd
     `common/mremap.h` fallback (mmap+memcpy+munmap) peaks at `old+new` and is
     briefly under-counted here; production is Linux-only.
   - Big-alloc copy branch (`old < threshold < new`): drop the outer
     `release_memory(old_size)`. The inner `alloc(new_size)` and `free(buf,
     old_size)` already self-track new_size and old_size respectively, so the 
outer
     release would double-release. The peak here is genuinely `old+new` for the
     copy window, which consumers of this path already tolerate.
   
   Net effect: growing a buffer charges only `(new_size - old_size)` to the 
current
   tracker, eliminating the false positive at the limit boundary while keeping 
the
   tracker balanced end-to-end.
   
   ### Release note
   
   Fix spurious `MEM_LIMIT_EXCEEDED` errors that could occur when a buffer is
   reallocated (e.g. a hash table grows) while a query's memory usage is near 
its
   limit and no spill fallback is available.
   
   ### Check List (For Author)
   
   - Test: Unit Test
       - Added `AllocatorTrackerTest` (6 cases) in
         `be/test/runtime/memory/allocator_test.cpp` covering 
grow-charges-delta,
         shrink-releases-delta, no-trip-at-limit-boundary, same-size no-op,
         grow-then-shrink balance, and the big-alloc copy path. Ran
         `run-be-ut.sh --filter='*Allocator*'`: 10/10 tests PASSED, including 
the
         pre-existing `AllocatorTest.TestNormal`.
   - Behavior changed: Yes. `realloc` now charges the net size delta to the 
memory
     tracker instead of the gross `new_size`, so it no longer transiently
     double-counts `old_size` against `mem_limit`.
   - Does this need documentation: No
   
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to