@@ -31,7 +31,10 @@ namespace NKikimr::NKqp {
31
31
namespace {
32
32
33
33
constexpr ui32 LEASE_UPDATE_FREQUENCY = 2 ;
34
- constexpr ui32 MAX_SAVE_RESULT_IN_FLIGHT = 1 ;
34
+
35
+ constexpr ui64 MIN_SAVE_RESULT_BATCH_SIZE = 5_MB;
36
+ constexpr i32 MIN_SAVE_RESULT_BATCH_ROWS = 5000 ;
37
+ constexpr ui64 RUN_SCRIPT_ACTOR_BUFFER_SIZE = 40_MB;
35
38
36
39
class TRunScriptActor : public NActors ::TActorBootstrapped<TRunScriptActor> {
37
40
enum class ERunState {
@@ -48,14 +51,20 @@ class TRunScriptActor : public NActors::TActorBootstrapped<TRunScriptActor> {
48
51
UpdateLeaseEvent,
49
52
};
50
53
51
- struct TPendingSaveResult {
52
- ui32 ResultSetIndex;
53
- ui64 FirstRow;
54
- ui64 AccumulatedSize;
55
- Ydb::ResultSet ResultSet;
54
+ struct TResultSetInfo {
55
+ bool Truncated = false ;
56
+ ui64 RowCount = 0 ;
57
+ ui64 ByteCount = 0 ;
58
+ NJson::TJsonValue* Meta;
59
+
60
+ ui64 FirstRowId = 0 ;
61
+ ui64 AccumulatedSize = 0 ;
62
+ Ydb::ResultSet PendingResult;
63
+ };
56
64
65
+ struct TPendingAck {
57
66
TActorId ReplyActorId;
58
- THolder<TEvKqpExecuter::TEvStreamDataAck> SaveResultResponse ;
67
+ THolder<TEvKqpExecuter::TEvStreamDataAck> AckEvent ;
59
68
};
60
69
61
70
public:
@@ -255,29 +264,55 @@ class TRunScriptActor : public NActors::TActorBootstrapped<TRunScriptActor> {
255
264
PassAway ();
256
265
}
257
266
258
- void SendStreamDataResponse (TActorId replyActorId, THolder<TEvKqpExecuter::TEvStreamDataAck> saveResultResponse) const {
259
- LOG_D (" Send stream data ack"
260
- << " , seqNo: " << saveResultResponse->Record .GetSeqNo ()
261
- << " , to: " << replyActorId);
267
+ void SendStreamDataResponse () {
268
+ if (PendingAcks.empty ()) {
269
+ return ;
270
+ }
271
+
272
+ if (PendingResultSetsSize > RUN_SCRIPT_ACTOR_BUFFER_SIZE) {
273
+ // Try to save any pending result
274
+ SaveResult ();
275
+ }
262
276
263
- Send (replyActorId, saveResultResponse.Release ());
277
+ if (PendingResultSetsSize <= RUN_SCRIPT_ACTOR_BUFFER_SIZE) {
278
+ while (!PendingAcks.empty ()) {
279
+ auto response = std::move (PendingAcks.front ());
280
+ PendingAcks.pop ();
281
+
282
+ LOG_D (" Send stream data ack"
283
+ << " , seqNo: " << response.AckEvent ->Record .GetSeqNo ()
284
+ << " , to: " << response.ReplyActorId );
285
+
286
+ Send (response.ReplyActorId , response.AckEvent .Release ());
287
+ }
288
+ }
264
289
}
265
290
266
- void SaveResult () {
267
- if (SaveResultInflight >= MAX_SAVE_RESULT_IN_FLIGHT || PendingSaveResults. empty () ) {
291
+ void SaveResult (size_t resultSetId ) {
292
+ if (SaveResultInflight) {
268
293
return ;
269
294
}
270
295
271
296
if (!ExpireAt && ResultsTtl > TDuration::Zero ()) {
272
297
ExpireAt = TInstant::Now () + ResultsTtl;
273
298
}
274
299
275
- TPendingSaveResult& result = PendingSaveResults.back ();
276
- Register (CreateSaveScriptExecutionResultActor (SelfId (), Database, ExecutionId, result.ResultSetIndex , ExpireAt, result.FirstRow , result.AccumulatedSize , std::move (result.ResultSet )));
277
- SendStreamDataResponse (result.ReplyActorId , std::move (result.SaveResultResponse ));
278
-
279
- PendingSaveResults.pop_back ();
300
+ auto & resultSetInfo = ResultSetInfos[resultSetId];
301
+ Register (CreateSaveScriptExecutionResultActor (SelfId (), Database, ExecutionId, resultSetId, ExpireAt, resultSetInfo.FirstRowId , resultSetInfo.AccumulatedSize , std::move (resultSetInfo.PendingResult )));
280
302
SaveResultInflight++;
303
+ PendingResultSetsSize -= resultSetInfo.ByteCount - resultSetInfo.AccumulatedSize ;
304
+ resultSetInfo.FirstRowId = resultSetInfo.RowCount ;
305
+ resultSetInfo.AccumulatedSize = resultSetInfo.ByteCount ;
306
+ resultSetInfo.PendingResult = Ydb::ResultSet ();
307
+ }
308
+
309
+ void SaveResult () {
310
+ for (size_t resultSetId = 0 ; resultSetId < ResultSetInfos.size (); ++resultSetId) {
311
+ if (ResultSetInfos[resultSetId].PendingResult .rows_size ()) {
312
+ SaveResult (resultSetId);
313
+ break ;
314
+ }
315
+ }
281
316
}
282
317
283
318
void Handle (TEvKqpExecuter::TEvStreamData::TPtr& ev) {
@@ -299,57 +334,52 @@ class TRunScriptActor : public NActors::TActorBootstrapped<TRunScriptActor> {
299
334
300
335
auto resultSetIndex = ev->Get ()->Record .GetQueryResultIndex ();
301
336
302
- if (resultSetIndex >= ResultSetMetaArray .size ()) {
337
+ if (resultSetIndex >= ResultSetInfos .size ()) {
303
338
// we don't know result set count, so just accept all of them
304
339
// it's possible to have several result sets per script
305
340
// they can arrive in any order and may be missed for some indices
306
- ResultSetRowCount.resize (resultSetIndex + 1 );
307
- ResultSetByteCount.resize (resultSetIndex + 1 );
308
- Truncated.resize (resultSetIndex + 1 );
309
- ResultSetMetaArray.resize (resultSetIndex + 1 , nullptr );
341
+ ResultSetInfos.resize (resultSetIndex + 1 );
310
342
}
311
343
312
- bool saveResultRequired = false ;
313
- if (IsExecuting () && !Truncated[resultSetIndex]) {
314
- auto & rowCount = ResultSetRowCount[resultSetIndex];
315
- auto & byteCount = ResultSetByteCount[resultSetIndex];
316
- auto firstRow = rowCount;
317
- auto accumulatedSize = byteCount;
344
+ auto & resultSetInfo = ResultSetInfos[resultSetIndex];
345
+ if (IsExecuting () && !resultSetInfo.Truncated ) {
346
+ auto & rowCount = resultSetInfo.RowCount ;
347
+ auto & byteCount = resultSetInfo.ByteCount ;
318
348
319
- Ydb::ResultSet resultSet;
320
349
for (auto & row : *ev->Get ()->Record .MutableResultSet ()->mutable_rows ()) {
321
350
if (QueryServiceConfig.GetScriptResultRowsLimit () && rowCount + 1 > QueryServiceConfig.GetScriptResultRowsLimit ()) {
322
- Truncated[resultSetIndex] = true ;
351
+ resultSetInfo. Truncated = true ;
323
352
break ;
324
353
}
325
354
326
355
auto serializedSize = row.ByteSizeLong ();
327
356
if (QueryServiceConfig.GetScriptResultSizeLimit () && byteCount + serializedSize > QueryServiceConfig.GetScriptResultSizeLimit ()) {
328
- Truncated[resultSetIndex] = true ;
357
+ resultSetInfo. Truncated = true ;
329
358
break ;
330
359
}
331
360
332
361
rowCount++;
333
362
byteCount += serializedSize;
334
- *resultSet.add_rows () = std::move (row);
363
+ PendingResultSetsSize += serializedSize;
364
+ *resultSetInfo.PendingResult .add_rows () = std::move (row);
335
365
}
336
366
337
- bool newResultSet = ResultSetMetaArray[resultSetIndex] == nullptr ;
338
- if (newResultSet || Truncated[resultSetIndex] ) {
367
+ bool newResultSet = resultSetInfo. Meta == nullptr ;
368
+ if (newResultSet || resultSetInfo. Truncated ) {
339
369
Ydb::Query::Internal::ResultSetMeta meta;
340
370
if (newResultSet) {
341
371
*meta.mutable_columns () = ev->Get ()->Record .GetResultSet ().columns ();
342
372
}
343
- if (Truncated[resultSetIndex] ) {
373
+ if (resultSetInfo. Truncated ) {
344
374
meta.set_truncated (true );
345
375
}
346
376
347
377
NJson::TJsonValue* value;
348
378
if (newResultSet) {
349
379
value = &ResultSetMetas[resultSetIndex];
350
- ResultSetMetaArray[resultSetIndex] = value;
380
+ resultSetInfo. Meta = value;
351
381
} else {
352
- value = ResultSetMetaArray[resultSetIndex] ;
382
+ value = resultSetInfo. Meta ;
353
383
}
354
384
NProtobufJson::Proto2Json (meta, *value, NProtobufJson::TProto2JsonConfig ());
355
385
@@ -362,24 +392,13 @@ class TRunScriptActor : public NActors::TActorBootstrapped<TRunScriptActor> {
362
392
}
363
393
}
364
394
365
- if (resultSet.rows_size () > 0 ) {
366
- saveResultRequired = true ;
367
- PendingSaveResults.push_back ({
368
- resultSetIndex,
369
- firstRow,
370
- accumulatedSize,
371
- std::move (resultSet),
372
- ev->Sender ,
373
- std::move (resp)
374
- });
395
+ if (ShouldSaveResult (resultSetInfo)) {
396
+ SaveResult (resultSetIndex);
375
397
}
376
398
}
377
399
378
- if (saveResultRequired) {
379
- SaveResult ();
380
- } else {
381
- SendStreamDataResponse (ev->Sender , std::move (resp));
382
- }
400
+ PendingAcks.push ({.ReplyActorId = ev->Sender , .AckEvent = std::move (resp)});
401
+ SendStreamDataResponse ();
383
402
}
384
403
385
404
void SaveResultMeta () {
@@ -504,9 +523,15 @@ class TRunScriptActor : public NActors::TActorBootstrapped<TRunScriptActor> {
504
523
Status = ev->Get ()->Status ;
505
524
Issues.AddIssues (ev->Get ()->Issues );
506
525
} else {
507
- SaveResult ();
526
+ for (size_t resultSetId = 0 ; resultSetId < ResultSetInfos.size (); ++resultSetId) {
527
+ if (ShouldSaveResult (ResultSetInfos[resultSetId])) {
528
+ SaveResult (resultSetId);
529
+ break ;
530
+ }
531
+ }
508
532
}
509
533
}
534
+ SendStreamDataResponse ();
510
535
CheckInflight ();
511
536
}
512
537
@@ -533,6 +558,12 @@ class TRunScriptActor : public NActors::TActorBootstrapped<TRunScriptActor> {
533
558
return ;
534
559
}
535
560
561
+ if (PendingResultSetsSize) {
562
+ // Complete results saving
563
+ SaveResult ();
564
+ return ;
565
+ }
566
+
536
567
if (!LeaseUpdateQueryRunning) {
537
568
RunScriptExecutionFinisher ();
538
569
} else {
@@ -545,7 +576,7 @@ class TRunScriptActor : public NActors::TActorBootstrapped<TRunScriptActor> {
545
576
Status = status;
546
577
547
578
// if query has no results, save empty json array
548
- if (ResultSetMetaArray .empty ()) {
579
+ if (ResultSetInfos .empty ()) {
549
580
ResultSetMetas.SetType (NJson::JSON_ARRAY);
550
581
SaveResultMeta ();
551
582
SaveResultMetaInflight++;
@@ -566,6 +597,13 @@ class TRunScriptActor : public NActors::TActorBootstrapped<TRunScriptActor> {
566
597
&& RunState != ERunState::Cancelling;
567
598
}
568
599
600
+ static bool ShouldSaveResult (TResultSetInfo& resultInfo) {
601
+ if (!resultInfo.PendingResult .rows_size ()) {
602
+ return false ;
603
+ }
604
+ return resultInfo.Truncated || resultInfo.PendingResult .rows_size () >= MIN_SAVE_RESULT_BATCH_ROWS || resultInfo.ByteCount - resultInfo.AccumulatedSize >= MIN_SAVE_RESULT_BATCH_SIZE;
605
+ }
606
+
569
607
private:
570
608
const TString ExecutionId;
571
609
NKikimrKqp::TEvQueryRequest Request;
@@ -589,16 +627,14 @@ class TRunScriptActor : public NActors::TActorBootstrapped<TRunScriptActor> {
589
627
Ydb::StatusIds::StatusCode Status = Ydb::StatusIds::STATUS_CODE_UNSPECIFIED;
590
628
591
629
// Result
592
- std::vector<TPendingSaveResult> PendingSaveResults;
593
- std::vector<ui64> ResultSetRowCount;
594
- std::vector<ui64> ResultSetByteCount;
595
- std::vector<bool > Truncated;
596
- std::vector<NJson::TJsonValue*> ResultSetMetaArray;
630
+ std::vector<TResultSetInfo> ResultSetInfos;
631
+ std::queue<TPendingAck> PendingAcks;
597
632
TMaybe<TInstant> ExpireAt;
598
633
NJson::TJsonValue ResultSetMetas;
599
634
ui32 SaveResultInflight = 0 ;
600
635
ui32 SaveResultMetaInflight = 0 ;
601
636
bool PendingResultMeta = false ;
637
+ ui64 PendingResultSetsSize = 0 ;
602
638
std::optional<TString> QueryPlan;
603
639
std::optional<TString> QueryAst;
604
640
std::optional<NKqpProto::TKqpStatsQuery> QueryStats;
0 commit comments