77#include < ydb/core/control/immediate_control_board_impl.h>
88#include < ydb/core/keyvalue/keyvalue_events.h>
99
10+ #include < library/cpp/histogram/hdr/histogram.h>
1011#include < library/cpp/monlib/service/pages/templates.h>
1112#include < library/cpp/time_provider/time_provider.h>
1213
1718namespace NKikimr {
1819class TKeyValueWriterLoadTestActor ;
1920
20- #define PARAM (NAME, VALUE ) \
21- TABLER () { \
22- TABLED () { str << NAME; } \
23- TABLED () { str << VALUE; } \
24- }
25-
2621class TWorker {
2722 friend class TKeyValueWriterLoadTestActor ;
2823
@@ -39,6 +34,10 @@ class TWorker {
3934 TString DataBuffer;
4035 TReallyFastRng32 *Gen;
4136 bool IsDying = false ;
37+
38+ ui64 Errors = 0 ;
39+ ui64 OutOfBoundsLatencies = 0 ;
40+ NHdr::THistogram LatencyHistogram{6'000'000 , 4 };
4241public:
4342
4443 TWorker (const NKikimr::TEvLoadTestRequest::TKeyValueLoad::TWorkerConfig& cmd,
@@ -87,12 +86,23 @@ class TWorker {
8786 return ev;
8887 }
8988
90- void OnResult (ui32 size) {
91- --ItemsInFlight;
92- BytesInFlight -= size;
89+ void OnSuccess (ui32 size, TDuration responseTime) {
90+ ReduceInFlight (size);
91+ if (!LatencyHistogram.RecordValue (responseTime.MicroSeconds ())) {
92+ LOG_INFO_S (*NActors::TActivationContext::ActorSystem (), NKikimrServices::BS_LOAD_TEST, " Worker# " << Idx << " skipped recording of " << responseTime << " response time" );
93+ ++OutOfBoundsLatencies;
94+ }
9395 }
9496
95- ~TWorker () {
97+ void OnFailure (ui32 size) {
98+ ReduceInFlight (size);
99+ ++Errors;
100+ }
101+
102+ private:
103+ void ReduceInFlight (ui32 size) {
104+ --ItemsInFlight;
105+ BytesInFlight -= size;
96106 }
97107};
98108
@@ -123,6 +133,7 @@ class TKeyValueWriterLoadTestActor : public TActorBootstrapped<TKeyValueWriterLo
123133 ui64 ReqIdx = 0 ;
124134 ui32 DurationSeconds;
125135 i32 OwnerInitInProgress = 0 ;
136+ TString ConfigString;
126137
127138 TReallyFastRng32 Rng;
128139
@@ -170,6 +181,8 @@ class TKeyValueWriterLoadTestActor : public TActorBootstrapped<TKeyValueWriterLo
170181 GetSubgroup (" tablet" , Sprintf (" %09" PRIu64, TabletId));
171182 KeyValueBytesWritten = LoadCounters->GetCounter (" KeyValueBytesWritten" , true );
172183 ResponseTimes.Initialize (LoadCounters, " subsystem" , " LoadActorLogWriteDuration" , " Time in microseconds" , percentiles);
184+
185+ google::protobuf::TextFormat::PrintToString (cmd, &ConfigString);
173186 }
174187
175188 ~TKeyValueWriterLoadTestActor () {
@@ -198,9 +211,9 @@ class TKeyValueWriterLoadTestActor : public TActorBootstrapped<TKeyValueWriterLo
198211 for (auto & worker : Workers) {
199212 AppData (ctx)->Icb ->RegisterLocalControl (worker->MaxInFlight ,
200213 Sprintf (" KeyValueWriteLoadActor_MaxInFlight_%04" PRIu64 " _%04" PRIu32, Tag, worker->Idx ));
201- LOG_INFO_S (ctx, NKikimrServices::BS_LOAD_TEST, " Tag# " << Tag << " last TEvKeyValueResult, "
202- << " all workers is initialized, start test" );
203214 }
215+ LOG_INFO_S (ctx, NKikimrServices::BS_LOAD_TEST, " Tag# " << Tag << " last TEvKeyValueResult, "
216+ << " all workers are initialized, start test" );
204217 EarlyStop = false ;
205218 Connect (ctx);
206219 }
@@ -232,7 +245,10 @@ class TKeyValueWriterLoadTestActor : public TActorBootstrapped<TKeyValueWriterLo
232245 }
233246 const TString errorReason = EarlyStop ?
234247 " Abort, stop signal received" : " OK, called StartDeathProcess" ;
235- ctx.Send (Parent, new TEvLoad::TEvLoadTestFinished (Tag, report, errorReason));
248+ auto * finishEv = new TEvLoad::TEvLoadTestFinished (Tag, report, errorReason);
249+ finishEv->LastHtmlPage = RenderHTML (false );
250+
251+ ctx.Send (Parent, finishEv);
236252 NTabletPipe::CloseClient (SelfId (), Pipe);
237253 Die (ctx);
238254 }
@@ -258,8 +274,8 @@ class TKeyValueWriterLoadTestActor : public TActorBootstrapped<TKeyValueWriterLo
258274 void SendWriteRequests (const TActorContext& ctx) {
259275 ui64 sent = 0 ;
260276 for (auto & worker : Workers) {
261- auto now = TAppData::TimeProvider->Now ();
262277 while (std::unique_ptr<TEvKeyValue::TEvRequest> ev = worker->TrySend ()) {
278+ auto now = TAppData::TimeProvider->Now ();
263279 ui64 size = ev->Record .GetCmdWrite (0 ).GetValue ().size ();
264280 *KeyValueBytesWritten += size;
265281 ev->Record .SetCookie (ReqIdx);
@@ -275,24 +291,21 @@ class TKeyValueWriterLoadTestActor : public TActorBootstrapped<TKeyValueWriterLo
275291 void Handle (TEvKeyValue::TEvResponse::TPtr& ev, const TActorContext& ctx) {
276292 auto msg = ev->Get ();
277293 auto record = msg->Record ;
278- if (record.GetStatus () != NMsgBusProxy::MSTATUS_OK) {
279- TStringStream str;
280- str << " TEvKeyValue::TEvResponse is not OK, msg.ToString()# " << msg->ToString ();
281- LOG_ERROR_S (ctx, NKikimrServices::BS_LOAD_TEST, str.Str ());
282- ctx.Send (Parent, new TEvLoad::TEvLoadTestFinished (Tag, nullptr , str.Str ()));
283- NTabletPipe::CloseClient (SelfId (), Pipe);
284- Die (ctx);
285- return ;
286- }
287294
288- auto now = TAppData::TimeProvider->Now ();
289295 auto it = InFlightWrites.find (record.GetCookie ());
290296 Y_ABORT_UNLESS (it != InFlightWrites.end ());
291297 const auto & stats = it->second ;
292- ResponseTimes.Increment ((now - stats.SentTime ).MicroSeconds ());
298+ auto responseTime = TAppData::TimeProvider->Now () - stats.SentTime ;
299+ ResponseTimes.Increment (responseTime.MicroSeconds ());
293300 auto & worker = Workers[stats.WorkerIdx ];
294301
295- worker->OnResult (stats.Size );
302+ if (record.GetStatus () == NMsgBusProxy::MSTATUS_OK) {
303+ worker->OnSuccess (stats.Size , responseTime);
304+ } else {
305+ LOG_WARN_S (ctx, NKikimrServices::BS_LOAD_TEST, " TEvKeyValue::TEvResponse is not OK, msg.ToString()# " << msg->ToString ());
306+
307+ worker->OnFailure (stats.Size );
308+ }
296309 WrittenBytes = WrittenBytes + stats.Size ;
297310 LOG_TRACE_S (ctx, NKikimrServices::BS_LOAD_TEST, " Tag# " << Tag << " EvResult, "
298311 << " WrittenBytes# " << WrittenBytes);
@@ -301,29 +314,86 @@ class TKeyValueWriterLoadTestActor : public TActorBootstrapped<TKeyValueWriterLo
301314 SendWriteRequests (ctx);
302315 }
303316
304- void Handle (NMon::TEvHttpInfo::TPtr& ev, const TActorContext& ctx ) {
317+ TString RenderHTML ( bool showPassedTime ) {
305318 TStringStream str;
306319 HTML (str) {
307- TABLE () {
320+ if (showPassedTime) {
321+ PARA () {
322+ str << " Time passed: " << (TAppData::TimeProvider->Now () - TestStartTime).Seconds () << " s / "
323+ << DurationSeconds << " s" ;
324+ }
325+ }
326+ TABLE_CLASS (" table table-condenced" ) {
308327 TABLEHEAD () {
309328 TABLER () {
310- TABLEH () { str << " Parameter" ; }
311- TABLEH () { str << " Value" ; }
329+ TABLEH () {
330+ str << " Worker#" ;
331+ }
332+ TABLEH () {
333+ str << " Writes" ;
334+ }
335+ TABLEH () {
336+ str << " Errors" ;
337+ }
338+ TABLEH () {
339+ str << " OOB Latencies" ;
340+ }
341+ TABLEH () {
342+ str << " p50(ms)" ;
343+ }
344+ TABLEH () {
345+ str << " p95(ms)" ;
346+ }
347+ TABLEH () {
348+ str << " p99(ms)" ;
349+ }
350+ TABLEH () {
351+ str << " pMax(ms)" ;
352+ }
312353 }
313354 }
314355 TABLEBODY () {
315-
316- PARAM (" Elapsed time / Duration" , (TAppData::TimeProvider->Now () - TestStartTime).Seconds () << " s / "
317- << DurationSeconds << " s" );
318- for (auto & worker : Workers) {
319- PARAM (" Worker idx" , worker->Idx );
320- PARAM (" Worker next OperationIdx" , worker->OperationIdx );
356+ for (auto & worker: Workers) {
357+ TABLER () {
358+ TABLED () {
359+ str << worker->Idx ;
360+ }
361+ TABLED () {
362+ str << worker->LatencyHistogram .GetTotalCount ();
363+ }
364+ TABLED () {
365+ str << worker->Errors ;
366+ }
367+ TABLED () {
368+ str << worker->OutOfBoundsLatencies ;
369+ }
370+ TABLED () {
371+ str << worker->LatencyHistogram .GetValueAtPercentile (50.0 ) / 1000.0 ;
372+ }
373+ TABLED () {
374+ str << worker->LatencyHistogram .GetValueAtPercentile (95.0 ) / 1000.0 ;
375+ }
376+ TABLED () {
377+ str << worker->LatencyHistogram .GetValueAtPercentile (99.0 ) / 1000.0 ;
378+ }
379+ TABLED () {
380+ str << worker->LatencyHistogram .GetMax () / 1000.0 ;
381+ }
382+ }
321383 }
322384 }
323385 }
386+ COLLAPSED_BUTTON_CONTENT (Sprintf (" configProtobuf%" PRIu64, Tag), " Config" ) {
387+ PRE () {
388+ str << ConfigString;
389+ }
390+ }
324391 }
392+ return str.Str ();
393+ }
325394
326- ctx.Send (ev->Sender , new NMon::TEvHttpInfoRes (str.Str (), ev->Get ()->SubRequestId ));
395+ void Handle (NMon::TEvHttpInfo::TPtr& ev, const TActorContext& ctx) {
396+ ctx.Send (ev->Sender , new NMon::TEvHttpInfoRes (RenderHTML (true ), ev->Get ()->SubRequestId ));
327397 }
328398
329399 void Handle (TEvTabletPipe::TEvClientConnected::TPtr ev, const TActorContext& ctx) {
0 commit comments