mirror of https://github.com/milvus-io/milvus.git
fix: delete may lost when enable lru cache, some field should be reset when ReleaseData (#32012)
issue: #30361 - Delete may be lost when segment is not data-loaded status in lru cache. skip filtering to fix it. - `stats_` and `variable_fields_avg_size_` should be reset when `ReleaseData` - Remove repeat load delta log operation in lru. --------- Signed-off-by: chyezh <chyezh@outlook.com>pull/32182/head
parent
55d894bd5e
commit
e19d17076f
|
@ -182,12 +182,12 @@ GetDataTypeName(DataType data_type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
inline size_t
|
inline size_t
|
||||||
CalcPksSize(const std::vector<PkType>& pks) {
|
CalcPksSize(const PkType* data, size_t n) {
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
for (auto& pk : pks) {
|
for (size_t i = 0; i < n; ++i) {
|
||||||
size += sizeof(pk);
|
size += sizeof(data[i]);
|
||||||
if (std::holds_alternative<std::string>(pk)) {
|
if (std::holds_alternative<std::string>(data[i])) {
|
||||||
size += std::get<std::string>(pk).size();
|
size += std::get<std::string>(data[i]).size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return size;
|
return size;
|
||||||
|
|
|
@ -105,6 +105,8 @@ struct DeletedRecord {
|
||||||
pks_.set_data_raw(n, pks.data() + divide_point, size);
|
pks_.set_data_raw(n, pks.data() + divide_point, size);
|
||||||
timestamps_.set_data_raw(n, timestamps + divide_point, size);
|
timestamps_.set_data_raw(n, timestamps + divide_point, size);
|
||||||
n_ += size;
|
n_ += size;
|
||||||
|
mem_size_ += sizeof(Timestamp) * size +
|
||||||
|
CalcPksSize(pks.data() + divide_point, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ConcurrentVector<Timestamp>&
|
const ConcurrentVector<Timestamp>&
|
||||||
|
@ -122,12 +124,18 @@ struct DeletedRecord {
|
||||||
return n_.load();
|
return n_.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
mem_size() const {
|
||||||
|
return mem_size_.load();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<TmpBitmap> lru_;
|
std::shared_ptr<TmpBitmap> lru_;
|
||||||
std::shared_mutex shared_mutex_;
|
std::shared_mutex shared_mutex_;
|
||||||
|
|
||||||
std::shared_mutex buffer_mutex_;
|
std::shared_mutex buffer_mutex_;
|
||||||
std::atomic<int64_t> n_ = 0;
|
std::atomic<int64_t> n_ = 0;
|
||||||
|
std::atomic<int64_t> mem_size_ = 0;
|
||||||
ConcurrentVector<Timestamp> timestamps_;
|
ConcurrentVector<Timestamp> timestamps_;
|
||||||
ConcurrentVector<PkType> pks_;
|
ConcurrentVector<PkType> pks_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -395,7 +395,6 @@ SegmentGrowingImpl::Delete(int64_t reserved_begin,
|
||||||
|
|
||||||
// step 2: fill delete record
|
// step 2: fill delete record
|
||||||
deleted_record_.push(sort_pks, sort_timestamps.data());
|
deleted_record_.push(sort_pks, sort_timestamps.data());
|
||||||
stats_.mem_size += size * sizeof(Timestamp) + CalcPksSize(sort_pks);
|
|
||||||
return SegcoreError::success();
|
return SegcoreError::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -417,8 +416,6 @@ SegmentGrowingImpl::LoadDeletedRecord(const LoadDeletedRecordInfo& info) {
|
||||||
|
|
||||||
// step 2: fill pks and timestamps
|
// step 2: fill pks and timestamps
|
||||||
deleted_record_.push(pks, timestamps);
|
deleted_record_.push(pks, timestamps);
|
||||||
|
|
||||||
stats_.mem_size += info.row_count * sizeof(Timestamp) + CalcPksSize(pks);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SpanBase
|
SpanBase
|
||||||
|
|
|
@ -130,6 +130,11 @@ class SegmentGrowingImpl : public SegmentGrowing {
|
||||||
try_remove_chunks(FieldId fieldId);
|
try_remove_chunks(FieldId fieldId);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
size_t
|
||||||
|
GetMemoryUsageInBytes() const override {
|
||||||
|
return stats_.mem_size.load() + deleted_record_.mem_size();
|
||||||
|
}
|
||||||
|
|
||||||
int64_t
|
int64_t
|
||||||
get_row_count() const override {
|
get_row_count() const override {
|
||||||
return insert_record_.ack_responder_.GetAck();
|
return insert_record_.ack_responder_.GetAck();
|
||||||
|
@ -305,6 +310,8 @@ class SegmentGrowingImpl : public SegmentGrowing {
|
||||||
mutable DeletedRecord deleted_record_;
|
mutable DeletedRecord deleted_record_;
|
||||||
|
|
||||||
int64_t id_;
|
int64_t id_;
|
||||||
|
|
||||||
|
SegmentStats stats_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
const static IndexMetaPtr empty_index_meta =
|
const static IndexMetaPtr empty_index_meta =
|
||||||
|
|
|
@ -69,10 +69,8 @@ class SegmentInterface {
|
||||||
Timestamp timestamp,
|
Timestamp timestamp,
|
||||||
int64_t limit_size) const = 0;
|
int64_t limit_size) const = 0;
|
||||||
|
|
||||||
size_t
|
virtual size_t
|
||||||
GetMemoryUsageInBytes() const {
|
GetMemoryUsageInBytes() const = 0;
|
||||||
return stats_.mem_size;
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual int64_t
|
virtual int64_t
|
||||||
get_row_count() const = 0;
|
get_row_count() const = 0;
|
||||||
|
@ -120,9 +118,6 @@ class SegmentInterface {
|
||||||
|
|
||||||
virtual bool
|
virtual bool
|
||||||
HasRawData(int64_t field_id) const = 0;
|
HasRawData(int64_t field_id) const = 0;
|
||||||
|
|
||||||
protected:
|
|
||||||
SegmentStats stats_{};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// internal API for DSL calculation
|
// internal API for DSL calculation
|
||||||
|
|
|
@ -627,8 +627,6 @@ SegmentSealedImpl::LoadDeletedRecord(const LoadDeletedRecordInfo& info) {
|
||||||
|
|
||||||
// step 2: fill pks and timestamps
|
// step 2: fill pks and timestamps
|
||||||
deleted_record_.push(pks, timestamps);
|
deleted_record_.push(pks, timestamps);
|
||||||
|
|
||||||
stats_.mem_size += sizeof(Timestamp) * info.row_count + CalcPksSize(pks);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1125,15 +1123,20 @@ SegmentSealedImpl::bulk_subscript_impl(int64_t element_sizeof,
|
||||||
|
|
||||||
void
|
void
|
||||||
SegmentSealedImpl::ClearData() {
|
SegmentSealedImpl::ClearData() {
|
||||||
field_data_ready_bitset_.reset();
|
{
|
||||||
index_ready_bitset_.reset();
|
std::unique_lock lck(mutex_);
|
||||||
binlog_index_bitset_.reset();
|
field_data_ready_bitset_.reset();
|
||||||
system_ready_count_ = 0;
|
index_ready_bitset_.reset();
|
||||||
num_rows_ = std::nullopt;
|
binlog_index_bitset_.reset();
|
||||||
scalar_indexings_.clear();
|
system_ready_count_ = 0;
|
||||||
vector_indexings_.clear();
|
num_rows_ = std::nullopt;
|
||||||
insert_record_.clear();
|
scalar_indexings_.clear();
|
||||||
fields_.clear();
|
vector_indexings_.clear();
|
||||||
|
insert_record_.clear();
|
||||||
|
fields_.clear();
|
||||||
|
variable_fields_avg_size_.clear();
|
||||||
|
stats_.mem_size = 0;
|
||||||
|
}
|
||||||
auto cc = storage::ChunkCacheSingleton::GetInstance().GetChunkCache();
|
auto cc = storage::ChunkCacheSingleton::GetInstance().GetChunkCache();
|
||||||
if (cc == nullptr) {
|
if (cc == nullptr) {
|
||||||
return;
|
return;
|
||||||
|
@ -1459,14 +1462,18 @@ SegmentSealedImpl::Delete(int64_t reserved_offset, // deprecated
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
ordering[i] = std::make_tuple(timestamps_raw[i], pks[i]);
|
ordering[i] = std::make_tuple(timestamps_raw[i], pks[i]);
|
||||||
}
|
}
|
||||||
auto end =
|
// if insert_record_ is empty (may be only-load meta but not data for lru-cache at go side),
|
||||||
std::remove_if(ordering.begin(),
|
// filtering may cause the deletion lost, skip the filtering to avoid it.
|
||||||
ordering.end(),
|
if (!insert_record_.empty_pks()) {
|
||||||
[&](const std::tuple<Timestamp, PkType>& record) {
|
auto end = std::remove_if(
|
||||||
return !insert_record_.contain(std::get<1>(record));
|
ordering.begin(),
|
||||||
});
|
ordering.end(),
|
||||||
size = end - ordering.begin();
|
[&](const std::tuple<Timestamp, PkType>& record) {
|
||||||
ordering.resize(size);
|
return !insert_record_.contain(std::get<1>(record));
|
||||||
|
});
|
||||||
|
size = end - ordering.begin();
|
||||||
|
ordering.resize(size);
|
||||||
|
}
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
return SegcoreError::success();
|
return SegcoreError::success();
|
||||||
}
|
}
|
||||||
|
@ -1483,9 +1490,6 @@ SegmentSealedImpl::Delete(int64_t reserved_offset, // deprecated
|
||||||
}
|
}
|
||||||
|
|
||||||
deleted_record_.push(sort_pks, sort_timestamps.data());
|
deleted_record_.push(sort_pks, sort_timestamps.data());
|
||||||
|
|
||||||
stats_.mem_size +=
|
|
||||||
sizeof(Timestamp) * sort_pks.size() + CalcPksSize(sort_pks);
|
|
||||||
return SegcoreError::success();
|
return SegcoreError::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,11 @@ class SegmentSealedImpl : public SegmentSealed {
|
||||||
GetFieldDataType(FieldId fieldId) const override;
|
GetFieldDataType(FieldId fieldId) const override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
size_t
|
||||||
|
GetMemoryUsageInBytes() const override {
|
||||||
|
return stats_.mem_size.load() + deleted_record_.mem_size();
|
||||||
|
}
|
||||||
|
|
||||||
int64_t
|
int64_t
|
||||||
get_row_count() const override;
|
get_row_count() const override;
|
||||||
|
|
||||||
|
@ -302,6 +307,8 @@ class SegmentSealedImpl : public SegmentSealed {
|
||||||
SegcoreConfig segcore_config_;
|
SegcoreConfig segcore_config_;
|
||||||
std::unordered_map<FieldId, std::unique_ptr<VecIndexConfig>>
|
std::unordered_map<FieldId, std::unique_ptr<VecIndexConfig>>
|
||||||
vec_binlog_config_;
|
vec_binlog_config_;
|
||||||
|
|
||||||
|
SegmentStats stats_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
inline SegmentSealedUPtr
|
inline SegmentSealedUPtr
|
||||||
|
|
|
@ -1229,23 +1229,51 @@ TEST(Sealed, BF_Overflow) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Sealed, DeleteCount) {
|
TEST(Sealed, DeleteCount) {
|
||||||
auto schema = std::make_shared<Schema>();
|
{
|
||||||
auto pk = schema->AddDebugField("pk", DataType::INT64);
|
auto schema = std::make_shared<Schema>();
|
||||||
schema->set_primary_field_id(pk);
|
auto pk = schema->AddDebugField("pk", DataType::INT64);
|
||||||
auto segment = CreateSealedSegment(schema);
|
schema->set_primary_field_id(pk);
|
||||||
|
auto segment = CreateSealedSegment(schema);
|
||||||
|
|
||||||
int64_t c = 10;
|
int64_t c = 10;
|
||||||
auto offset = segment->get_deleted_count();
|
auto offset = segment->get_deleted_count();
|
||||||
ASSERT_EQ(offset, 0);
|
ASSERT_EQ(offset, 0);
|
||||||
|
|
||||||
Timestamp begin_ts = 100;
|
Timestamp begin_ts = 100;
|
||||||
auto tss = GenTss(c, begin_ts);
|
auto tss = GenTss(c, begin_ts);
|
||||||
auto pks = GenPKs(c, 0);
|
auto pks = GenPKs(c, 0);
|
||||||
auto status = segment->Delete(offset, c, pks.get(), tss.data());
|
auto status = segment->Delete(offset, c, pks.get(), tss.data());
|
||||||
ASSERT_TRUE(status.ok());
|
ASSERT_TRUE(status.ok());
|
||||||
|
|
||||||
auto cnt = segment->get_deleted_count();
|
// shouldn't be filtered for empty segment.
|
||||||
ASSERT_EQ(cnt, 0);
|
auto cnt = segment->get_deleted_count();
|
||||||
|
ASSERT_EQ(cnt, 10);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto schema = std::make_shared<Schema>();
|
||||||
|
auto pk = schema->AddDebugField("pk", DataType::INT64);
|
||||||
|
schema->set_primary_field_id(pk);
|
||||||
|
auto segment = CreateSealedSegment(schema);
|
||||||
|
|
||||||
|
int64_t c = 10;
|
||||||
|
auto dataset = DataGen(schema, c);
|
||||||
|
auto pks = dataset.get_col<int64_t>(pk);
|
||||||
|
SealedLoadFieldData(dataset, *segment);
|
||||||
|
|
||||||
|
auto offset = segment->get_deleted_count();
|
||||||
|
ASSERT_EQ(offset, 0);
|
||||||
|
|
||||||
|
auto iter = std::max_element(pks.begin(), pks.end());
|
||||||
|
auto delete_pks = GenPKs(c, *iter);
|
||||||
|
Timestamp begin_ts = 100;
|
||||||
|
auto tss = GenTss(c, begin_ts);
|
||||||
|
auto status = segment->Delete(offset, c, delete_pks.get(), tss.data());
|
||||||
|
ASSERT_TRUE(status.ok());
|
||||||
|
|
||||||
|
// 9 of element should be filtered.
|
||||||
|
auto cnt = segment->get_deleted_count();
|
||||||
|
ASSERT_EQ(cnt, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Sealed, RealCount) {
|
TEST(Sealed, RealCount) {
|
||||||
|
|
|
@ -654,35 +654,39 @@ func (loader *segmentLoader) Load(ctx context.Context,
|
||||||
newSegments.Insert(loadInfo.GetSegmentID(), segment)
|
newSegments.Insert(loadInfo.GetSegmentID(), segment)
|
||||||
}
|
}
|
||||||
|
|
||||||
loadSegmentFunc := func(idx int) error {
|
loadSegmentFunc := func(idx int) (err error) {
|
||||||
loadInfo := infos[idx]
|
loadInfo := infos[idx]
|
||||||
partitionID := loadInfo.PartitionID
|
partitionID := loadInfo.PartitionID
|
||||||
segmentID := loadInfo.SegmentID
|
segmentID := loadInfo.SegmentID
|
||||||
segment, _ := newSegments.Get(segmentID)
|
segment, _ := newSegments.Get(segmentID)
|
||||||
|
|
||||||
|
logger := log.With(zap.Int64("partitionID", partitionID),
|
||||||
|
zap.Int64("segmentID", segmentID),
|
||||||
|
zap.String("segmentType", loadInfo.GetLevel().String()))
|
||||||
metrics.QueryNodeLoadSegmentConcurrency.WithLabelValues(fmt.Sprint(paramtable.GetNodeID()), "LoadSegment").Inc()
|
metrics.QueryNodeLoadSegmentConcurrency.WithLabelValues(fmt.Sprint(paramtable.GetNodeID()), "LoadSegment").Inc()
|
||||||
defer metrics.QueryNodeLoadSegmentConcurrency.WithLabelValues(fmt.Sprint(paramtable.GetNodeID()), "LoadSegment").Dec()
|
defer func() {
|
||||||
|
metrics.QueryNodeLoadSegmentConcurrency.WithLabelValues(fmt.Sprint(paramtable.GetNodeID()), "LoadSegment").Dec()
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn("load segment failed when load data into memory", zap.Error(err))
|
||||||
|
}
|
||||||
|
logger.Info("load segment done")
|
||||||
|
}()
|
||||||
tr := timerecord.NewTimeRecorder("loadDurationPerSegment")
|
tr := timerecord.NewTimeRecorder("loadDurationPerSegment")
|
||||||
|
logger.Info("load segment...")
|
||||||
|
|
||||||
var err error
|
// L0 segment has no index or data to be load
|
||||||
if loadInfo.GetLevel() == datapb.SegmentLevel_L0 {
|
if loadInfo.GetLevel() != datapb.SegmentLevel_L0 {
|
||||||
err = loader.LoadDeltaLogs(ctx, segment, loadInfo.GetDeltalogs())
|
if err = loader.LoadSegment(ctx, segment.(*LocalSegment), loadInfo, loadStatus); err != nil {
|
||||||
} else {
|
return errors.Wrap(err, "At LoadSegment")
|
||||||
err = loader.LoadSegment(ctx, segment.(*LocalSegment), loadInfo, loadStatus)
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err = loader.LoadDeltaLogs(ctx, segment, loadInfo.GetDeltalogs()); err != nil {
|
||||||
log.Warn("load segment failed when load data into memory",
|
return errors.Wrap(err, "At LoadDeltaLogs")
|
||||||
zap.Int64("partitionID", partitionID),
|
|
||||||
zap.Int64("segmentID", segmentID),
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
loader.manager.Segment.Put(segmentType, segment)
|
loader.manager.Segment.Put(segmentType, segment)
|
||||||
newSegments.GetAndRemove(segmentID)
|
newSegments.GetAndRemove(segmentID)
|
||||||
loaded.Insert(segmentID, segment)
|
loaded.Insert(segmentID, segment)
|
||||||
log.Info("load segment done", zap.Int64("segmentID", segmentID))
|
|
||||||
loader.notifyLoadFinish(loadInfo)
|
loader.notifyLoadFinish(loadInfo)
|
||||||
|
|
||||||
metrics.QueryNodeLoadSegmentLatency.WithLabelValues(fmt.Sprint(paramtable.GetNodeID())).Observe(float64(tr.ElapseSpan().Milliseconds()))
|
metrics.QueryNodeLoadSegmentLatency.WithLabelValues(fmt.Sprint(paramtable.GetNodeID())).Observe(float64(tr.ElapseSpan().Milliseconds()))
|
||||||
|
@ -1096,8 +1100,7 @@ func (loader *segmentLoader) LoadSegment(ctx context.Context,
|
||||||
strconv.FormatInt(int64(len(segment.Indexes())), 10),
|
strconv.FormatInt(int64(len(segment.Indexes())), 10),
|
||||||
).Add(float64(loadInfo.GetNumOfRows()))
|
).Add(float64(loadInfo.GetNumOfRows()))
|
||||||
|
|
||||||
log.Info("loading delta...")
|
return nil
|
||||||
return loader.LoadDeltaLogs(ctx, segment, loadInfo.Deltalogs)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (loader *segmentLoader) filterPKStatsBinlogs(fieldBinlogs []*datapb.FieldBinlog, pkFieldID int64) ([]string, storage.StatsLogType) {
|
func (loader *segmentLoader) filterPKStatsBinlogs(fieldBinlogs []*datapb.FieldBinlog, pkFieldID int64) ([]string, storage.StatsLogType) {
|
||||||
|
@ -1279,7 +1282,10 @@ func (loader *segmentLoader) LoadDeltaLogs(ctx context.Context, segment Segment,
|
||||||
defer sp.End()
|
defer sp.End()
|
||||||
log := log.Ctx(ctx).With(
|
log := log.Ctx(ctx).With(
|
||||||
zap.Int64("segmentID", segment.ID()),
|
zap.Int64("segmentID", segment.ID()),
|
||||||
|
zap.Int("deltaNum", len(deltaLogs)),
|
||||||
)
|
)
|
||||||
|
log.Info("loading delta...")
|
||||||
|
|
||||||
dCodec := storage.DeleteCodec{}
|
dCodec := storage.DeleteCodec{}
|
||||||
var blobs []*storage.Blob
|
var blobs []*storage.Blob
|
||||||
var futures []*conc.Future[any]
|
var futures []*conc.Future[any]
|
||||||
|
|
Loading…
Reference in New Issue