blob: ac2f17db2521eae67c912c0b0c6606593a75ca6e [file]
// 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 "storage/index/index_file_reader.h"
#include <memory>
#include <utility>
#include "common/cast_set.h"
#include "common/config.h"
#include "storage/index/inverted/inverted_index_compound_reader.h"
#include "storage/index/inverted/inverted_index_fs_directory.h"
#include "storage/tablet/tablet_schema.h"
#include "util/debug_points.h"
namespace doris::segment_v2 {
Status IndexFileReader::init(int32_t read_buffer_size, const io::IOContext* io_ctx) {
std::unique_lock<std::shared_mutex> lock(_mutex); // Lock for writing
if (!_inited) {
_read_buffer_size = read_buffer_size;
if (_storage_format == InvertedIndexStorageFormatPB::SNII) {
RETURN_IF_ERROR(_init_snii(io_ctx));
} else if (_storage_format >= InvertedIndexStorageFormatPB::V2) {
RETURN_IF_ERROR(_init_from(read_buffer_size, io_ctx));
}
_inited = true;
}
return Status::OK();
}
Status IndexFileReader::_init_from(int32_t read_buffer_size, const io::IOContext* io_ctx) {
auto index_file_full_path = InvertedIndexDescriptor::get_index_file_path_v2(_index_path_prefix);
try {
CLuceneError err;
CL_NS(store)::IndexInput* index_input = nullptr;
// 1. get file size from meta
int64_t file_size = -1;
if (_idx_file_info.has_index_size()) {
file_size = _idx_file_info.index_size();
}
file_size = file_size == 0 ? -1 : file_size;
DBUG_EXECUTE_IF("file_size_not_in_rowset_meta ", {
if (file_size == -1) {
return Status::Error<ErrorCode::INVERTED_INDEX_CLUCENE_ERROR>(
"CLuceneError occur file size = -1, file is {}", index_file_full_path);
}
})
DCHECK(_fs != nullptr) << "file system is nullptr, index_file_full_path: "
<< index_file_full_path;
// 2. open file
auto ok =
DorisFSDirectory::FSIndexInput::open(_fs, index_file_full_path.c_str(), index_input,
err, read_buffer_size, file_size, _tablet_id);
if (!ok) {
if (err.number() == CL_ERR_FileNotFound) {
return Status::Error<ErrorCode::INVERTED_INDEX_FILE_NOT_FOUND>(
"inverted index file {} is not found.", index_file_full_path);
} else if (err.number() == CL_ERR_EmptyIndexSegment) {
return Status::Error<ErrorCode::INVERTED_INDEX_BYPASS>(
"inverted index file {} is empty.", index_file_full_path);
}
return Status::Error<ErrorCode::INVERTED_INDEX_CLUCENE_ERROR>(
"CLuceneError occur when open idx file {}, error msg: {}", index_file_full_path,
err.what());
}
_stream = std::unique_ptr<CL_NS(store)::IndexInput>(index_input);
_stream->setIoContext(io_ctx);
_stream->setIndexFile(true);
// 3. read file
int32_t version = _stream->readInt(); // Read version number
if (version >= InvertedIndexStorageFormatPB::V2) {
DCHECK(version == _storage_format);
int32_t numIndices = _stream->readInt(); // Read number of indices
for (int32_t i = 0; i < numIndices; ++i) {
int64_t indexId = _stream->readLong(); // Read index ID
int32_t suffix_length = _stream->readInt(); // Read suffix length
std::vector<uint8_t> suffix_data(suffix_length);
_stream->readBytes(suffix_data.data(), suffix_length);
std::string suffix_str(suffix_data.begin(), suffix_data.end());
int32_t numFiles = _stream->readInt(); // Read number of files in the index
auto fileEntries = std::make_unique<EntriesType>();
fileEntries->reserve(numFiles);
for (int32_t j = 0; j < numFiles; ++j) {
int32_t file_name_length = _stream->readInt();
std::string file_name(file_name_length, '\0');
_stream->readBytes(reinterpret_cast<uint8_t*>(file_name.data()),
file_name_length);
auto entry = std::make_unique<ReaderFileEntry>();
entry->file_name = std::move(file_name);
entry->offset = _stream->readLong();
entry->length = _stream->readLong();
fileEntries->emplace(entry->file_name, std::move(entry));
}
_indices_entries.emplace(std::make_pair(indexId, std::move(suffix_str)),
std::move(fileEntries));
}
} else {
return Status::Error<ErrorCode::INVERTED_INDEX_CLUCENE_ERROR>(
"unknown inverted index format {}", version);
}
} catch (CLuceneError& err) {
if (_stream != nullptr) {
try {
_stream->close();
} catch (CLuceneError& err) {
return Status::Error<ErrorCode::INVERTED_INDEX_CLUCENE_ERROR>(
"CLuceneError occur when close idx file {}, error msg: {}",
index_file_full_path, err.what());
}
}
return Status::Error<ErrorCode::INVERTED_INDEX_CLUCENE_ERROR>(
"CLuceneError occur when init idx file {}, error msg: {}", index_file_full_path,
err.what());
}
return Status::OK();
}
Status IndexFileReader::_init_snii(const io::IOContext* io_ctx) {
auto index_file_full_path = InvertedIndexDescriptor::get_index_file_path_v2(_index_path_prefix);
int64_t file_size = -1;
if (_idx_file_info.has_index_size()) {
file_size = _idx_file_info.index_size();
}
file_size = file_size == 0 ? -1 : file_size;
io::FileReaderOptions opts;
opts.cache_type = config::enable_file_cache ? io::FileCachePolicy::FILE_BLOCK_CACHE
: io::FileCachePolicy::NO_CACHE;
opts.is_doris_table = true;
opts.file_size = file_size;
opts.tablet_id = _tablet_id;
io::FileReaderSPtr reader;
// A rowset written before any index existed has no container at all. The
// filesystem reports that as a plain NOT_FOUND; translate it to the
// index-specific code the way the V1/V2 path does, because callers
// (IndexBuilder's BUILD INDEX rewrite) distinguish "no container yet, build
// everything fresh" from a real IO failure by exactly that code.
if (const Status open_status = _fs->open_file(index_file_full_path, &reader, &opts);
!open_status.ok()) {
if (open_status.is<ErrorCode::NOT_FOUND>()) {
return Status::Error<ErrorCode::INVERTED_INDEX_FILE_NOT_FOUND, false>(
"inverted index file {} is not found.", index_file_full_path);
}
return open_status;
}
// With NO_CACHE on a remote filesystem there is no CachedRemoteFileReader to
// account physical remote bytes, so the adapter must count its own reads.
const bool direct_remote_io = opts.cache_type == io::FileCachePolicy::NO_CACHE &&
_fs->type() != io::FileSystemType::LOCAL;
_snii_file_reader = std::make_shared<snii_doris::DorisSniiFileReader>(
std::move(reader), /*io_ctx=*/nullptr, direct_remote_io);
_snii_segment_reader = std::make_unique<doris::snii::reader::SniiSegmentReader>();
io::IOContext meta_io_ctx;
if (io_ctx != nullptr) {
meta_io_ctx = *io_ctx;
}
meta_io_ctx.is_inverted_index = true;
meta_io_ctx.is_index_data = true;
snii_doris::DorisSniiFileReader::ScopedIOContext io_context_scope(&meta_io_ctx);
RETURN_IF_ERROR(doris::snii::reader::SniiSegmentReader::open(_snii_file_reader.get(),
_snii_segment_reader.get()));
return Status::OK();
}
Result<InvertedIndexDirectoryMap> IndexFileReader::get_all_directories() {
if (_storage_format == InvertedIndexStorageFormatPB::SNII) {
return ResultError(Status::Error<ErrorCode::INVERTED_INDEX_NOT_SUPPORTED>(
"SNII format does not expose CLucene directories"));
}
InvertedIndexDirectoryMap res;
std::shared_lock<std::shared_mutex> lock(_mutex); // Lock for reading
for (auto& [index, _] : _indices_entries) {
auto&& [index_id, index_suffix] = index;
LOG(INFO) << "index_id:" << index_id << " index_suffix:" << index_suffix;
auto ret = _open(index_id, index_suffix);
if (!ret.has_value()) {
return ResultError(ret.error());
}
res.emplace(std::make_pair(index_id, index_suffix), std::move(ret.value()));
}
return res;
}
Result<std::unique_ptr<DorisCompoundReader, DirectoryDeleter>> IndexFileReader::_open(
int64_t index_id, const std::string& index_suffix, const io::IOContext* io_ctx) const {
std::unique_ptr<DorisCompoundReader, DirectoryDeleter> compound_reader;
if (_storage_format == InvertedIndexStorageFormatPB::SNII) {
// A blob logical index is a named-sub-file table over the container, and
// a compound reader is a named-sub-file table over a stream -- the same
// shape. The offsets recorded in the directory are ABSOLUTE container
// offsets, exactly like a V2 compound entry, so the sub-files need no
// rebasing and DorisCompoundReader is reused unchanged.
const auto index_file_path =
InvertedIndexDescriptor::get_index_file_path_v2(_index_path_prefix);
EntriesType entries;
int64_t container_size = 0;
// The lock spans every use of _snii_segment_reader state, not just the
// lookup: `entry` points into the reader's decoded directory, and every
// other SNII accessor on this class holds the lock across the whole use.
// Narrowing it here would make this the one site whose safety rests on
// "the segment reader is never reset after init" rather than on the lock.
{
std::shared_lock<std::shared_mutex> lock(_mutex);
if (_snii_segment_reader == nullptr) {
return ResultError(Status::Error<ErrorCode::INVERTED_INDEX_FILE_NOT_FOUND>(
"SNII index file {} is not opened", index_file_path));
}
const doris::snii::format::LogicalIndexMetadataRef* entry = nullptr;
RETURN_IF_ERROR_RESULT(_snii_segment_reader->blob_entry(cast_set<uint64_t>(index_id),
index_suffix, &entry));
DORIS_CHECK(entry != nullptr);
// Only an ANN index is served through a CLucene directory. A BKD blob
// has its own reader and must not be reachable this way, or a caller
// would get a directory over bytes no CLucene code can parse.
if (entry->kind != doris::snii::format::LogicalIndexKind::kAnn) {
return ResultError(Status::Error<ErrorCode::INVERTED_INDEX_NOT_SUPPORTED>(
"SNII logical index {} is not an ANN blob; it has no CLucene directory",
index_id));
}
// Blob extents were bounded against the container at open time
// (SniiSegmentReader::validate_blob_files), so they are safe to hand
// to the compound reader as-is.
for (const auto& blob : entry->files) {
auto file_entry = std::make_unique<ReaderFileEntry>();
file_entry->file_name = blob.name;
file_entry->offset = cast_set<int64_t>(blob.offset);
file_entry->length = cast_set<int64_t>(blob.length);
entries.emplace(blob.name, std::move(file_entry));
}
container_size = get_inverted_file_size();
}
CLuceneError err;
CL_NS(store)::IndexInput* index_input = nullptr;
// The container size is already resident -- init() opened the file to read
// its directory. Passing -1 here would make the filesystem re-discover it,
// which is a stat() locally and a HeadObject round trip on S3, on every
// cold ANN index load.
if (!DorisFSDirectory::FSIndexInput::open(_fs, index_file_path.c_str(), index_input, err,
_read_buffer_size, container_size, _tablet_id)) {
return ResultError(Status::Error<ErrorCode::INVERTED_INDEX_CLUCENE_ERROR>(
"CLuceneError occur when open SNII container {}, error msg: {}",
index_file_path, err.what()));
}
compound_reader.reset(
new DorisCompoundReader(index_input, entries, _read_buffer_size, io_ctx));
return compound_reader;
}
if (_storage_format == InvertedIndexStorageFormatPB::V1) {
auto index_file_path = InvertedIndexDescriptor::get_index_file_path_v1(
_index_path_prefix, index_id, index_suffix);
try {
CLuceneError err;
CL_NS(store)::IndexInput* index_input = nullptr;
// 1. get file size from meta
int64_t file_size = -1;
if (_idx_file_info.index_info_size() > 0) {
for (const auto& idx_info : _idx_file_info.index_info()) {
if (index_id == idx_info.index_id() &&
index_suffix == idx_info.index_suffix()) {
file_size = idx_info.index_file_size();
break;
}
}
}
file_size = file_size == 0 ? -1 : file_size;
DBUG_EXECUTE_IF("file_size_not_in_rowset_meta ", {
if (file_size == -1) {
return ResultError(Status::Error<ErrorCode::INVERTED_INDEX_CLUCENE_ERROR>(
"CLuceneError occur file size = -1, file is {}", index_file_path));
}
})
DCHECK(_fs != nullptr)
<< "file system is nullptr, index_file_path: " << index_file_path;
// 2. open file
auto ok = DorisFSDirectory::FSIndexInput::open(_fs, index_file_path.c_str(),
index_input, err, _read_buffer_size,
file_size, _tablet_id);
if (!ok) {
// now index_input = nullptr
if (err.number() == CL_ERR_FileNotFound) {
return ResultError(Status::Error<ErrorCode::INVERTED_INDEX_FILE_NOT_FOUND>(
"inverted index file {} is not found.", index_file_path));
}
return ResultError(Status::Error<ErrorCode::INVERTED_INDEX_CLUCENE_ERROR>(
"CLuceneError occur when open idx file {}, error msg: {}", index_file_path,
err.what()));
}
// 3. read file in DorisCompoundReader
compound_reader.reset(new DorisCompoundReader(index_input, _read_buffer_size));
} catch (CLuceneError& err) {
return ResultError(Status::Error<ErrorCode::INVERTED_INDEX_CLUCENE_ERROR>(
"CLuceneError occur when open idx file {}, error msg: {}", index_file_path,
err.what()));
}
} else {
std::shared_lock<std::shared_mutex> lock(_mutex); // Lock for reading
if (_stream == nullptr) {
return ResultError(Status::Error<ErrorCode::INVERTED_INDEX_FILE_NOT_FOUND>(
"CLuceneError occur when open idx file {}, stream is nullptr",
InvertedIndexDescriptor::get_index_file_path_v2(_index_path_prefix)));
}
// Check if the specified index exists
auto index_it = _indices_entries.find(std::make_pair(index_id, index_suffix));
if (index_it == _indices_entries.end()) {
std::ostringstream errMsg;
errMsg << "No index with id " << index_id << " found";
return ResultError(Status::Error<ErrorCode::INVERTED_INDEX_FILE_NOT_FOUND>(
"CLuceneError occur when open idx file {}, error msg: {}",
InvertedIndexDescriptor::get_index_file_path_v2(_index_path_prefix),
errMsg.str()));
}
// Need to clone resource here, because index searcher cache need it.
compound_reader.reset(new DorisCompoundReader(_stream->clone(), *index_it->second,
_read_buffer_size, io_ctx));
}
return compound_reader;
}
Result<std::unique_ptr<doris::snii::reader::LogicalIndexReader>> IndexFileReader::open_snii_index(
const TabletIndex* index_meta, const io::IOContext* io_ctx,
doris::snii::reader::LogicalIndexOpenMode open_mode) const {
DCHECK(_storage_format == InvertedIndexStorageFormatPB::SNII);
std::shared_lock<std::shared_mutex> lock(_mutex);
if (_snii_segment_reader == nullptr) {
return ResultError(Status::Error<ErrorCode::INVERTED_INDEX_FILE_NOT_FOUND>(
"SNII index file {} is not opened",
InvertedIndexDescriptor::get_index_file_path_v2(_index_path_prefix)));
}
io::IOContext meta_io_ctx;
if (io_ctx != nullptr) {
meta_io_ctx = *io_ctx;
}
meta_io_ctx.is_inverted_index = true;
meta_io_ctx.is_index_data = true;
snii_doris::DorisSniiFileReader::ScopedIOContext io_context_scope(&meta_io_ctx);
auto logical_reader = std::make_unique<doris::snii::reader::LogicalIndexReader>();
auto status = _snii_segment_reader->open_index(cast_set<uint64_t>(index_meta->index_id()),
index_meta->get_index_suffix(),
logical_reader.get(), open_mode);
auto doris_status = status;
if (!doris_status.ok()) {
return ResultError(doris_status);
}
return logical_reader;
}
Result<std::unique_ptr<doris::snii::bkd::BkdSearcher>> IndexFileReader::open_snii_bkd_index(
const TabletIndex* index_meta, const io::IOContext* io_ctx) const {
DCHECK(_storage_format == InvertedIndexStorageFormatPB::SNII);
std::shared_lock<std::shared_mutex> lock(_mutex);
if (_snii_segment_reader == nullptr) {
return ResultError(Status::Error<ErrorCode::INVERTED_INDEX_FILE_NOT_FOUND>(
"SNII index file {} is not opened",
InvertedIndexDescriptor::get_index_file_path_v2(_index_path_prefix)));
}
io::IOContext meta_io_ctx;
if (io_ctx != nullptr) {
meta_io_ctx = *io_ctx;
}
meta_io_ctx.is_inverted_index = true;
meta_io_ctx.is_index_data = true;
snii_doris::DorisSniiFileReader::ScopedIOContext io_context_scope(&meta_io_ctx);
const doris::snii::format::LogicalIndexMetadataRef* entry = nullptr;
RETURN_IF_ERROR_RESULT(_snii_segment_reader->blob_entry(
cast_set<uint64_t>(index_meta->index_id()), index_meta->get_index_suffix(), &entry));
// Placement is the container's decision, so every extent comes from the
// sealed directory rather than from anything the producer remembered.
doris::snii::bkd::BkdSections sections;
auto searcher = std::make_unique<doris::snii::bkd::BkdSearcher>();
for (const doris::snii::format::NamedBlobFileRef& blob : entry->files) {
if (blob.name == "bkd_data") {
sections.data_offset = blob.offset;
sections.data_length = blob.length;
} else if (blob.name == "bkd_index") {
sections.index_offset = blob.offset;
sections.index_length = blob.length;
} else if (blob.name == "bkd_nulls") {
searcher->null_bitmap_offset = blob.offset;
searcher->null_bitmap_length = blob.length;
}
}
RETURN_IF_ERROR_RESULT(doris::snii::bkd::BkdReader::open(_snii_segment_reader->reader(),
sections, &searcher->reader));
return searcher;
}
Status IndexFileReader::prepare_snii_rewrite_snapshot(
const std::vector<doris::snii::reader::LogicalIndexKey>& keep, uint64_t segment_doc_count,
doris::snii::reader::SniiRewriteSnapshot* out) const {
DCHECK(_storage_format == InvertedIndexStorageFormatPB::SNII);
std::shared_lock<std::shared_mutex> lock(_mutex);
if (_snii_segment_reader == nullptr) {
return Status::Error<ErrorCode::INVERTED_INDEX_FILE_NOT_FOUND>(
"SNII index file {} is not opened",
InvertedIndexDescriptor::get_index_file_path_v2(_index_path_prefix));
}
return _snii_segment_reader->prepare_rewrite_snapshot(keep, segment_doc_count, out);
}
Result<std::unique_ptr<DorisCompoundReader, DirectoryDeleter>> IndexFileReader::open(
const TabletIndex* index_meta, const io::IOContext* io_ctx) const {
auto index_id = index_meta->index_id();
auto index_suffix = index_meta->get_index_suffix();
return _open(index_id, index_suffix, io_ctx);
}
std::string IndexFileReader::get_index_file_cache_key(const TabletIndex* index_meta) const {
return InvertedIndexDescriptor::get_index_file_cache_key(
_index_path_prefix, index_meta->index_id(), index_meta->get_index_suffix());
}
std::string IndexFileReader::get_index_file_path(const TabletIndex* index_meta) const {
if (_storage_format == InvertedIndexStorageFormatPB::V1) {
return InvertedIndexDescriptor::get_index_file_path_v1(
_index_path_prefix, index_meta->index_id(), index_meta->get_index_suffix());
}
return InvertedIndexDescriptor::get_index_file_path_v2(_index_path_prefix);
}
Status IndexFileReader::index_file_exist(const TabletIndex* index_meta, bool* res) const {
if (_storage_format == InvertedIndexStorageFormatPB::V1) {
auto index_file_path = InvertedIndexDescriptor::get_index_file_path_v1(
_index_path_prefix, index_meta->index_id(), index_meta->get_index_suffix());
return _fs->exists(index_file_path, res);
} else if (_storage_format == InvertedIndexStorageFormatPB::SNII) {
auto index_file_path = InvertedIndexDescriptor::get_index_file_path_v2(_index_path_prefix);
RETURN_IF_ERROR(_fs->exists(index_file_path, res));
if (!*res) {
return Status::OK();
}
std::shared_lock<std::shared_mutex> lock(_mutex);
if (_snii_segment_reader == nullptr) {
// The container is on disk but this reader never opened it, so we
// cannot tell whether the index is in there. Answering "absent"
// would make a BUILD INDEX rewrite drop it silently; report the real
// condition instead, as the V2 branch below does.
*res = false;
return Status::Error<ErrorCode::INVERTED_INDEX_FILE_NOT_FOUND>(
"SNII idx file {} exists but is not opened",
InvertedIndexDescriptor::get_index_file_path_v2(_index_path_prefix));
}
return _snii_segment_reader->index_exists(cast_set<uint64_t>(index_meta->index_id()),
index_meta->get_index_suffix(), res);
} else {
std::shared_lock<std::shared_mutex> lock(_mutex); // Lock for reading
if (_stream == nullptr) {
*res = false;
return Status::Error<ErrorCode::INVERTED_INDEX_FILE_NOT_FOUND>(
"idx file {} is not opened",
InvertedIndexDescriptor::get_index_file_path_v2(_index_path_prefix));
}
// Check if the specified index exists
auto index_it = _indices_entries.find(
std::make_pair(index_meta->index_id(), index_meta->get_index_suffix()));
if (index_it == _indices_entries.end()) {
*res = false;
} else {
*res = true;
}
}
return Status::OK();
}
Status IndexFileReader::has_null(const TabletIndex* index_meta, bool* res) const {
if (_storage_format == InvertedIndexStorageFormatPB::V1) {
*res = true;
return Status::OK();
}
if (_storage_format == InvertedIndexStorageFormatPB::SNII) {
std::shared_lock<std::shared_mutex> lock(_mutex);
if (_snii_segment_reader == nullptr) {
return Status::Error<ErrorCode::INVERTED_INDEX_FILE_NOT_FOUND>(
"SNII index file {} is not opened",
InvertedIndexDescriptor::get_index_file_path_v2(_index_path_prefix));
}
io::IOContext meta_io_ctx;
meta_io_ctx.is_inverted_index = true;
meta_io_ctx.is_index_data = true;
snii_doris::DorisSniiFileReader::ScopedIOContext io_context_scope(&meta_io_ctx);
doris::snii::format::SectionRefs section_refs;
RETURN_IF_ERROR(_snii_segment_reader->section_refs_for_index(
cast_set<uint64_t>(index_meta->index_id()), index_meta->get_index_suffix(),
&section_refs));
*res = section_refs.null_bitmap.length > 0;
return Status::OK();
}
std::shared_lock<std::shared_mutex> lock(_mutex); // Lock for reading
if (_stream == nullptr) {
return Status::Error<ErrorCode::INVERTED_INDEX_FILE_NOT_FOUND>(
"idx file {} is not opened",
InvertedIndexDescriptor::get_index_file_path_v2(_index_path_prefix));
}
// Check if the specified index exists
auto index_it = _indices_entries.find(
std::make_pair(index_meta->index_id(), index_meta->get_index_suffix()));
if (index_it == _indices_entries.end()) {
*res = false;
} else {
const auto& entries = index_it->second;
auto entry_it =
entries->find(InvertedIndexDescriptor::get_temporary_null_bitmap_file_name());
if (entry_it == entries->end()) {
*res = false;
return Status::OK();
}
const auto& e = entry_it->second;
// roaring bitmap cookie header size is 5
if (e->length <= 5) {
*res = false;
} else {
*res = true;
}
}
return Status::OK();
}
void IndexFileReader::debug_file_entries() {
std::shared_lock<std::shared_mutex> lock(_mutex); // Lock for reading
for (const auto& index : _indices_entries) {
LOG(INFO) << "index_id:" << index.first.first;
const auto& index_entries = index.second;
for (const auto& entry : *index_entries) {
const auto& file_entry = entry.second;
LOG(INFO) << "file entry name:" << file_entry->file_name
<< " length:" << file_entry->length << " offset:" << file_entry->offset;
}
}
}
} // namespace doris::segment_v2