1
1
#include " wilson_uploader.h"
2
+
2
3
#include < ydb/library/actors/core/actor_bootstrapped.h>
3
4
#include < ydb/library/actors/core/hfunc.h>
4
5
#include < ydb/library/actors/core/log.h>
7
8
#include < library/cpp/string_utils/url/url.h>
8
9
#include < util/stream/file.h>
9
10
#include < util/string/hex.h>
11
+
10
12
#include < chrono>
13
+ #include < queue>
11
14
12
15
namespace NWilson {
13
16
@@ -18,11 +21,82 @@ namespace NWilson {
18
21
19
22
namespace {
20
23
24
+ struct TSpanQueueItem {
25
+ TMonotonic ExpirationTimestamp;
26
+ NTraceProto::Span Span;
27
+ size_t Size;
28
+ };
29
+
30
+ class TBatch {
31
+ private:
32
+ ui64 MaxSpansInBatch;
33
+ ui64 MaxBytesInBatch;
34
+
35
+ NServiceProto::ExportTraceServiceRequest Request;
36
+ NTraceProto::ScopeSpans* ScopeSpans;
37
+ ui64 SizeBytes = 0 ;
38
+ TMonotonic ExpirationTimestamp = TMonotonic::Zero();
39
+
40
+ public:
41
+ struct TData {
42
+ NServiceProto::ExportTraceServiceRequest Request;
43
+ ui64 SizeBytes;
44
+ ui64 SizeSpans;
45
+ TMonotonic ExpirationTimestamp;
46
+ };
47
+
48
+ TBatch (ui64 maxSpansInBatch, ui64 maxBytesInBatch, TString serviceName)
49
+ : MaxSpansInBatch(maxSpansInBatch)
50
+ , MaxBytesInBatch(maxBytesInBatch)
51
+ {
52
+ auto *rspan = Request.add_resource_spans ();
53
+ auto *serviceNameAttr = rspan->mutable_resource ()->add_attributes ();
54
+ serviceNameAttr->set_key (" service.name" );
55
+ serviceNameAttr->mutable_value ()->set_string_value (std::move (serviceName));
56
+ ScopeSpans = rspan->add_scope_spans ();
57
+ }
58
+
59
+ size_t SizeSpans () const {
60
+ return ScopeSpans->spansSize ();
61
+ }
62
+
63
+ bool IsEmpty () const {
64
+ return SizeSpans () == 0 ;
65
+ }
66
+
67
+ bool Add (TSpanQueueItem& span) {
68
+ if (SizeBytes + span.Size > MaxBytesInBatch || SizeSpans () == MaxSpansInBatch) {
69
+ return false ;
70
+ }
71
+ SizeBytes += span.Size ;
72
+ span.Span .Swap (ScopeSpans->Addspans ());
73
+ ExpirationTimestamp = span.ExpirationTimestamp ;
74
+ return true ;
75
+ }
76
+
77
+ TData Complete () && {
78
+ return TData {
79
+ .Request = std::move (Request),
80
+ .SizeBytes = SizeBytes,
81
+ .SizeSpans = SizeSpans (),
82
+ .ExpirationTimestamp = ExpirationTimestamp,
83
+ };
84
+ }
85
+ };
86
+
21
87
class TWilsonUploader
22
88
: public TActorBootstrapped<TWilsonUploader>
23
89
{
24
90
static constexpr size_t WILSON_SERVICE_ID = 430 ;
25
91
92
+ ui64 MaxSpansInBatch = 150 ;
93
+ ui64 MaxBytesInBatch = 20'000'000 ;
94
+ ui64 MaxBatchAccumulationMilliseconds = 1'000 ;
95
+ ui32 MaxSpansPerSecond = 10 ;
96
+ TDuration MaxSpanTimeInQueue = TDuration::Seconds(60 );
97
+
98
+ bool WakeupScheduled = false ;
99
+
26
100
TString CollectorUrl;
27
101
TString ServiceName;
28
102
@@ -36,26 +110,18 @@ namespace NWilson {
36
110
NServiceProto::ExportTraceServiceResponse Response;
37
111
grpc::Status Status;
38
112
39
- struct TSpanQueueItem {
40
- TMonotonic ExpirationTimestamp;
41
- NTraceProto::Span Span;
42
- ui32 Size;
43
- };
44
-
45
- std::deque<TSpanQueueItem> Spans;
113
+ TBatch CurrentBatch;
114
+ std::queue<TBatch::TData> BatchQueue;
46
115
ui64 SpansSize = 0 ;
47
116
TMonotonic NextSendTimestamp;
48
- ui32 MaxSpansAtOnce = 25 ;
49
- ui32 MaxSpansPerSecond = 10 ;
50
- TDuration MaxSpanTimeInQueue = TDuration::Seconds(60 );
51
-
52
- bool WakeupScheduled = false ;
117
+ ui64 SendBatchId = 1 ;
53
118
54
119
public:
55
120
TWilsonUploader (WilsonUploaderParams params)
56
121
: CollectorUrl(std::move(params.CollectorUrl))
57
122
, ServiceName(std::move(params.ServiceName))
58
123
, GrpcSigner(std::move(params.GrpcSigner))
124
+ , CurrentBatch(MaxSpansInBatch, MaxBytesInBatch, ServiceName)
59
125
{}
60
126
61
127
~TWilsonUploader () {
@@ -90,25 +156,52 @@ namespace NWilson {
90
156
if (SpansSize >= 100'000'000 ) {
91
157
LOG_ERROR_S (*TlsActivationContext, WILSON_SERVICE_ID, " dropped span due to overflow" );
92
158
} else {
93
- const TMonotonic expirationTimestamp = TActivationContext::Monotonic () + MaxSpanTimeInQueue;
159
+ const TMonotonic now = TActivationContext::Monotonic ();
160
+ const TMonotonic expirationTimestamp = now + MaxSpanTimeInQueue;
94
161
auto & span = ev->Get ()->Span ;
95
162
const ui32 size = span.ByteSizeLong ();
96
- Spans.push_back (TSpanQueueItem{expirationTimestamp, std::move (span), size});
163
+ if (size > MaxBytesInBatch) {
164
+ ALOG_ERROR (WILSON_SERVICE_ID, " dropped span of size " << size << " , which exceeds max batch size " << MaxBytesInBatch);
165
+ return ;
166
+ }
167
+ TSpanQueueItem spanItem{expirationTimestamp, std::move (span), size};
97
168
SpansSize += size;
169
+ if (CurrentBatch.IsEmpty ()) {
170
+ ScheduleBatchCompletion (now);
171
+ }
172
+ if (CurrentBatch.Add (spanItem)) {
173
+ return ;
174
+ }
175
+ CompleteCurrentBatch ();
98
176
TryMakeProgress ();
177
+ Y_ABORT_UNLESS (CurrentBatch.Add (spanItem), " failed to add span to empty batch" );
178
+ ScheduleBatchCompletion (now);
99
179
}
100
180
}
101
181
182
+ void ScheduleBatchCompletion (TMonotonic now) {
183
+ TMonotonic completionTime = now + TDuration::MilliSeconds (MaxBatchAccumulationMilliseconds);
184
+ TActivationContext::Schedule (completionTime,
185
+ new IEventHandle (SelfId (), {}, new TEvents::TEvWakeup (SendBatchId)));
186
+
187
+ }
188
+
189
+ void CompleteCurrentBatch () {
190
+ BatchQueue.push (std::move (CurrentBatch).Complete ());
191
+ CurrentBatch = TBatch (MaxSpansInBatch, MaxBytesInBatch, ServiceName);
192
+ ++SendBatchId;
193
+ }
194
+
102
195
void TryToSend () {
103
196
const TMonotonic now = TActivationContext::Monotonic ();
104
197
105
198
ui32 numSpansDropped = 0 ;
106
- while (!Spans .empty ()) {
107
- const TSpanQueueItem & item = Spans .front ();
199
+ while (!BatchQueue .empty ()) {
200
+ const TBatch::TData & item = BatchQueue .front ();
108
201
if (item.ExpirationTimestamp <= now) {
109
- SpansSize -= item.Size ;
110
- Spans. pop_front () ;
111
- ++numSpansDropped ;
202
+ SpansSize -= item.SizeBytes ;
203
+ numSpansDropped += item. SizeSpans ;
204
+ BatchQueue. pop () ;
112
205
} else {
113
206
break ;
114
207
}
@@ -119,42 +212,36 @@ namespace NWilson {
119
212
" dropped " << numSpansDropped << " span(s) due to expiration" );
120
213
}
121
214
122
- if (Context || Spans .empty ()) {
215
+ if (Context || BatchQueue .empty ()) {
123
216
return ;
124
217
} else if (now < NextSendTimestamp) {
125
218
ScheduleWakeup (NextSendTimestamp);
126
219
return ;
127
220
}
128
221
129
- NServiceProto::ExportTraceServiceRequest request;
130
- auto *rspan = request.add_resource_spans ();
131
- auto *serviceNameAttr = rspan->mutable_resource ()->add_attributes ();
132
- serviceNameAttr->set_key (" service.name" );
133
- serviceNameAttr->mutable_value ()->set_string_value (ServiceName);
134
- auto *sspan = rspan->add_scope_spans ();
135
-
136
- NextSendTimestamp = now;
137
- for (ui32 i = 0 ; i < MaxSpansAtOnce && !Spans.empty (); ++i, Spans.pop_front ()) {
138
- auto & item = Spans.front ();
139
- auto & s = item.Span ;
140
-
141
- LOG_DEBUG_S (*TlsActivationContext, WILSON_SERVICE_ID, " exporting span"
142
- << " TraceId# " << HexEncode (s.trace_id ())
143
- << " SpanId# " << HexEncode (s.span_id ())
144
- << " ParentSpanId# " << HexEncode (s.parent_span_id ())
145
- << " Name# " << s.name ());
146
-
147
- SpansSize -= item.Size ;
148
- s.Swap (sspan->add_spans ());
149
- NextSendTimestamp += TDuration::MicroSeconds (1'000'000 / MaxSpansPerSecond);
222
+
223
+ TBatch::TData batch = std::move (BatchQueue.front ());
224
+ BatchQueue.pop ();
225
+
226
+ ALOG_DEBUG (WILSON_SERVICE_ID, " exporting batch of " << batch.SizeSpans << " spans, total spans size: " << batch.SizeBytes );
227
+ Y_ABORT_UNLESS (batch.Request .resource_spansSize () == 1 && batch.Request .resource_spans (0 ).scope_spansSize () == 1 );
228
+ for (const auto & span : batch.Request .resource_spans (0 ).scope_spans (0 ).spans ()) {
229
+ ALOG_DEBUG (WILSON_SERVICE_ID, " exporting span"
230
+ << " TraceId# " << HexEncode (span.trace_id ())
231
+ << " SpanId# " << HexEncode (span.span_id ())
232
+ << " ParentSpanId# " << HexEncode (span.parent_span_id ())
233
+ << " Name# " << span.name ());
150
234
}
151
235
236
+ NextSendTimestamp = now + TDuration::MicroSeconds ((batch.SizeSpans * 1'000'000 ) / MaxSpansPerSecond);
237
+ SpansSize -= batch.SizeBytes ;
238
+
152
239
ScheduleWakeup (NextSendTimestamp);
153
240
Context = std::make_unique<grpc::ClientContext>();
154
241
if (GrpcSigner) {
155
242
GrpcSigner->SignClientContext (*Context);
156
243
}
157
- Reader = Stub->AsyncExport (Context.get (), std::move (request ), &CQ);
244
+ Reader = Stub->AsyncExport (Context.get (), std::move (batch. Request ), &CQ);
158
245
Reader->Finish (&Response, &Status, nullptr );
159
246
}
160
247
@@ -179,15 +266,20 @@ namespace NWilson {
179
266
template <typename T>
180
267
void ScheduleWakeup (T&& deadline) {
181
268
if (!WakeupScheduled) {
182
- TActivationContext::Schedule (deadline, new IEventHandle (TEvents::TSystem::Wakeup, 0 , SelfId (), {},
183
- nullptr , 0 ));
269
+ TActivationContext::Schedule (deadline,
270
+ new IEventHandle ( SelfId (), {}, new TEvents::TEvWakeup ));
184
271
WakeupScheduled = true ;
185
272
}
186
273
}
187
274
188
- void HandleWakeup () {
189
- Y_ABORT_UNLESS (WakeupScheduled);
190
- WakeupScheduled = false ;
275
+ void HandleWakeup (TEvents::TEvWakeup::TPtr& ev) {
276
+ const auto tag = ev->Get ()->Tag ;
277
+ if (tag == SendBatchId) {
278
+ CompleteCurrentBatch ();
279
+ } else if (tag == 0 ) {
280
+ Y_ABORT_UNLESS (WakeupScheduled);
281
+ WakeupScheduled = false ;
282
+ }
191
283
TryMakeProgress ();
192
284
}
193
285
@@ -198,7 +290,7 @@ namespace NWilson {
198
290
199
291
STRICT_STFUNC (StateWork,
200
292
hFunc (TEvWilson, Handle);
201
- cFunc (TEvents::TSystem::Wakeup , HandleWakeup);
293
+ hFunc (TEvents::TEvWakeup , HandleWakeup);
202
294
);
203
295
204
296
STRICT_STFUNC (StateBroken,
0 commit comments