/** * Copyright (c) 2021 OceanBase * OceanBase CE is licensed under Mulan PubL v2. * You can use this software according to the terms and conditions of the Mulan PubL v2. * You may obtain a copy of Mulan PubL v2 at: * http://license.coscl.org.cn/MulanPubL-2.0 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. * See the Mulan PubL v2 for more details. */ #define USING_LOG_PREFIX SQL #include "sql/ob_query_exec_ctx_mgr.h" #include "share/ob_server_blacklist.h" #include "observer/ob_server.h" namespace oceanbase { using namespace lib; using namespace common; using namespace observer; namespace sql { ObQueryExecCtx* ObQueryExecCtx::alloc(ObSQLSessionInfo& session) { int ret = OB_SUCCESS; ObQueryExecCtx* query_ctx = nullptr; MemoryContext* entity = nullptr; if (OB_ISNULL(entity = session.get_ctx_mem_context())) { ObMemAttr mem_attr; mem_attr.tenant_id_ = session.get_effective_tenant_id(); mem_attr.label_ = "QueryExecCtx"; mem_attr.ctx_id_ = ObCtxIds::QUERY_EXEC_CTX_ID; lib::ContextParam param; param.set_mem_attr(mem_attr) .set_page_size(OB_MALLOC_NORMAL_BLOCK_SIZE) .set_ablock_size(lib::INTACT_MIDDLE_AOBJECT_SIZE); // Memory entity used by result set may be accessed across threads during // asynchronous execution, so we must use ROOT_CONTEXT if (OB_FAIL(ROOT_CONTEXT.CREATE_CONTEXT(entity, param))) { LOG_WARN("create entity failed", K(ret), K(mem_attr)); } else if (NULL == entity) { ret = OB_ERR_UNEXPECTED; LOG_WARN("NULL memory entity", K(ret)); } } else { // memory entity can not be used concurrently. session.set_ctx_mem_context(nullptr); } if (OB_SUCC(ret)) { if (OB_ISNULL(query_ctx = op_reclaim_alloc_args(ObQueryExecCtx, session, *entity))) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_WARN("allocate memory failed", K(ret), K(sizeof(ObQueryExecCtx))); } else { query_ctx->inc_ref_count(); query_ctx->get_result_set().get_exec_context().set_query_exec_ctx(query_ctx); query_ctx->set_cached_schema_guard_info(session.get_cached_schema_guard_info()); } if (OB_FAIL(ret) && query_ctx != nullptr) { ObQueryExecCtx::free(query_ctx); query_ctx = nullptr; } } return query_ctx; } void ObQueryExecCtx::free(ObQueryExecCtx* query_ctx) { if (query_ctx != nullptr) { int64_t ref_cnt = query_ctx->dec_ref_count(); if (0 == ref_cnt) { ObSQLSessionInfo& session = query_ctx->get_result_set().get_session(); MemoryContext& mem_context = query_ctx->mem_context_; query_ctx->cache_schema_info_->try_revert_schema_guard(); query_ctx->cache_schema_info_ = NULL; op_reclaim_free(query_ctx); query_ctx = nullptr; { ObSQLSessionInfo::LockGuard lock_guard(session.get_query_lock()); // save memory entity to session if none memory entity in session, otherwise // destroy it. if (nullptr == session.get_ctx_mem_context()) { mem_context.reset_remain_one_page(); session.set_ctx_mem_context(&mem_context); } else { DESTROY_CONTEXT(&mem_context); } } } else if (ref_cnt < 0) { LOG_ERROR("invalid ref cnt, maybe double free", K(ref_cnt)); } } } int ObRemoteTaskCtx::init(ObQueryExecCtx& query_ctx) { int ret = OB_SUCCESS; query_exec_ctx_ = &query_ctx; query_exec_ctx_->inc_ref_count(); new (&remote_plan_driver_) observer::ObRemotePlanDriver( GCTX, sql_ctx_, query_ctx.get_result_set().get_session(), retry_ctrl_, mppacket_sender_); if (OB_ISNULL(GCTX.session_mgr_)) { ret = OB_NOT_INIT; LOG_WARN("session mgr is null", K(ret)); } else if (OB_FAIL(GCTX.session_mgr_->inc_session_ref(&query_ctx.get_result_set().get_session()))) { LOG_WARN("inc session ref failed", K(ret)); } return ret; } void ObTaskExecCtxAlloc::free_value(ObRemoteTaskCtx* p) { if (p != nullptr) { op_reclaim_free(p); p = nullptr; } } int ObQueryExecCtxMgr::init() { int ret = OB_SUCCESS; if (OB_FAIL(task_exec_ctx_map_.init("QueryExecCtxMap"))) { LOG_WARN("init task exec ctx map failed", K(ret)); } else if (OB_FAIL(TG_CREATE(TGDefIDs::QueryExecCtxGC, tg_id_))) { LOG_ERROR("create tg failed", K(ret)); } else if (OB_FAIL(TG_SET_RUNNABLE_AND_START(tg_id_, *this))) { LOG_ERROR("start query exec ctx gc thread failed", K(ret)); } return ret; } void ObQueryExecCtxMgr::destroy() { TG_STOP(tg_id_); TG_WAIT(tg_id_); task_exec_ctx_map_.destroy(); } int ObQueryExecCtxMgr::create_task_exec_ctx( const ObExecutionID& execution_id, ObQueryExecCtx& query_ctx, ObRemoteTaskCtx*& task_ctx) { int ret = OB_SUCCESS; task_ctx = nullptr; if (OB_ISNULL(task_ctx = op_reclaim_alloc_args(ObRemoteTaskCtx))) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_WARN("allocate memory failed", K(ret), K(sizeof(ObQueryExecCtx)), K(execution_id)); } else if (OB_FAIL(task_ctx->init(query_ctx))) { LOG_WARN("init task ctx failed", K(ret)); } else if (OB_FAIL(task_exec_ctx_map_.insert_and_get(execution_id, task_ctx))) { LOG_WARN("insert and get query exec ctx failed", K(ret), K(execution_id)); } else { task_ctx->set_execution_id(execution_id); } if (OB_FAIL(ret) && task_ctx != nullptr) { op_reclaim_free(task_ctx); task_ctx = nullptr; } return ret; } int ObQueryExecCtxMgr::get_task_exec_ctx(const ObExecutionID& execution_id, ObRemoteTaskCtx*& task_ctx) { int ret = OB_SUCCESS; task_ctx = nullptr; if (OB_FAIL(task_exec_ctx_map_.get(execution_id, task_ctx))) { if (OB_ENTRY_NOT_EXIST != ret) { LOG_WARN("get query exec ctx map failed", K(ret), K(execution_id)); } else { LOG_INFO("query exec ctx not exists", K(execution_id)); } } return ret; } void ObQueryExecCtxMgr::revert_task_exec_ctx(ObRemoteTaskCtx* task_ctx) { task_exec_ctx_map_.revert(task_ctx); task_ctx = nullptr; } int ObQueryExecCtxMgr::drop_task_exec_ctx(const ObExecutionID& execution_id) { int ret = OB_SUCCESS; if (OB_FAIL(task_exec_ctx_map_.del(execution_id))) { LOG_WARN("delete query exec ctx map node failed", K(ret), K(execution_id)); } return ret; } void ObQueryExecCtxMgr::run1() { int ret = OB_SUCCESS; (void)prctl(PR_SET_NAME, "QueryExecCtxGC", 0, 0, 0); LOG_INFO("QueryExecCtx GC Thread start"); while (!has_set_stop()) { // GC per 100ms. USLEEP(GC_QUERY_CTX_INTERVAL_US); GCQueryExecCtx functor; if (OB_FAIL(task_exec_ctx_map_.remove_if(functor))) { // continue to next. LOG_WARN("remove query exec context failed", K(ret)); } } LOG_INFO("QueryExecCtx GC Thread end"); } bool GCQueryExecCtx::operator()(const ObExecutionID& execution_id, ObRemoteTaskCtx* task_ctx) { bool bret = false; int ret = OB_SUCCESS; if (task_ctx != nullptr && task_ctx->get_query_exec_ctx() != nullptr && task_ctx->is_running()) { ObQueryExecCtx* query_ctx = task_ctx->get_query_exec_ctx(); ObMySQLResultSet& result = query_ctx->get_result_set(); ObSQLSessionInfo& session = result.get_session(); ObSQLSessionInfo::LockGuard lock_guard(session.get_query_lock()); if (!task_ctx->is_exiting()) { ObExecContext& exec_ctx = result.get_exec_context(); if (OB_FAIL(exec_ctx.check_status())) { LOG_WARN("check query status failed", K(ret), K(execution_id), K(task_ctx->get_runner_svr())); } else if (SVR_BLACK_LIST.is_in_blacklist( share::ObCascadMember(task_ctx->get_runner_svr(), session.get_local_ob_org_cluster_id()))) { ret = OB_RPC_POST_ERROR; LOG_WARN("runner server in black list, post rpc error", K(ret), K(task_ctx->get_runner_svr())); } if (OB_SUCCESS != ret) { bret = true; // set exiting to true to avoid access from cb thread. task_ctx->set_is_exiting(true); result.set_errcode(ret); THIS_WORKER.disable_retry(); if (OB_FAIL(task_ctx->get_remote_plan_driver().response_result(result))) { LOG_WARN("response result failed", K(ret)); } bool need_retry = (RETRY_TYPE_NONE != task_ctx->get_retry_ctrl().get_retry_type()); if (!need_retry) { int fret = OB_SUCCESS; if (OB_SUCCESS != (fret = task_ctx->get_mppacket_sender().flush_buffer(true))) { LOG_WARN("flush the last buffer to client failed", K(ret), K(fret)); } } else { rpc::ObRequest* mysql_request = task_ctx->get_mysql_request(); if (OB_ISNULL(mysql_request)) { LOG_WARN("mysql request is null", K(ret), K(need_retry)); } else { OBSERVER.get_net_frame().get_deliver().deliver(*mysql_request); } } } } } return bret; } } // namespace sql } // namespace oceanbase