This is an automated email from the ASF dual-hosted git repository.

yiguolei pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 58cb404661 [fix](memory) Allocator throws Exception instead of 
std::bad_alloc (#19285)
58cb404661 is described below

commit 58cb4046614c0ecc8fd452a3c1debf83fa202294
Author: Xinyi Zou <[email protected]>
AuthorDate: Fri May 5 18:01:48 2023 +0800

    [fix](memory) Allocator throws Exception instead of std::bad_alloc (#19285)
    
    W0505 01:31:25.840227 1727715 scanner_scheduler.cpp:340] Scan thread read 
VScanner failed: [MEM_LIMIT_EXCEEDED]PreCatch error code:11, [E11] Allocator 
sys memory check failed: Cannot alloc:16384, consuming tracker:<Orphan>, exec 
node:<>, process memory used 5.87 GB exceed limit 5.64 GB or sys mem available 
252.17 GB less than low water mark 1.60 GB, failed alloc size 16.00 KB.
        @     0x555c19e0cca8  doris::Exception::Exception()
        @     0x555c1c3e0c3f  Allocator<>::sys_memory_check()
        @     0x555c1c3e1052  Allocator<>::memory_check()
        @     0x555c19e0a645  Allocator<>::alloc()
        @     0x555c1c34508b  COWHelper<>::create<>()
        @     0x555c1e23f574  
doris::vectorized::ConvertThroughParsing<>::execute<>()
        @     0x555c1e23f209  
doris::vectorized::FunctionConvertFromString<>::execute_impl()
        @     0x555c1e23f4aa  
doris::vectorized::FunctionConvertFromString<>::execute_impl()
        @     0x555c1e15ac29  
doris::vectorized::PreparedFunctionImpl::execute_without_low_cardinality_columns()
        @     0x555c1e15ac56  doris::vectorized::PreparedFunctionImpl::execute()
        @     0x555c1e245276  
_ZNSt17_Function_handlerIFN5doris6StatusEPNS0_15FunctionContextERNS0_10vectorized5BlockERKSt6vectorImSaImEEmmEZNKS4_12FunctionCast14create_wrapperINS4_14DataTypeNumberIiEEEESt8functionISC_ERKSt10shared_ptrIKNS4_9IDataTypeEEPKT_bEUlS3_S6_SB_mmE_E9_M_invokeERKSt9_Any_dataOS3_S6_SB_OmSY_
        @     0x555c1e2a9341  
_ZZNK5doris10vectorized12FunctionCast23prepare_remove_nullableEPNS_15FunctionContextERKSt10shared_ptrIKNS0_9IDataTypeEES9_bENKUlS3_RNS0_5BlockERKSt6vectorImSaImEEmmE_clES3_SB_SG_mm
        @     0x555c1e2a8d42  
_ZNSt17_Function_handlerIFN5doris6StatusEPNS0_15FunctionContextERNS0_10vectorized5BlockERKSt6vectorImSaImEEmmEZNKS4_12FunctionCast23prepare_remove_nullableES3_RKSt10shared_ptrIKNS4_9IDataTypeEESJ_bEUlS3_S6_SB_mmE_E9_M_invokeERKSt9_Any_dataOS3_S6_SB_OmSQ_
        @     0x555c1e20e42b  
doris::vectorized::PreparedFunctionCast::execute_impl()
        @     0x555c1e15ac29  
doris::vectorized::PreparedFunctionImpl::execute_without_low_cardinality_columns()
        @     0x555c1e15ac56  doris::vectorized::PreparedFunctionImpl::execute()
        @     0x555c1d63e960  doris::vectorized::IFunctionBase::execute()
        @     0x555c1d628700  doris::vectorized::VCastExpr::execute()
        @     0x555c1d6163e5  doris::vectorized::VExprContext::execute()
        @     0x555c20a83fe1  
doris::vectorized::VFileScanner::_convert_to_output_block()
        @     0x555c20a809af  doris::vectorized::VFileScanner::_get_block_impl()
        @     0x555c209b9bc4  doris::vectorized::VScanner::get_block()
        @     0x555c209b1a50  
doris::vectorized::ScannerScheduler::_scanner_scan()
        @     0x555c209b2ac1  
_ZNSt17_Function_handlerIFvvEZZN5doris10vectorized16ScannerScheduler18_schedule_scannersEPNS2_14ScannerContextEENK3$_0clEvEUlvE1_E9_M_invokeERKSt9_Any_data
        @     0x555c1a8378cf  doris::ThreadPool::dispatch_thread()
        @     0x555c1a830fac  doris::Thread::supervise_thread()
        @     0x7f461faa117a  start_thread
        @     0x7f462033bdf3  __GI___clone
        @              (nil)  (unknown)
---
 be/src/common/config.h                         |  3 --
 be/src/runtime/exec_env_init.cpp               |  4 +--
 be/src/runtime/memory/mem_tracker.cpp          |  2 +-
 be/src/runtime/memory/thread_mem_tracker_mgr.h | 11 ------
 be/src/runtime/thread_context.h                | 46 +++++++++++++++-----------
 be/src/vec/common/allocator.cpp                | 21 ++++++------
 6 files changed, 38 insertions(+), 49 deletions(-)

diff --git a/be/src/common/config.h b/be/src/common/config.h
index 9186e5881e..330721b7e7 100644
--- a/be/src/common/config.h
+++ b/be/src/common/config.h
@@ -686,9 +686,6 @@ CONF_Int32(aws_log_level, "3");
 // the buffer size when read data from remote storage like s3
 CONF_mInt32(remote_storage_read_buffer_mb, "16");
 
-// Whether Hook TCmalloc new/delete, currently consume/release tls mem tracker 
in Hook.
-CONF_Bool(enable_tcmalloc_hook, "true");
-
 // Print more detailed logs, more detailed records, etc.
 CONF_mBool(memory_debug, "false");
 
diff --git a/be/src/runtime/exec_env_init.cpp b/be/src/runtime/exec_env_init.cpp
index 9655c62a40..8d72a4d54f 100644
--- a/be/src/runtime/exec_env_init.cpp
+++ b/be/src/runtime/exec_env_init.cpp
@@ -212,9 +212,7 @@ Status ExecEnv::_init_mem_env() {
     thread_context()->thread_mem_tracker_mgr->init();
 #if defined(USE_MEM_TRACKER) && !defined(__SANITIZE_ADDRESS__) && 
!defined(ADDRESS_SANITIZER) && \
         !defined(LEAK_SANITIZER) && !defined(THREAD_SANITIZER) && 
!defined(USE_JEMALLOC)
-    if (doris::config::enable_tcmalloc_hook) {
-        init_hook();
-    }
+    init_hook();
 #endif
 
     // 2. init buffer pool
diff --git a/be/src/runtime/memory/mem_tracker.cpp 
b/be/src/runtime/memory/mem_tracker.cpp
index 75e59385c2..bf3902821d 100644
--- a/be/src/runtime/memory/mem_tracker.cpp
+++ b/be/src/runtime/memory/mem_tracker.cpp
@@ -113,7 +113,7 @@ void 
MemTracker::make_group_snapshot(std::vector<MemTracker::Snapshot>* snapshot
                                      int64_t group_num, std::string 
parent_label) {
     std::lock_guard<std::mutex> l(mem_tracker_pool[group_num].group_lock);
     for (auto tracker : mem_tracker_pool[group_num].trackers) {
-        if (tracker->parent_label() == parent_label && tracker->consumption() 
!= 0) {
+        if (tracker->parent_label() == parent_label && 
tracker->peak_consumption() != 0) {
             snapshots->push_back(tracker->make_snapshot());
         }
     }
diff --git a/be/src/runtime/memory/thread_mem_tracker_mgr.h 
b/be/src/runtime/memory/thread_mem_tracker_mgr.h
index 1192bdb2a1..a0bef7a76b 100644
--- a/be/src/runtime/memory/thread_mem_tracker_mgr.h
+++ b/be/src/runtime/memory/thread_mem_tracker_mgr.h
@@ -89,9 +89,6 @@ public:
         return _limiter_tracker_raw;
     }
 
-    std::string exceed_mem_limit_msg() { return _exceed_mem_limit_msg; }
-    void save_exceed_mem_limit_msg(const std::string& msg) { 
_exceed_mem_limit_msg = msg; }
-    void clear_exceed_mem_limit_msg() { _exceed_mem_limit_msg = ""; }
     void disable_wait_gc() { _wait_gc = false; }
     bool wait_gc() { return _wait_gc; }
     void cancel_fragment(const std::string& exceed_msg);
@@ -119,7 +116,6 @@ private:
     int64_t _scope_mem = 0;
 
     std::string _failed_consume_msg = std::string();
-    std::string _exceed_mem_limit_msg = std::string();
     // If true, the Allocator will wait for the GC to free memory if it finds 
that the memory exceed limit.
     // A thread of query/load will only wait once during execution.
     bool _wait_gc = false;
@@ -191,13 +187,6 @@ inline bool ThreadMemTrackerMgr::flush_untracked_mem() {
     _stop_consume = true;
     init();
     DCHECK(_limiter_tracker_raw);
-    if (doris::MemTrackerLimiter::sys_mem_exceed_limit_check(_untracked_mem)) {
-        LOG(WARNING) << fmt::format(
-                "MemHook alloc:{} failed, not enough system memory, consuming 
tracker:<{}>, exec "
-                "node:<{}>, {}.",
-                _untracked_mem, _limiter_tracker_raw->label(), 
last_consumer_tracker(),
-                
doris::MemTrackerLimiter::process_limit_exceeded_errmsg_str(_untracked_mem));
-    }
 
     old_untracked_mem = _untracked_mem;
     if (_count_scope_mem) _scope_mem += _untracked_mem;
diff --git a/be/src/runtime/thread_context.h b/be/src/runtime/thread_context.h
index 983ad2fd0c..6c0fd3d996 100644
--- a/be/src/runtime/thread_context.h
+++ b/be/src/runtime/thread_context.h
@@ -35,6 +35,24 @@
 #include "runtime/threadlocal.h"
 #include "util/defer_op.h" // IWYU pragma: keep
 
+#define RETURN_IF_CATCH_EXCEPTION(stmt)                                        
              \
+    do {                                                                       
              \
+        try {                                                                  
              \
+            doris::enable_thread_catch_bad_alloc++;                            
              \
+            Defer defer {[&]() { doris::enable_thread_catch_bad_alloc--; }};   
              \
+            { stmt; }                                                          
              \
+        } catch (std::bad_alloc const& e) {                                    
              \
+            return Status::MemoryLimitExceeded(fmt::format("PreCatch {}", 
e.what()));        \
+        } catch (const doris::Exception& e) {                                  
              \
+            if (e.code() == doris::ErrorCode::MEM_ALLOC_FAILED) {              
              \
+                return Status::MemoryLimitExceeded(                            
              \
+                        fmt::format("PreCatch error code:{}, {}", e.code(), 
e.to_string())); \
+            } else {                                                           
              \
+                return Status::Error(e.code(), e.to_string());                 
              \
+            }                                                                  
              \
+        }                                                                      
              \
+    } while (0)
+
 // Used to observe the memory usage of the specified code segment
 #ifdef USE_MEM_TRACKER
 // Count a code segment memory (memory malloc - memory free) to int64_t
@@ -74,6 +92,13 @@
 #define SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(mem_tracker_limiter) (void)0
 #endif
 
+#define SKIP_MEMORY_CHECK(...)                  \
+    do {                                        \
+        doris::skip_memory_check++;             \
+        DEFER({ doris::skip_memory_check--; }); \
+        __VA_ARGS__;                            \
+    } while (0)
+
 namespace doris {
 
 class ThreadContext;
@@ -118,6 +143,7 @@ public:
 
 inline thread_local ThreadContextPtr thread_context_ptr;
 inline thread_local int enable_thread_catch_bad_alloc = 0;
+inline thread_local int skip_memory_check = 0;
 
 // To avoid performance problems caused by frequently calling 
`bthread_getspecific` to obtain bthread TLS
 // in tcmalloc hook, cache the key and value of bthread TLS in pthread TLS.
@@ -299,24 +325,6 @@ private:
     tracker->transfer_to(                               \
             size, 
doris::thread_context()->thread_mem_tracker_mgr->limiter_mem_tracker_raw())
 
-#define RETURN_IF_CATCH_EXCEPTION(stmt)                                        
                \
-    do {                                                                       
                \
-        try {                                                                  
                \
-            
doris::thread_context()->thread_mem_tracker_mgr->clear_exceed_mem_limit_msg();  
   \
-            doris::enable_thread_catch_bad_alloc++;                            
                \
-            Defer defer {[&]() { doris::enable_thread_catch_bad_alloc--; }};   
                \
-            { stmt; }                                                          
                \
-        } catch (std::bad_alloc const& e) {                                    
                \
-            doris::thread_context()->thread_mem_tracker()->print_log_usage(    
                \
-                    
doris::thread_context()->thread_mem_tracker_mgr->exceed_mem_limit_msg());  \
-            return Status::MemoryLimitExceeded(fmt::format(                    
                \
-                    "PreCatch {}, {}", e.what(),                               
                \
-                    
doris::thread_context()->thread_mem_tracker_mgr->exceed_mem_limit_msg())); \
-        } catch (const doris::Exception& e) {                                  
                \
-            return Status::Error(e.code(), e.to_string());                     
                \
-        }                                                                      
                \
-    } while (0)
-
 // Mem Hook to consume thread mem tracker
 // TODO: In the original design, the MemTracker consume method is called 
before the memory is allocated.
 // If the consume succeeds, the memory is actually allocated, otherwise an 
exception is thrown.
@@ -346,7 +354,5 @@ private:
 #define THREAD_MEM_TRACKER_TRANSFER_FROM(size, tracker) (void)0
 #define CONSUME_MEM_TRACKER(size) (void)0
 #define RELEASE_MEM_TRACKER(size) (void)0
-#define RETURN_IF_CATCH_EXCEPTION(stmt) \
-    { stmt; }
 #endif
 } // namespace doris
diff --git a/be/src/vec/common/allocator.cpp b/be/src/vec/common/allocator.cpp
index e6b163734b..fed24bf539 100644
--- a/be/src/vec/common/allocator.cpp
+++ b/be/src/vec/common/allocator.cpp
@@ -34,6 +34,7 @@
 
 template <bool clear_memory_, bool mmap_populate>
 void Allocator<clear_memory_, mmap_populate>::sys_memory_check(size_t size) 
const {
+    if (doris::skip_memory_check) return;
     if (doris::MemTrackerLimiter::sys_mem_exceed_limit_check(size)) {
         // Only thread attach query, and has not completely waited for 
thread_wait_gc_max_milliseconds,
         // will wait for gc, asynchronous cancel or throw bad::alloc.
@@ -58,13 +59,12 @@ void Allocator<clear_memory_, 
mmap_populate>::sys_memory_check(size_t size) cons
                         size, 
doris::thread_context()->thread_mem_tracker()->label(),
                         
doris::thread_context()->thread_mem_tracker_mgr->last_consumer_tracker(),
                         
doris::MemTrackerLimiter::process_limit_exceeded_errmsg_str(size));
+                doris::MemTrackerLimiter::print_log_process_usage(err_msg);
                 // If the external catch, throw bad::alloc first, let the 
query actively cancel. Otherwise asynchronous cancel.
                 if (!doris::enable_thread_catch_bad_alloc) {
                     
doris::thread_context()->thread_mem_tracker_mgr->cancel_fragment(err_msg);
                 } else {
-                    
doris::thread_context()->thread_mem_tracker_mgr->save_exceed_mem_limit_msg(
-                            err_msg);
-                    throw std::bad_alloc {};
+                    throw doris::Exception(doris::ErrorCode::MEM_ALLOC_FAILED, 
err_msg);
                 }
             }
         } else if (doris::enable_thread_catch_bad_alloc) {
@@ -74,14 +74,15 @@ void Allocator<clear_memory_, 
mmap_populate>::sys_memory_check(size_t size) cons
                     size, 
doris::thread_context()->thread_mem_tracker()->label(),
                     
doris::thread_context()->thread_mem_tracker_mgr->last_consumer_tracker(),
                     
doris::MemTrackerLimiter::process_limit_exceeded_errmsg_str(size));
-            
doris::thread_context()->thread_mem_tracker_mgr->save_exceed_mem_limit_msg(err_msg);
-            throw std::bad_alloc {};
+            doris::MemTrackerLimiter::print_log_process_usage(err_msg);
+            throw doris::Exception(doris::ErrorCode::MEM_ALLOC_FAILED, 
err_msg);
         }
     }
 }
 
 template <bool clear_memory_, bool mmap_populate>
 void Allocator<clear_memory_, mmap_populate>::memory_tracker_check(size_t 
size) const {
+    if (doris::skip_memory_check) return;
     auto st = doris::thread_context()->thread_mem_tracker()->check_limit(size);
     if (!st) {
         doris::thread_context()->thread_mem_tracker_mgr->disable_wait_gc();
@@ -90,13 +91,12 @@ void Allocator<clear_memory_, 
mmap_populate>::memory_tracker_check(size_t size)
                         st.to_string(),
                         
doris::thread_context()->thread_mem_tracker_mgr->last_consumer_tracker(),
                         "Allocator mem tracker check failed");
+        
doris::thread_context()->thread_mem_tracker()->print_log_usage(err_msg);
         // If the external catch, throw bad::alloc first, let the query 
actively cancel. Otherwise asynchronous cancel.
         if (!doris::enable_thread_catch_bad_alloc) {
             
doris::thread_context()->thread_mem_tracker_mgr->cancel_fragment(err_msg);
-            
doris::thread_context()->thread_mem_tracker()->print_log_usage(err_msg);
         } else {
-            
doris::thread_context()->thread_mem_tracker_mgr->save_exceed_mem_limit_msg(err_msg);
-            throw std::bad_alloc {};
+            throw doris::Exception(doris::ErrorCode::MEM_ALLOC_FAILED, 
err_msg);
         }
     }
 }
@@ -120,9 +120,8 @@ void Allocator<clear_memory_, 
mmap_populate>::release_memory(size_t size) const
 template <bool clear_memory_, bool mmap_populate>
 void Allocator<clear_memory_, mmap_populate>::throw_bad_alloc(const 
std::string& err) const {
     LOG(WARNING) << err;
-    if (!doris::enable_thread_catch_bad_alloc)
-        doris::MemTrackerLimiter::print_log_process_usage(err);
-    throw std::bad_alloc {};
+    doris::MemTrackerLimiter::print_log_process_usage(err);
+    throw doris::Exception(doris::ErrorCode::MEM_ALLOC_FAILED, err);
 }
 
 template class Allocator<true, true>;


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

Reply via email to