// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. #include "olap/tablet_manager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "olap/base_compaction.h" #include "olap/cumulative_compaction.h" #include "olap/lru_cache.h" #include "olap/tablet_meta.h" #include "olap/tablet_meta_manager.h" #include "olap/push_handler.h" #include "olap/reader.h" #include "olap/schema_change.h" #include "olap/data_dir.h" #include "olap/utils.h" #include "olap/olap_common.h" #include "olap/rowset/column_data_writer.h" #include "olap/rowset/rowset_factory.h" #include "olap/rowset/rowset_id_generator.h" #include "util/time.h" #include "util/doris_metrics.h" #include "util/pretty_printer.h" using apache::thrift::ThriftDebugString; using boost::filesystem::canonical; using boost::filesystem::directory_iterator; using boost::filesystem::path; using boost::filesystem::recursive_directory_iterator; using std::back_inserter; using std::copy; using std::inserter; using std::list; using std::map; using std::nothrow; using std::pair; using std::priority_queue; using std::set; using std::set_difference; using std::string; using std::stringstream; using std::vector; namespace doris { bool _sort_tablet_by_creation_time(const TabletSharedPtr& a, const TabletSharedPtr& b) { return a->creation_time() < b->creation_time(); } TabletManager::TabletManager() : _tablet_stat_cache_update_time_ms(0), _available_storage_medium_type_count(0) { } OLAPStatus TabletManager::_add_tablet_unlock(TTabletId tablet_id, SchemaHash schema_hash, const TabletSharedPtr& tablet, bool update_meta, bool force) { OLAPStatus res = OLAP_SUCCESS; VLOG(3) << "begin to add tablet to TabletManager. " << "tablet_id=" << tablet_id << ", schema_hash=" << schema_hash << ", force=" << force; TabletSharedPtr table_item = nullptr; for (TabletSharedPtr item : _tablet_map[tablet_id].table_arr) { if (item->equal(tablet_id, schema_hash)) { table_item = item; break; } } if (table_item == nullptr) { VLOG(3) << "not find exist tablet just add it to map" << " tablet_id = " << tablet_id << " schema_hash = " << schema_hash; return _add_tablet_to_map(tablet_id, schema_hash, tablet, update_meta, false, false); } if (!force) { if (table_item->tablet_path() == tablet->tablet_path()) { LOG(WARNING) << "add the same tablet twice! tablet_id=" << tablet_id << " schema_hash=" << schema_hash; return OLAP_ERR_ENGINE_INSERT_EXISTS_TABLE; } if (table_item->data_dir() == tablet->data_dir()) { LOG(WARNING) << "add tablet with same data dir twice! tablet_id=" << tablet_id << " schema_hash=" << schema_hash; return OLAP_ERR_ENGINE_INSERT_EXISTS_TABLE; } } table_item->obtain_header_rdlock(); const RowsetSharedPtr old_rowset = table_item->rowset_with_max_version(); const RowsetSharedPtr new_rowset = tablet->rowset_with_max_version(); // if new tablet is empty, it is a newly created schema change tablet // the old tablet is dropped before add tablet. it should not exist old tablet if (new_rowset == nullptr) { table_item->release_header_lock(); // it seems useless to call unlock and return here. // it could prevent error when log level is changed in the future. LOG(FATAL) << "new tablet is empty and old tablet exists. it should not happen." << " tablet_id=" << tablet_id << " schema_hash=" << schema_hash; return OLAP_ERR_ENGINE_INSERT_EXISTS_TABLE; } int64_t old_time = old_rowset == nullptr ? -1 : old_rowset->creation_time(); int64_t new_time = new_rowset->creation_time(); int32_t old_version = old_rowset == nullptr ? -1 : old_rowset->end_version(); int32_t new_version = new_rowset->end_version(); table_item->release_header_lock(); /* * In restore process, we replace all origin files in tablet dir with * the downloaded snapshot files. Than we try to reload tablet header. * force == true means we forcibly replace the Tablet in _tablet_map * with the new one. But if we do so, the files in the tablet dir will be * dropped when the origin Tablet deconstruct. * So we set keep_files == true to not delete files when the * origin Tablet deconstruct. */ bool keep_files = force ? true : false; if (force || (new_version > old_version || (new_version == old_version && new_time > old_time))) { // check if new tablet's meta is in store and add new tablet's meta to meta store res = _add_tablet_to_map(tablet_id, schema_hash, tablet, update_meta, keep_files, true); } else { res = OLAP_ERR_ENGINE_INSERT_EXISTS_TABLE; } LOG(WARNING) << "add duplicated tablet. force=" << force << ", res=" << res << ", tablet_id=" << tablet_id << ", schema_hash=" << schema_hash << ", old_version=" << old_version << ", new_version=" << new_version << ", old_time=" << old_time << ", new_time=" << new_time << ", old_tablet_path=" << table_item->tablet_path() << ", new_tablet_path=" << tablet->tablet_path(); return res; } // add_tablet OLAPStatus TabletManager::_add_tablet_to_map(TTabletId tablet_id, SchemaHash schema_hash, const TabletSharedPtr& tablet, bool update_meta, bool keep_files, bool drop_old) { // check if new tablet's meta is in store and add new tablet's meta to meta store OLAPStatus res = OLAP_SUCCESS; if (update_meta) { // call tablet save meta in order to valid the meta res = tablet->save_meta(); if (res != OLAP_SUCCESS) { LOG(WARNING) << "failed to save new tablet's meta to meta store" << " tablet_id = " << tablet_id << " schema_hash = " << schema_hash; return res; } } if (drop_old) { // if the new tablet is fresher than current one // then delete current one and add new one res = _drop_tablet_unlock(tablet_id, schema_hash, keep_files); if (res != OLAP_SUCCESS) { LOG(WARNING) << "failed to drop old tablet when add new tablet" << " tablet_id = " << tablet_id << " schema_hash = " << schema_hash; return res; } } // Register tablet into StorageEngine, so that we can manage tablet from // the perspective of root path. // Example: unregister all tables when a bad disk found. res = tablet->register_tablet_into_dir(); if (res != OLAP_SUCCESS) { LOG(WARNING) << "fail to register tablet into StorageEngine. res=" << res << ", data_dir=" << tablet->data_dir()->path(); return res; } _tablet_map[tablet_id].table_arr.push_back(tablet); _tablet_map[tablet_id].table_arr.sort(_sort_tablet_by_creation_time); // add the tablet id to partition map _partition_tablet_map[tablet->partition_id()].insert(tablet->get_tablet_info()); VLOG(3) << "add tablet to map successfully" << " tablet_id = " << tablet_id << " schema_hash = " << schema_hash; return res; } bool TabletManager::check_tablet_id_exist(TTabletId tablet_id) { ReadLock rlock(&_tablet_map_lock); return _check_tablet_id_exist_unlock(tablet_id); } // check_tablet_id_exist bool TabletManager::_check_tablet_id_exist_unlock(TTabletId tablet_id) { bool is_exist = false; tablet_map_t::iterator it = _tablet_map.find(tablet_id); if (it != _tablet_map.end() && it->second.table_arr.size() != 0) { is_exist = true; } return is_exist; } // check_tablet_id_exist void TabletManager::clear() { _tablet_map.clear(); _shutdown_tablets.clear(); } // clear OLAPStatus TabletManager::create_tablet(const TCreateTabletReq& request, std::vector stores) { WriteLock wrlock(&_tablet_map_lock); LOG(INFO) << "begin to process create tablet. tablet=" << request.tablet_id << ", schema_hash=" << request.tablet_schema.schema_hash; OLAPStatus res = OLAP_SUCCESS; DorisMetrics::create_tablet_requests_total.increment(1); // Make sure create_tablet operation is idempotent: // return success if tablet with same tablet_id and schema_hash exist, // false if tablet with same tablet_id but different schema_hash exist // during alter, if the tablet(same tabletid and schema hash) already exist // then just return true, if tablet id with different schema hash exist, wait report // task to delete the tablet if (_check_tablet_id_exist_unlock(request.tablet_id)) { TabletSharedPtr tablet = _get_tablet_with_no_lock( request.tablet_id, request.tablet_schema.schema_hash); if (tablet != nullptr) { LOG(INFO) << "create tablet success for tablet already exist."; return OLAP_SUCCESS; } else { LOG(WARNING) << "tablet with different schema hash already exists."; return OLAP_ERR_CE_TABLET_ID_EXIST; } } TabletSharedPtr ref_tablet = nullptr; bool is_schema_change_tablet = false; // if the CreateTabletReq has base_tablet_id then it is a alter tablet request if (request.__isset.base_tablet_id && request.base_tablet_id > 0) { is_schema_change_tablet = true; ref_tablet = _get_tablet_with_no_lock(request.base_tablet_id, request.base_schema_hash); if (ref_tablet == nullptr) { LOG(WARNING) << "fail to create new tablet. new_tablet_id=" << request.tablet_id << ", new_schema_hash=" << request.tablet_schema.schema_hash << ", because could not find base tablet, base_tablet_id=" << request.base_tablet_id << ", base_schema_hash=" << request.base_schema_hash; return OLAP_ERR_TABLE_CREATE_META_ERROR; } // schema change should use the same data dir stores.clear(); stores.push_back(ref_tablet->data_dir()); } // set alter type to schema change. it is useless TabletSharedPtr tablet = _internal_create_tablet(AlterTabletType::SCHEMA_CHANGE, request, is_schema_change_tablet, ref_tablet, stores); if (tablet == nullptr) { res = OLAP_ERR_CE_CMD_PARAMS_ERROR; LOG(WARNING) << "fail to create tablet. res=" << res; } LOG(INFO) << "finish to process create tablet. res=" << res; return res; } // create_tablet TabletSharedPtr TabletManager::create_tablet(const AlterTabletType alter_type, const TCreateTabletReq& request, const bool is_schema_change_tablet, const TabletSharedPtr ref_tablet, std::vector data_dirs) { DCHECK(is_schema_change_tablet && ref_tablet != nullptr); WriteLock wrlock(&_tablet_map_lock); return _internal_create_tablet(alter_type, request, is_schema_change_tablet, ref_tablet, data_dirs); } TabletSharedPtr TabletManager::_internal_create_tablet(const AlterTabletType alter_type, const TCreateTabletReq& request, const bool is_schema_change_tablet, const TabletSharedPtr ref_tablet, std::vector data_dirs) { DCHECK((is_schema_change_tablet && ref_tablet != nullptr) || (!is_schema_change_tablet && ref_tablet == nullptr)); // check if the tablet with specified tablet id and schema hash already exists TabletSharedPtr checked_tablet = _get_tablet_with_no_lock(request.tablet_id, request.tablet_schema.schema_hash); if (checked_tablet != nullptr) { LOG(WARNING) << "failed to create tablet because tablet already exist." << " tablet id = " << request.tablet_id << " schema hash = " << request.tablet_schema.schema_hash; return nullptr; } bool is_tablet_added = false; TabletSharedPtr tablet = _create_tablet_meta_and_dir(request, is_schema_change_tablet, ref_tablet, data_dirs); if (tablet == nullptr) { return nullptr; } // TODO(yiguolei) // the following code is very difficult to understand because it mixed alter tablet v2 and alter tablet v1 // should remove alter tablet v1 code after v0.12 OLAPStatus res = OLAP_SUCCESS; do { res = tablet->init(); if (res != OLAP_SUCCESS) { LOG(WARNING) << "tablet init failed. tablet:" << tablet->full_name(); break; } if (!is_schema_change_tablet || (request.__isset.base_tablet_id && request.base_tablet_id > 0)) { // Create init version if this is not a restore mode replica and request.version is set // bool in_restore_mode = request.__isset.in_restore_mode && request.in_restore_mode; // if (!in_restore_mode && request.__isset.version) { // create inital rowset before add it to storage engine could omit many locks res = _create_inital_rowset(tablet, request); if (res != OLAP_SUCCESS) { LOG(WARNING) << "fail to create initial version for tablet. res=" << res; break; } } if (is_schema_change_tablet) { if (request.__isset.base_tablet_id && request.base_tablet_id > 0) { LOG(INFO) << "this request is for alter tablet request v2, so that not add alter task to tablet"; // if this is a new alter tablet, has to set its state to not ready // because schema change hanlder depends on it to check whether history data // convert finished tablet->set_tablet_state(TabletState::TABLET_NOTREADY); } else { // add alter task to new tablet if it is a new tablet during schema change tablet->add_alter_task(ref_tablet->tablet_id(), ref_tablet->schema_hash(), vector(), alter_type); } // 有可能出现以下2种特殊情况: // 1. 因为操作系统时间跳变,导致新生成的表的creation_time小于旧表的creation_time时间 // 2. 因为olap engine代码中统一以秒为单位,所以如果2个操作(比如create一个表, // 然后立即alter该表)之间的时间间隔小于1s,则alter得到的新表和旧表的creation_time会相同 // // 当出现以上2种情况时,为了能够区分alter得到的新表和旧表,这里把新表的creation_time设置为 // 旧表的creation_time加1 if (tablet->creation_time() <= ref_tablet->creation_time()) { LOG(WARNING) << "new tablet's creation time is less than or equal to old tablet" << "new_tablet_creation_time=" << tablet->creation_time() << ", ref_tablet_creation_time=" << ref_tablet->creation_time(); int64_t new_creation_time = ref_tablet->creation_time() + 1; tablet->set_creation_time(new_creation_time); } } // Add tablet to StorageEngine will make it visiable to user res = _add_tablet_unlock(request.tablet_id, request.tablet_schema.schema_hash, tablet, true, false); if (res != OLAP_SUCCESS) { LOG(WARNING) << "fail to add tablet to StorageEngine. res=" << res; break; } is_tablet_added = true; TabletSharedPtr tablet_ptr = _get_tablet_with_no_lock(request.tablet_id, request.tablet_schema.schema_hash); if (tablet_ptr == nullptr) { res = OLAP_ERR_TABLE_NOT_FOUND; LOG(WARNING) << "fail to get tablet. res=" << res; break; } } while (0); // should remove the pending path of tablet id no matter create tablet success or not tablet->data_dir()->remove_pending_ids(TABLET_ID_PREFIX + std::to_string(request.tablet_id)); // clear environment if (res != OLAP_SUCCESS) { DorisMetrics::create_tablet_requests_failed.increment(1); if (is_tablet_added) { OLAPStatus status = _drop_tablet_unlock( request.tablet_id, request.tablet_schema.schema_hash, false); if (status != OLAP_SUCCESS) { LOG(WARNING) << "fail to drop tablet when create tablet failed. res=" << res; } } else { tablet->delete_all_files(); TabletMetaManager::remove(tablet->data_dir(), request.tablet_id, request.tablet_schema.schema_hash); } return nullptr; } else { LOG(INFO) << "finish to process create tablet. res=" << res; return tablet; } } // create_tablet TabletSharedPtr TabletManager::_create_tablet_meta_and_dir( const TCreateTabletReq& request, const bool is_schema_change_tablet, const TabletSharedPtr ref_tablet, std::vector data_dirs) { TabletSharedPtr tablet; // Try to create tablet on each of all_available_root_path, util success DataDir* last_dir = nullptr; for (auto& data_dir : data_dirs) { if (last_dir != nullptr) { // if last dir != null, it means preivous create tablet retry failed last_dir->remove_pending_ids(TABLET_ID_PREFIX + std::to_string(request.tablet_id)); } last_dir = data_dir; TabletMetaSharedPtr tablet_meta; // if create meta faild, do not need to clean dir, because it is only in memory OLAPStatus res = _create_tablet_meta(request, data_dir, is_schema_change_tablet, ref_tablet, &tablet_meta); if (res != OLAP_SUCCESS) { LOG(WARNING) << "fail to create tablet meta. res=" << res << ", root=" << data_dir->path(); continue; } stringstream schema_hash_dir_stream; schema_hash_dir_stream << data_dir->path() << DATA_PREFIX << "/" << tablet_meta->shard_id() << "/" << request.tablet_id << "/" << request.tablet_schema.schema_hash; string schema_hash_dir = schema_hash_dir_stream.str(); boost::filesystem::path schema_hash_path(schema_hash_dir); boost::filesystem::path tablet_path = schema_hash_path.parent_path(); std::string tablet_dir = tablet_path.string(); // because the tablet is removed async, so that the dir may still exist // when be receive create tablet again. For example redo schema change if (check_dir_existed(schema_hash_dir)) { LOG(WARNING) << "skip this dir because tablet path exist, path="<< schema_hash_dir; continue; } else { data_dir->add_pending_ids(TABLET_ID_PREFIX + std::to_string(request.tablet_id)); res = create_dirs(schema_hash_dir); if (res != OLAP_SUCCESS) { LOG(WARNING) << "create dir fail. [res=" << res << " path:" << schema_hash_dir; continue; } } tablet = Tablet::create_tablet_from_meta(tablet_meta, data_dir); if (tablet == nullptr) { LOG(WARNING) << "fail to load tablet from tablet_meta. root_path:" << data_dir->path(); res = remove_all_dir(tablet_dir); if (res != OLAP_SUCCESS) { LOG(WARNING) << "remove tablet dir:" << tablet_dir; } continue; } break; } return tablet; } // Drop tablet specified, the main logical is as follows: // 1. tablet not in schema change: // drop specified tablet directly; // 2. tablet in schema change: // a. schema change not finished && dropped tablet is base : // base tablet cannot be dropped; // b. other cases: // drop specified tablet and clear schema change info. OLAPStatus TabletManager::drop_tablet( TTabletId tablet_id, SchemaHash schema_hash, bool keep_files) { WriteLock wlock(&_tablet_map_lock); return _drop_tablet_unlock(tablet_id, schema_hash, keep_files); } // drop_tablet // Drop tablet specified, the main logical is as follows: // 1. tablet not in schema change: // drop specified tablet directly; // 2. tablet in schema change: // a. schema change not finished && dropped tablet is base : // base tablet cannot be dropped; // b. other cases: // drop specified tablet and clear schema change info. OLAPStatus TabletManager::_drop_tablet_unlock( TTabletId tablet_id, SchemaHash schema_hash, bool keep_files) { LOG(INFO) << "begin to process drop tablet." << "tablet=" << tablet_id << ", schema_hash=" << schema_hash; DorisMetrics::drop_tablet_requests_total.increment(1); OLAPStatus res = OLAP_SUCCESS; // Get tablet which need to be droped TabletSharedPtr dropped_tablet = _get_tablet_with_no_lock(tablet_id, schema_hash); if (dropped_tablet == nullptr) { LOG(WARNING) << "tablet to drop does not exist already." << " tablet_id=" << tablet_id << ", schema_hash=" << schema_hash; return OLAP_SUCCESS; } // Try to get schema change info AlterTabletTaskSharedPtr alter_task = dropped_tablet->alter_task(); // Drop tablet directly when not in schema change if (alter_task == nullptr) { return _drop_tablet_directly_unlocked(tablet_id, schema_hash, keep_files); } AlterTabletState alter_state = alter_task->alter_state(); TTabletId related_tablet_id = alter_task->related_tablet_id(); TSchemaHash related_schema_hash = alter_task->related_schema_hash();; // Check tablet is in schema change or not, is base tablet or not bool is_schema_change_finished = (alter_state == ALTER_FINISHED || alter_state == ALTER_FAILED); bool is_drop_base_tablet = false; TabletSharedPtr related_tablet = _get_tablet_with_no_lock( related_tablet_id, related_schema_hash); if (related_tablet == nullptr) { LOG(WARNING) << "drop tablet directly when related tablet not found. " << " tablet_id=" << related_tablet_id << " schema_hash=" << related_schema_hash; return _drop_tablet_directly_unlocked(tablet_id, schema_hash, keep_files); } if (dropped_tablet->creation_time() < related_tablet->creation_time()) { is_drop_base_tablet = true; } if (is_drop_base_tablet && !is_schema_change_finished) { LOG(WARNING) << "base tablet in schema change cannot be droped. tablet=" << dropped_tablet->full_name(); return OLAP_ERR_PREVIOUS_SCHEMA_CHANGE_NOT_FINISHED; } // Drop specified tablet and clear schema change info // must first break the link and then drop the tablet // if drop tablet, then break link. the link maybe exists but the tablet // not exist when be restarts related_tablet->obtain_header_wrlock(); // should check the related tablet id in alter task is current tablet to be dropped // A related to B, BUT B related to C // if drop A, should not clear B's alter task AlterTabletTaskSharedPtr related_alter_task = related_tablet->alter_task(); if (related_alter_task != nullptr && related_alter_task->related_tablet_id() == tablet_id && related_alter_task->related_schema_hash() == schema_hash) { related_tablet->delete_alter_task(); res = related_tablet->save_meta(); if (res != OLAP_SUCCESS) { LOG(FATAL) << "fail to save tablet header. res=" << res << ", tablet=" << related_tablet->full_name(); } } related_tablet->release_header_lock(); res = _drop_tablet_directly_unlocked(tablet_id, schema_hash, keep_files); if (res != OLAP_SUCCESS) { LOG(WARNING) << "fail to drop tablet which in schema change. tablet=" << dropped_tablet->full_name(); return res; } LOG(INFO) << "finish to drop tablet. res=" << res; return res; } // drop_tablet_unlock OLAPStatus TabletManager::drop_tablets_on_error_root_path( const vector& tablet_info_vec) { OLAPStatus res = OLAP_SUCCESS; WriteLock wlock(&_tablet_map_lock); for (const TabletInfo& tablet_info : tablet_info_vec) { TTabletId tablet_id = tablet_info.tablet_id; TSchemaHash schema_hash = tablet_info.schema_hash; VLOG(3) << "drop_tablet begin. tablet_id=" << tablet_id << ", schema_hash=" << schema_hash; TabletSharedPtr dropped_tablet = _get_tablet_with_no_lock(tablet_id, schema_hash); if (dropped_tablet == nullptr) { LOG(WARNING) << "dropping tablet not exist. " << " tablet=" << tablet_id << " schema_hash=" << schema_hash; continue; } else { for (list::iterator it = _tablet_map[tablet_id].table_arr.begin(); it != _tablet_map[tablet_id].table_arr.end();) { if ((*it)->equal(tablet_id, schema_hash)) { it = _tablet_map[tablet_id].table_arr.erase(it); _partition_tablet_map[(*it)->partition_id()].erase((*it)->get_tablet_info()); if (_partition_tablet_map[(*it)->partition_id()].empty()) { _partition_tablet_map.erase((*it)->partition_id()); } } else { ++it; } } } } return res; } // drop_tablets_on_error_root_path TabletSharedPtr TabletManager::get_tablet(TTabletId tablet_id, SchemaHash schema_hash, bool include_deleted, std::string* err) { ReadLock rlock(&_tablet_map_lock); return _get_tablet(tablet_id, schema_hash, include_deleted, err); } // get_tablet TabletSharedPtr TabletManager::_get_tablet(TTabletId tablet_id, SchemaHash schema_hash, bool include_deleted, std::string* err) { TabletSharedPtr tablet; tablet = _get_tablet_with_no_lock(tablet_id, schema_hash); if (tablet == nullptr && include_deleted) { for (auto& deleted_tablet : _shutdown_tablets) { CHECK(deleted_tablet != nullptr) << "deleted tablet in nullptr"; if (deleted_tablet->tablet_id() == tablet_id && deleted_tablet->schema_hash() == schema_hash) { tablet = deleted_tablet; break; } } } if (tablet != nullptr) { if (!tablet->is_used()) { LOG(WARNING) << "tablet cannot be used. tablet=" << tablet_id; if (err != nullptr) { *err = "tablet cannot be used"; } tablet.reset(); } } else if (err != nullptr) { *err = "tablet does not exist"; } return tablet; } // get_tablet TabletSharedPtr TabletManager::get_tablet(TTabletId tablet_id, SchemaHash schema_hash, TabletUid tablet_uid, bool include_deleted, std::string* err) { ReadLock rlock(&_tablet_map_lock); TabletSharedPtr tablet = _get_tablet(tablet_id, schema_hash, include_deleted, err); if (tablet != nullptr && tablet->tablet_uid() == tablet_uid) { return tablet; } return nullptr; } // get_tablet bool TabletManager::get_tablet_id_and_schema_hash_from_path( const std::string& path, TTabletId* tablet_id, TSchemaHash* schema_hash) { static re2::RE2 normal_re("/data/\\d+/(\\d+)/(\\d+)($|/)"); if (RE2::PartialMatch(path, normal_re, tablet_id, schema_hash)) { return true; } // If we can't match normal path pattern, this may be a path which is a empty tablet // directory. Use this pattern to match empty tablet directory. In this case schema_hash // will be set to zero. static re2::RE2 empty_tablet_re("/data/\\d+/(\\d+)($|/$)"); if (!RE2::PartialMatch(path, empty_tablet_re, tablet_id)) { return false; } *schema_hash = 0; return true; } bool TabletManager::get_rowset_id_from_path(const std::string& path, RowsetId* rowset_id) { static re2::RE2 re("/data/\\d+/\\d+/\\d+/([A-Fa-f0-9]+)_.*"); std::string id_str; bool ret = RE2::PartialMatch(path, re, &id_str); if (ret) { rowset_id->init(id_str); return true; } return false; } void TabletManager::get_tablet_stat(TTabletStatResult& result) { VLOG(3) << "begin to get all tablet stat."; // get current time int64_t current_time = UnixMillis(); // update cache if too old { std::lock_guard l(_tablet_stat_mutex); if (current_time - _tablet_stat_cache_update_time_ms > config::tablet_stat_cache_update_interval_second * 1000) { VLOG(3) << "update tablet stat."; _build_tablet_stat(); } } result.__set_tablets_stats(_tablet_stat_cache); } // get_tablet_stat TabletSharedPtr TabletManager::find_best_tablet_to_compaction( CompactionType compaction_type, DataDir* data_dir) { ReadLock tablet_map_rdlock(&_tablet_map_lock); uint32_t highest_score = 0; TabletSharedPtr best_tablet; int64_t now = UnixMillis(); for (tablet_map_t::value_type& table_ins : _tablet_map){ for (TabletSharedPtr& table_ptr : table_ins.second.table_arr) { AlterTabletTaskSharedPtr cur_alter_task = table_ptr->alter_task(); if (cur_alter_task != nullptr && cur_alter_task->alter_state() != ALTER_FINISHED && cur_alter_task->alter_state() != ALTER_FAILED) { TabletSharedPtr related_tablet = _get_tablet_with_no_lock(cur_alter_task->related_tablet_id(), cur_alter_task->related_schema_hash()); if (related_tablet != nullptr && table_ptr->creation_time() > related_tablet->creation_time()) { // it means cur tablet is a new tablet during schema change or rollup, skip compaction continue; } } // if tablet is not ready, it maybe a new tablet under schema change, not do compaction if (table_ptr->tablet_state() == TABLET_NOTREADY) { continue; } if (table_ptr->data_dir()->path_hash() != data_dir->path_hash() || !table_ptr->is_used() || !table_ptr->init_succeeded() || !table_ptr->can_do_compaction()) { continue; } if (now - table_ptr->last_compaction_failure_time() <= config::min_compaction_failure_interval_sec * 1000) { continue; } if (compaction_type == CompactionType::CUMULATIVE_COMPACTION) { MutexLock lock(table_ptr->get_cumulative_lock(), TRY_LOCK); if (!lock.own_lock()) { continue; } } if (compaction_type == CompactionType::BASE_COMPACTION) { MutexLock lock(table_ptr->get_base_lock(), TRY_LOCK); if (!lock.own_lock()) { continue; } } ReadLock rdlock(table_ptr->get_header_lock_ptr()); uint32_t table_score = 0; if (compaction_type == CompactionType::BASE_COMPACTION) { table_score = table_ptr->calc_base_compaction_score(); } else if (compaction_type == CompactionType::CUMULATIVE_COMPACTION) { table_score = table_ptr->calc_cumulative_compaction_score(); } if (table_score > highest_score) { highest_score = table_score; best_tablet = table_ptr; } } } if (best_tablet != nullptr) { LOG(INFO) << "find best tablet to do compaction." << " type: " << (compaction_type == CompactionType::CUMULATIVE_COMPACTION ? "cumulative" : "base") << ", tablet id: " << best_tablet->tablet_id() << ", score: " << highest_score; } return best_tablet; } OLAPStatus TabletManager::load_tablet_from_meta(DataDir* data_dir, TTabletId tablet_id, TSchemaHash schema_hash, const std::string& meta_binary, bool update_meta, bool force) { WriteLock wlock(&_tablet_map_lock); TabletMetaSharedPtr tablet_meta(new TabletMeta()); OLAPStatus status = tablet_meta->deserialize(meta_binary); if (status != OLAP_SUCCESS) { LOG(WARNING) << "parse meta_binary string failed for tablet_id:" << tablet_id << ", schema_hash:" << schema_hash; return OLAP_ERR_HEADER_PB_PARSE_FAILED; } // check if tablet meta is valid if (tablet_meta->tablet_id() != tablet_id || tablet_meta->schema_hash() != schema_hash) { LOG(WARNING) << "tablet meta load from meta is invalid" << " input tablet id=" << tablet_id << " input tablet schema_hash=" << schema_hash << " meta tablet=" << tablet_meta->full_name(); return OLAP_ERR_HEADER_PB_PARSE_FAILED; } if (tablet_meta->tablet_uid().hi == 0 && tablet_meta->tablet_uid().lo == 0) { LOG(WARNING) << "not load this tablet because uid == 0" << " tablet=" << tablet_meta->full_name(); return OLAP_ERR_HEADER_PB_PARSE_FAILED; } // init must be called TabletSharedPtr tablet = Tablet::create_tablet_from_meta(tablet_meta, data_dir); if (tablet == nullptr) { LOG(WARNING) << "fail to new tablet. tablet_id=" << tablet_id << ", schema_hash:" << schema_hash; return OLAP_ERR_TABLE_CREATE_FROM_HEADER_ERROR; } if (tablet_meta->tablet_state() == TABLET_SHUTDOWN) { LOG(INFO) << "tablet is to be deleted, skip load it" << " tablet id = " << tablet_meta->tablet_id() << " schema hash = " << tablet_meta->schema_hash(); _shutdown_tablets.push_back(tablet); return OLAP_ERR_TABLE_ALREADY_DELETED_ERROR; } // not check tablet init version because when be restarts during alter task the new tablet may be empty if (tablet->max_version().first == -1 && tablet->tablet_state() == TABLET_RUNNING) { LOG(WARNING) << "tablet is in running state without delta is invalid." << "tablet=" << tablet->full_name(); // tablet state is invalid, drop tablet return OLAP_ERR_TABLE_INDEX_VALIDATE_ERROR; } OLAPStatus res = tablet->init(); if (res != OLAP_SUCCESS) { LOG(WARNING) << "tablet init failed. tablet:" << tablet->full_name(); return res; } res = _add_tablet_unlock(tablet_id, schema_hash, tablet, update_meta, force); if (res != OLAP_SUCCESS) { // insert existed tablet return OLAP_SUCCESS if (res == OLAP_ERR_ENGINE_INSERT_EXISTS_TABLE) { LOG(WARNING) << "add duplicate tablet. tablet=" << tablet->full_name(); } LOG(WARNING) << "failed to add tablet. tablet=" << tablet->full_name(); return res; } return OLAP_SUCCESS; } // load_tablet_from_meta OLAPStatus TabletManager::load_tablet_from_dir( DataDir* store, TTabletId tablet_id, SchemaHash schema_hash, const string& schema_hash_path, bool force) { LOG(INFO) << "begin to load tablet from dir. " << " tablet_id=" << tablet_id << " schema_hash=" << schema_hash << " path = " << schema_hash_path; // not add lock here, because load_tablet_from_meta already add lock string header_path = TabletMeta::construct_header_file_path(schema_hash_path, tablet_id); // should change shard id before load tablet path boost_header_path(header_path); std::string shard_path = boost_header_path.parent_path().parent_path().parent_path().string(); std::string shard_str = shard_path.substr(shard_path.find_last_of('/') + 1); int32_t shard = stol(shard_str); // load dir is called by clone, restore, storage migration // should change tablet uid when tablet object changed OLAPStatus res = TabletMeta::reset_tablet_uid(header_path); if (res != OLAP_SUCCESS) { LOG(WARNING) << "failed to set tablet uid when copied tablet meta file" << " header_path=" << header_path; return res; } TabletMetaSharedPtr tablet_meta(new(nothrow) TabletMeta()); do { if (access(header_path.c_str(), F_OK) != 0) { LOG(WARNING) << "fail to find header file. [header_path=" << header_path << "]"; res = OLAP_ERR_FILE_NOT_EXIST; break; } if (tablet_meta == nullptr) { LOG(WARNING) << "fail to malloc TabletMeta."; res = OLAP_ERR_ENGINE_LOAD_INDEX_TABLE_ERROR; break; } if (tablet_meta->create_from_file(header_path) != OLAP_SUCCESS) { LOG(WARNING) << "fail to load tablet_meta. file_path=" << header_path; res = OLAP_ERR_ENGINE_LOAD_INDEX_TABLE_ERROR; break; } // has to change shard id here, because meta file maybe copyed from other source // its shard is different from local shard tablet_meta->set_shard_id(shard); std::string meta_binary; tablet_meta->serialize(&meta_binary); res = load_tablet_from_meta(store, tablet_id, schema_hash, meta_binary, true, force); if (res != OLAP_SUCCESS) { LOG(WARNING) << "fail to load tablet. [header_path=" << header_path << "]"; res = OLAP_ERR_ENGINE_LOAD_INDEX_TABLE_ERROR; break; } } while (0); return res; } // load_tablet_from_dir void TabletManager::release_schema_change_lock(TTabletId tablet_id) { VLOG(3) << "release_schema_change_lock begin. tablet_id=" << tablet_id; ReadLock rlock(&_tablet_map_lock); tablet_map_t::iterator it = _tablet_map.find(tablet_id); if (it == _tablet_map.end()) { LOG(WARNING) << "tablet does not exists. tablet=" << tablet_id; } else { it->second.schema_change_lock.unlock(); } VLOG(3) << "release_schema_change_lock end. tablet_id=" << tablet_id; } // release_schema_change_lock OLAPStatus TabletManager::report_tablet_info(TTabletInfo* tablet_info) { DorisMetrics::report_tablet_requests_total.increment(1); LOG(INFO) << "begin to process report tablet info." << "tablet_id=" << tablet_info->tablet_id << ", schema_hash=" << tablet_info->schema_hash; OLAPStatus res = OLAP_SUCCESS; TabletSharedPtr tablet = get_tablet( tablet_info->tablet_id, tablet_info->schema_hash); if (tablet == nullptr) { LOG(WARNING) << "can't find tablet. " << " tablet=" << tablet_info->tablet_id << " schema_hash=" << tablet_info->schema_hash; return OLAP_ERR_TABLE_NOT_FOUND; } tablet->build_tablet_report_info(tablet_info); VLOG(10) << "success to process report tablet info."; return res; } // report_tablet_info OLAPStatus TabletManager::report_all_tablets_info(std::map* tablets_info) { LOG(INFO) << "begin to process report all tablets info."; ReadLock rlock(&_tablet_map_lock); DorisMetrics::report_all_tablets_requests_total.increment(1); if (tablets_info == nullptr) { return OLAP_ERR_INPUT_PARAMETER_ERROR; } for (const auto& item : _tablet_map) { if (item.second.table_arr.size() == 0) { continue; } TTablet tablet; for (TabletSharedPtr tablet_ptr : item.second.table_arr) { if (tablet_ptr == nullptr) { continue; } TTabletInfo tablet_info; tablet_ptr->build_tablet_report_info(&tablet_info); // report expire transaction vector transaction_ids; // TODO(ygl): tablet manager and txn manager may be dead lock StorageEngine::instance()->txn_manager()->get_expire_txns(tablet_ptr->tablet_id(), tablet_ptr->schema_hash(), tablet_ptr->tablet_uid(), &transaction_ids); tablet_info.__set_transaction_ids(transaction_ids); tablet.tablet_infos.push_back(tablet_info); } if (tablet.tablet_infos.size() != 0) { tablets_info->insert(pair(tablet.tablet_infos[0].tablet_id, tablet)); } } LOG(INFO) << "success to process report all tablets info. tablet_num=" << tablets_info->size(); return OLAP_SUCCESS; } // report_all_tablets_info OLAPStatus TabletManager::start_trash_sweep() { { ReadLock rlock(&_tablet_map_lock); std::vector tablets_to_clean; for (auto& item : _tablet_map) { // try to clean empty item if (item.second.table_arr.empty()) { // try to get schema change lock if could get schema change lock, then nobody // own the lock could remove the item // it will core if schema change thread may hold the lock and this thread will deconstruct lock if (item.second.schema_change_lock.trylock() == OLAP_SUCCESS) { item.second.schema_change_lock.unlock(); tablets_to_clean.push_back(item.first); } } for (TabletSharedPtr tablet : item.second.table_arr) { if (tablet == nullptr) { continue; } tablet->delete_expired_inc_rowsets(); } } // clean empty tablet id item for (const auto& tablet_id_to_clean : tablets_to_clean) { if (_tablet_map[tablet_id_to_clean].table_arr.empty()) { _tablet_map.erase(tablet_id_to_clean); } } } int32_t clean_num = 0; do { sleep(1); clean_num = 0; // should get write lock here, because it will remove tablet from shut_down_tablets // and get tablet will access shut_down_tablets WriteLock wlock(&_tablet_map_lock); auto it = _shutdown_tablets.begin(); for (; it != _shutdown_tablets.end();) { // check if the meta has the tablet info and its state is shutdown if (it->use_count() > 1) { // it means current tablet is referenced in other thread ++it; continue; } TabletMetaSharedPtr new_tablet_meta(new(nothrow) TabletMeta()); if (new_tablet_meta == nullptr) { LOG(WARNING) << "fail to malloc TabletMeta."; ++it; continue; } OLAPStatus check_st = TabletMetaManager::get_meta((*it)->data_dir(), (*it)->tablet_id(), (*it)->schema_hash(), new_tablet_meta); if (check_st == OLAP_SUCCESS) { if (new_tablet_meta->tablet_state() != TABLET_SHUTDOWN || new_tablet_meta->tablet_uid() != (*it)->tablet_uid()) { LOG(WARNING) << "tablet's state changed to normal, skip remove dirs" << " tablet id = " << new_tablet_meta->tablet_id() << " schema hash = " << new_tablet_meta->schema_hash() << " old tablet_uid=" << (*it)->tablet_uid() << " cur tablet_uid=" << new_tablet_meta->tablet_uid(); // remove it from list it = _shutdown_tablets.erase(it); continue; } if (check_dir_existed((*it)->tablet_path())) { // take snapshot of tablet meta std::string meta_file = (*it)->tablet_path() + "/" + std::to_string((*it)->tablet_id()) + ".hdr"; (*it)->tablet_meta()->save(meta_file); LOG(INFO) << "start to move path to trash" << " tablet path = " << (*it)->tablet_path(); OLAPStatus rm_st = move_to_trash((*it)->tablet_path(), (*it)->tablet_path()); if (rm_st != OLAP_SUCCESS) { LOG(WARNING) << "failed to move dir to trash" << " dir = " << (*it)->tablet_path(); ++it; continue; } } TabletMetaManager::remove((*it)->data_dir(), (*it)->tablet_id(), (*it)->schema_hash()); LOG(INFO) << "successfully move tablet to trash." << " tablet id " << (*it)->tablet_id() << " schema hash " << (*it)->schema_hash() << " tablet path " << (*it)->tablet_path(); it = _shutdown_tablets.erase(it); ++ clean_num; } else { // if could not find tablet info in meta store, then check if dir existed if (check_dir_existed((*it)->tablet_path())) { LOG(WARNING) << "errors while load meta from store, skip this tablet" << " tablet id " << (*it)->tablet_id() << " schema hash " << (*it)->schema_hash(); ++it; } else { LOG(INFO) << "could not find tablet dir, skip move to trash, remove it from gc queue." << " tablet id " << (*it)->tablet_id() << " schema hash " << (*it)->schema_hash() << " tablet path " << (*it)->tablet_path(); it = _shutdown_tablets.erase(it); } } // if clean 100 tablets, should yield if (clean_num >= 200) { break; } } } while (clean_num >= 200); return OLAP_SUCCESS; } // start_trash_sweep bool TabletManager::try_schema_change_lock(TTabletId tablet_id) { bool res = false; VLOG(3) << "try_schema_change_lock begin. tablet_id=" << tablet_id; ReadLock rlock(&_tablet_map_lock); tablet_map_t::iterator it = _tablet_map.find(tablet_id); if (it == _tablet_map.end()) { LOG(WARNING) << "tablet does not exists. tablet_id=" << tablet_id; } else { res = (it->second.schema_change_lock.trylock() == OLAP_SUCCESS); } VLOG(3) << "try_schema_change_lock end. tablet_id=" << tablet_id; return res; } // try_schema_change_lock void TabletManager::update_root_path_info(std::map* path_map, int* tablet_counter) { ReadLock rlock(&_tablet_map_lock); for (auto& entry : _tablet_map) { TableInstances& instance = entry.second; for (auto& tablet : instance.table_arr) { (*tablet_counter) ++ ; int64_t data_size = tablet->tablet_footprint(); auto find = path_map->find(tablet->data_dir()->path()); if (find == path_map->end()) { continue; } if (find->second.is_used) { find->second.data_used_capacity += data_size; } } } } // update_root_path_info void TabletManager::get_partition_related_tablets(int64_t partition_id, std::set* tablet_infos) { ReadLock rlock(&_tablet_map_lock); if (_partition_tablet_map.find(partition_id) != _partition_tablet_map.end()) { for (auto& tablet_info : _partition_tablet_map[partition_id]) { tablet_infos->insert(tablet_info); } } } void TabletManager::do_tablet_meta_checkpoint(DataDir* data_dir) { vector related_tablets; { ReadLock tablet_map_rdlock(&_tablet_map_lock); for (tablet_map_t::value_type& table_ins : _tablet_map){ for (TabletSharedPtr& table_ptr : table_ins.second.table_arr) { // if tablet is not ready, it maybe a new tablet under schema change, not do compaction if (table_ptr->tablet_state() != TABLET_RUNNING) { continue; } if (table_ptr->data_dir()->path_hash() != data_dir->path_hash() || !table_ptr->is_used() || !table_ptr->init_succeeded()) { continue; } related_tablets.push_back(table_ptr); } } } for (TabletSharedPtr tablet : related_tablets) { tablet->do_tablet_meta_checkpoint(); } return; } void TabletManager::_build_tablet_stat() { _tablet_stat_cache.clear(); ReadLock rdlock(&_tablet_map_lock); for (const auto& item : _tablet_map) { if (item.second.table_arr.size() == 0) { continue; } TTabletStat stat; stat.tablet_id = item.first; for (TabletSharedPtr tablet : item.second.table_arr) { if (tablet == nullptr) { continue; } // we only get base tablet's stat stat.__set_data_size(tablet->tablet_footprint()); stat.__set_row_num(tablet->num_rows()); VLOG(3) << "tablet_id=" << item.first << ", data_size=" << tablet->tablet_footprint() << ", row_num:" << tablet->num_rows(); break; } _tablet_stat_cache.emplace(item.first, stat); } _tablet_stat_cache_update_time_ms = UnixMillis(); } OLAPStatus TabletManager::_create_inital_rowset( TabletSharedPtr tablet, const TCreateTabletReq& request) { OLAPStatus res = OLAP_SUCCESS; if (request.version < 1) { LOG(WARNING) << "init version of tablet should at least 1."; return OLAP_ERR_CE_CMD_PARAMS_ERROR; } else { Version version(0, request.version); VLOG(3) << "begin to create init version. " << "begin=" << version.first << ", end=" << version.second; RowsetSharedPtr new_rowset; do { if (version.first > version.second) { LOG(WARNING) << "begin should not larger than end." << " begin=" << version.first << " end=" << version.second; res = OLAP_ERR_INPUT_PARAMETER_ERROR; break; } RowsetWriterContext context; context.rowset_id = StorageEngine::instance()->next_rowset_id(); context.tablet_uid = tablet->tablet_uid(); context.tablet_id = tablet->tablet_id(); context.partition_id = tablet->partition_id(); context.tablet_schema_hash = tablet->schema_hash(); context.rowset_type = StorageEngine::instance()->default_rowset_type(); context.rowset_path_prefix = tablet->tablet_path(); context.tablet_schema = &(tablet->tablet_schema()); context.rowset_state = VISIBLE; context.data_dir = tablet->data_dir(); context.version = version; context.version_hash = request.version_hash; std::unique_ptr builder; res = RowsetFactory::create_rowset_writer(context, &builder); if (res != OLAP_SUCCESS) { LOG(WARNING) << "failed to init rowset writer for tablet " << tablet->full_name(); break; } res = builder->flush(); if (res != OLAP_SUCCESS) { LOG(WARNING) << "failed to flush rowset writer for tablet " << tablet->full_name(); break; } new_rowset = builder->build(); res = tablet->add_rowset(new_rowset, false); if (res != OLAP_SUCCESS) { LOG(WARNING) << "failed to add rowset for tablet " << tablet->full_name(); break; } } while (0); // Unregister index and delete files(index and data) if failed if (res != OLAP_SUCCESS) { LOG(WARNING) << "fail to create init base version. " << " res=" << res << " version=" << request.version; StorageEngine::instance()->add_unused_rowset(new_rowset); return res; } } tablet->set_cumulative_layer_point(request.version + 1); // should not save tablet meta here, because it will be saved if add to map successfully if (res != OLAP_SUCCESS) { LOG(WARNING) << "fail to save header. [tablet=" << tablet->full_name() << "]"; } return res; } OLAPStatus TabletManager::_create_tablet_meta( const TCreateTabletReq& request, DataDir* store, const bool is_schema_change_tablet, const TabletSharedPtr ref_tablet, TabletMetaSharedPtr* tablet_meta) { uint64_t shard_id = 0; OLAPStatus res = store->get_shard(&shard_id); if (res != OLAP_SUCCESS) { LOG(WARNING) << "fail to get root path shard. res=" << res; return res; } uint32_t next_unique_id = 0; uint32_t col_ordinal = 0; std::unordered_map col_ordinal_to_unique_id; if (!is_schema_change_tablet) { for (TColumn column : request.tablet_schema.columns) { col_ordinal_to_unique_id[col_ordinal] = col_ordinal; col_ordinal++; } next_unique_id = col_ordinal; } else { next_unique_id = ref_tablet->next_unique_id(); size_t num_columns = ref_tablet->num_columns(); size_t field = 0; for (TColumn column : request.tablet_schema.columns) { /* * for schema change, compare old_tablet and new_tablet * 1. if column in both new_tablet and old_tablet, * assign unique_id of old_tablet to the column of new_tablet * 2. if column exists only in new_tablet, assign next_unique_id of old_tablet * to the new column * */ for (field = 0 ; field < num_columns; ++field) { if (ref_tablet->tablet_schema().column(field).name() == column.column_name) { uint32_t unique_id = ref_tablet->tablet_schema().column(field).unique_id(); col_ordinal_to_unique_id[col_ordinal] = unique_id; break; } } if (field == num_columns) { col_ordinal_to_unique_id[col_ordinal] = next_unique_id; next_unique_id++; } col_ordinal++; } } LOG(INFO) << "next_unique_id:" << next_unique_id; // it is a new tablet meta obviously, should generate a new tablet id TabletUid tablet_uid = TabletUid::gen_uid(); res = TabletMeta::create(request.table_id, request.partition_id, request.tablet_id, request.tablet_schema.schema_hash, shard_id, request.tablet_schema, next_unique_id, col_ordinal_to_unique_id, tablet_meta, tablet_uid); return res; } OLAPStatus TabletManager::_drop_tablet_directly_unlocked( TTabletId tablet_id, SchemaHash schema_hash, bool keep_files) { OLAPStatus res = OLAP_SUCCESS; TabletSharedPtr dropped_tablet = _get_tablet_with_no_lock(tablet_id, schema_hash); if (dropped_tablet == nullptr) { LOG(WARNING) << "fail to drop not existed tablet. " << " tablet_id=" << tablet_id << " schema_hash=" << schema_hash; return OLAP_ERR_TABLE_NOT_FOUND; } for (list::iterator it = _tablet_map[tablet_id].table_arr.begin(); it != _tablet_map[tablet_id].table_arr.end();) { if ((*it)->equal(tablet_id, schema_hash)) { TabletSharedPtr tablet = *it; _partition_tablet_map[(*it)->partition_id()].erase((*it)->get_tablet_info()); if (_partition_tablet_map[(*it)->partition_id()].empty()) { _partition_tablet_map.erase((*it)->partition_id()); } it = _tablet_map[tablet_id].table_arr.erase(it); if (!keep_files) { // drop tablet will update tablet meta, should lock WriteLock wrlock(tablet->get_header_lock_ptr()); LOG(INFO) << "set tablet to shutdown state and remove it from memory" << " tablet_id=" << tablet_id << " schema_hash=" << schema_hash << " tablet path=" << dropped_tablet->tablet_path(); // has to update tablet here, must not update tablet meta directly // because other thread may hold the tablet object, they may save meta too // if update meta directly here, other thread may override the meta // and the tablet will be loaded at restart time. tablet->set_tablet_state(TABLET_SHUTDOWN); res = tablet->save_meta(); if (res != OLAP_SUCCESS) { LOG(WARNING) << "fail to drop tablet. " << " tablet_id=" << tablet_id << " schema_hash=" << schema_hash; return res; } _shutdown_tablets.push_back(tablet); } } else { ++it; } } res = dropped_tablet->deregister_tablet_from_dir(); if (res != OLAP_SUCCESS) { LOG(WARNING) << "fail to unregister from root path. " << " res= " << res << " tablet=" << tablet_id; } return res; } // _drop_tablet_directly_unlocked TabletSharedPtr TabletManager::_get_tablet_with_no_lock(TTabletId tablet_id, SchemaHash schema_hash) { VLOG(3) << "begin to get tablet. tablet_id=" << tablet_id << ", schema_hash=" << schema_hash; tablet_map_t::iterator it = _tablet_map.find(tablet_id); if (it != _tablet_map.end()) { for (TabletSharedPtr tablet : it->second.table_arr) { CHECK(tablet != nullptr) << "tablet is nullptr:" << tablet; if (tablet->equal(tablet_id, schema_hash)) { VLOG(3) << "get tablet success. tablet_id=" << tablet_id << ", schema_hash=" << schema_hash; return tablet; } } } VLOG(3) << "fail to get tablet. tablet_id=" << tablet_id << ", schema_hash=" << schema_hash; // Return empty tablet if fail TabletSharedPtr tablet; return tablet; } // _get_tablet_with_no_lock } // doris