|
@@ -5,6 +5,7 @@
|
|
|
#include "datashard_locks_db.h"
|
|
|
#include "probes.h"
|
|
|
|
|
|
+#include <ydb/core/base/counters.h>
|
|
|
#include <ydb/core/formats/arrow/arrow_batch_builder.h>
|
|
|
|
|
|
#include <ydb/library/actors/core/monotonic_provider.h>
|
|
@@ -315,6 +316,8 @@ public:
|
|
|
, Self(self)
|
|
|
, TableId(state.PathId.OwnerId, state.PathId.LocalPathId, state.SchemaVersion)
|
|
|
, FirstUnprocessedQuery(State.FirstUnprocessedQuery)
|
|
|
+ , LastProcessedKey(State.LastProcessedKey)
|
|
|
+ , LastProcessedKeyErasedOrMissing(State.LastProcessedKeyErasedOrMissing)
|
|
|
{
|
|
|
GetTimeFast(&StartTime);
|
|
|
EndTime = StartTime;
|
|
@@ -329,10 +332,10 @@ public:
|
|
|
bool toInclusive;
|
|
|
TSerializedCellVec keyFromCells;
|
|
|
TSerializedCellVec keyToCells;
|
|
|
- if (Y_UNLIKELY(FirstUnprocessedQuery == State.FirstUnprocessedQuery && State.LastProcessedKey)) {
|
|
|
+ if (LastProcessedKey) {
|
|
|
if (!State.Reverse) {
|
|
|
- keyFromCells = TSerializedCellVec(State.LastProcessedKey);
|
|
|
- fromInclusive = State.LastProcessedKeyErasedOrMissing;
|
|
|
+ keyFromCells = TSerializedCellVec(LastProcessedKey);
|
|
|
+ fromInclusive = LastProcessedKeyErasedOrMissing;
|
|
|
|
|
|
keyToCells = range.To;
|
|
|
toInclusive = range.ToInclusive;
|
|
@@ -341,8 +344,8 @@ public:
|
|
|
keyFromCells = range.From;
|
|
|
fromInclusive = range.FromInclusive;
|
|
|
|
|
|
- keyToCells = TSerializedCellVec(State.LastProcessedKey);
|
|
|
- toInclusive = State.LastProcessedKeyErasedOrMissing;
|
|
|
+ keyToCells = TSerializedCellVec(LastProcessedKey);
|
|
|
+ toInclusive = LastProcessedKeyErasedOrMissing;
|
|
|
}
|
|
|
} else {
|
|
|
keyFromCells = range.From;
|
|
@@ -505,6 +508,7 @@ public:
|
|
|
while (FirstUnprocessedQuery < State.Request->Ranges.size()) {
|
|
|
if (ReachedTotalRowsLimit()) {
|
|
|
FirstUnprocessedQuery = -1;
|
|
|
+ LastProcessedKey.clear();
|
|
|
return true;
|
|
|
}
|
|
|
|
|
@@ -531,6 +535,7 @@ public:
|
|
|
FirstUnprocessedQuery++;
|
|
|
else
|
|
|
FirstUnprocessedQuery--;
|
|
|
+ LastProcessedKey.clear();
|
|
|
}
|
|
|
|
|
|
return true;
|
|
@@ -542,6 +547,7 @@ public:
|
|
|
while (FirstUnprocessedQuery < State.Request->Keys.size()) {
|
|
|
if (ReachedTotalRowsLimit()) {
|
|
|
FirstUnprocessedQuery = -1;
|
|
|
+ LastProcessedKey.clear();
|
|
|
return true;
|
|
|
}
|
|
|
|
|
@@ -567,6 +573,7 @@ public:
|
|
|
FirstUnprocessedQuery++;
|
|
|
else
|
|
|
FirstUnprocessedQuery--;
|
|
|
+ LastProcessedKey.clear();
|
|
|
}
|
|
|
|
|
|
return true;
|
|
@@ -732,6 +739,28 @@ public:
|
|
|
}
|
|
|
|
|
|
void UpdateState(TReadIteratorState& state, bool sentResult) {
|
|
|
+ if (state.FirstUnprocessedQuery == FirstUnprocessedQuery &&
|
|
|
+ state.LastProcessedKey && !LastProcessedKey)
|
|
|
+ {
|
|
|
+ LOG_CRIT_S(*TlsActivationContext, NKikimrServices::TX_DATASHARD,
|
|
|
+ "DataShard " << Self->TabletID() << " detected unexpected reset of LastProcessedKey:"
|
|
|
+ << " ReadId# " << State.ReadId
|
|
|
+ << " LastSeqNo# " << State.SeqNo
|
|
|
+ << " LastQuery# " << State.FirstUnprocessedQuery
|
|
|
+ << " RowsRead# " << RowsRead
|
|
|
+ << " RowsProcessed# " << RowsProcessed
|
|
|
+ << " RowsSinceLastCheck# " << RowsSinceLastCheck
|
|
|
+ << " BytesInResult# " << BytesInResult
|
|
|
+ << " DeletedRowSkips# " << DeletedRowSkips
|
|
|
+ << " InvisibleRowSkips# " << InvisibleRowSkips
|
|
|
+ << " Quota.Rows# " << State.Quota.Rows
|
|
|
+ << " Quota.Bytes# " << State.Quota.Bytes
|
|
|
+ << " State.TotalRows# " << State.TotalRows
|
|
|
+ << " State.TotalRowsLimit# " << State.TotalRowsLimit
|
|
|
+ << " State.MaxRowsInResult# " << State.MaxRowsInResult);
|
|
|
+ Self->IncCounterReadIteratorLastKeyReset();
|
|
|
+ }
|
|
|
+
|
|
|
state.TotalRows += RowsRead;
|
|
|
state.FirstUnprocessedQuery = FirstUnprocessedQuery;
|
|
|
state.LastProcessedKey = LastProcessedKey;
|
|
@@ -1683,6 +1712,7 @@ public:
|
|
|
if (Reader->HasUnreadQueries()) {
|
|
|
Reader->UpdateState(state, ResultSent);
|
|
|
if (!state.IsExhausted()) {
|
|
|
+ state.ReadContinuePending = true;
|
|
|
ctx.Send(
|
|
|
Self->SelfId(),
|
|
|
new TEvDataShard::TEvReadContinue(ReadId.Sender, ReadId.ReadId));
|
|
@@ -2333,6 +2363,15 @@ public:
|
|
|
Y_ASSERT(it->second);
|
|
|
auto& state = *it->second;
|
|
|
|
|
|
+ if (state.IsExhausted()) {
|
|
|
+ // iterator quota reduced and exhausted while ReadContinue was inflight
|
|
|
+ LOG_TRACE_S(ctx, NKikimrServices::TX_DATASHARD, Self->TabletID() << " ReadContinue for iterator# " << ReadId
|
|
|
+ << ", quota exhausted while rescheduling");
|
|
|
+ state.ReadContinuePending = false;
|
|
|
+ Result.reset();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
LOG_TRACE_S(ctx, NKikimrServices::TX_DATASHARD, Self->TabletID() << " ReadContinue for iterator# " << ReadId
|
|
|
<< ", firstUnprocessedQuery# " << state.FirstUnprocessedQuery);
|
|
|
|
|
@@ -2446,6 +2485,7 @@ public:
|
|
|
if (Reader->Read(txc, ctx)) {
|
|
|
// Retry later when dependencies are resolved
|
|
|
if (!Reader->GetVolatileReadDependencies().empty()) {
|
|
|
+ state.ReadContinuePending = true;
|
|
|
Self->WaitVolatileDependenciesThenSend(
|
|
|
Reader->GetVolatileReadDependencies(),
|
|
|
Self->SelfId(),
|
|
@@ -2532,6 +2572,8 @@ public:
|
|
|
Y_ABORT_UNLESS(it->second);
|
|
|
auto& state = *it->second;
|
|
|
|
|
|
+ state.ReadContinuePending = false;
|
|
|
+
|
|
|
if (!Result) {
|
|
|
LOG_DEBUG_S(ctx, NKikimrServices::TX_DATASHARD, Self->TabletID() << " read iterator# " << ReadId
|
|
|
<< " TTxReadContinue::Execute() finished without Result, aborting");
|
|
@@ -2579,14 +2621,14 @@ public:
|
|
|
}
|
|
|
|
|
|
if (Reader->HasUnreadQueries()) {
|
|
|
- Y_ASSERT(it->second);
|
|
|
- auto& state = *it->second;
|
|
|
+ bool wasExhausted = state.IsExhausted();
|
|
|
Reader->UpdateState(state, useful);
|
|
|
if (!state.IsExhausted()) {
|
|
|
+ state.ReadContinuePending = true;
|
|
|
ctx.Send(
|
|
|
Self->SelfId(),
|
|
|
new TEvDataShard::TEvReadContinue(ReadId.Sender, ReadId.ReadId));
|
|
|
- } else {
|
|
|
+ } else if (!wasExhausted) {
|
|
|
Self->IncCounter(COUNTER_READ_ITERATORS_EXHAUSTED_COUNT);
|
|
|
LOG_DEBUG_S(ctx, NKikimrServices::TX_DATASHARD, Self->TabletID()
|
|
|
<< " read iterator# " << ReadId << " exhausted");
|
|
@@ -2859,14 +2901,19 @@ void TDataShard::Handle(TEvDataShard::TEvReadAck::TPtr& ev, const TActorContext&
|
|
|
bool wasExhausted = state.IsExhausted();
|
|
|
state.UpQuota(
|
|
|
record.GetSeqNo(),
|
|
|
- record.GetMaxRows(),
|
|
|
- record.GetMaxBytes());
|
|
|
+ record.HasMaxRows() ? record.GetMaxRows() : Max<ui64>(),
|
|
|
+ record.HasMaxBytes() ? record.GetMaxBytes() : Max<ui64>());
|
|
|
|
|
|
if (wasExhausted && !state.IsExhausted()) {
|
|
|
DecCounter(COUNTER_READ_ITERATORS_EXHAUSTED_COUNT);
|
|
|
- ctx.Send(
|
|
|
- SelfId(),
|
|
|
- new TEvDataShard::TEvReadContinue(ev->Sender, record.GetReadId()));
|
|
|
+ if (!state.ReadContinuePending) {
|
|
|
+ state.ReadContinuePending = true;
|
|
|
+ ctx.Send(
|
|
|
+ SelfId(),
|
|
|
+ new TEvDataShard::TEvReadContinue(ev->Sender, record.GetReadId()));
|
|
|
+ }
|
|
|
+ } else if (!wasExhausted && state.IsExhausted()) {
|
|
|
+ IncCounter(COUNTER_READ_ITERATORS_EXHAUSTED_COUNT);
|
|
|
}
|
|
|
|
|
|
LOG_TRACE_S(ctx, NKikimrServices::TX_DATASHARD, TabletID() << " ReadAck for read iterator# " << readId
|
|
@@ -2995,6 +3042,16 @@ void TDataShard::UnsubscribeReadIteratorSessions(const TActorContext& ctx) {
|
|
|
ReadIteratorSessions.clear();
|
|
|
}
|
|
|
|
|
|
+void TDataShard::IncCounterReadIteratorLastKeyReset() {
|
|
|
+ if (!CounterReadIteratorLastKeyReset) {
|
|
|
+ CounterReadIteratorLastKeyReset = GetServiceCounters(AppData()->Counters, "tablets")
|
|
|
+ ->GetSubgroup("type", "DataShard")
|
|
|
+ ->GetSubgroup("category", "app")
|
|
|
+ ->GetCounter("DataShard/ReadIteratorLastKeyReset", true);
|
|
|
+ }
|
|
|
+ ++*CounterReadIteratorLastKeyReset;
|
|
|
+}
|
|
|
+
|
|
|
} // NKikimr::NDataShard
|
|
|
|
|
|
template<>
|