Project
Loading...
Searching...
No Matches
CommonServices.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
25#include "Framework/Signpost.h"
32#include "InputRouteHelpers.h"
36#include "Framework/Tracing.h"
46#include "Framework/Signpost.h"
49
50#include "TextDriverClient.h"
51#include "WSDriverClient.h"
52#include "HTTPParser.h"
53#include "../src/DataProcessingStatus.h"
54#include "DecongestionService.h"
55#include "ArrowSupport.h"
58#include "Headers/STFHeader.h"
59#include "Headers/DataHeader.h"
60
61#include <Configuration/ConfigurationInterface.h>
62#include <Configuration/ConfigurationFactory.h>
63#include <Monitoring/MonitoringFactory.h>
64#include <Monitoring/ProcessMonitor.h>
65#include "Framework/Signpost.h"
66
67#include <fairmq/Device.h>
68#include <fairmq/shmem/Monitor.h>
69#include <fairmq/shmem/Common.h>
70#include <fairmq/ProgOptions.h>
71#include <uv.h>
72
73#include <cstdlib>
74#include <cstring>
75
76using o2::configuration::ConfigurationFactory;
77using o2::configuration::ConfigurationInterface;
78using o2::monitoring::Monitoring;
79using o2::monitoring::MonitoringFactory;
80using Metric = o2::monitoring::Metric;
81using Key = o2::monitoring::tags::Key;
82using Value = o2::monitoring::tags::Value;
83
84O2_DECLARE_DYNAMIC_LOG(data_processor_context);
85O2_DECLARE_DYNAMIC_LOG(stream_context);
88
89namespace o2::framework
90{
91
92#define MONITORING_QUEUE_SIZE 100
94{
95 return ServiceSpec{
96 .name = "monitoring",
97 .init = [](ServiceRegistryRef registry, DeviceState&, fair::mq::ProgOptions& options) -> ServiceHandle {
98 void* service = nullptr;
99 bool isWebsocket = strncmp(options.GetPropertyAsString("driver-client-backend").c_str(), "ws://", 4) == 0;
100 bool isDefault = options.GetPropertyAsString("monitoring-backend") == "default";
101 bool useDPL = (isWebsocket && isDefault) || options.GetPropertyAsString("monitoring-backend") == "dpl://";
102 o2::monitoring::Monitoring* monitoring;
103 if (useDPL) {
104 monitoring = new Monitoring();
105 auto dplBackend = std::make_unique<DPLMonitoringBackend>(registry);
106 (dynamic_cast<o2::monitoring::Backend*>(dplBackend.get()))->setVerbosity(o2::monitoring::Verbosity::Debug);
107 monitoring->addBackend(std::move(dplBackend));
108 } else {
109 auto backend = isDefault ? "infologger://" : options.GetPropertyAsString("monitoring-backend");
110 monitoring = MonitoringFactory::Get(backend).release();
111 }
112 service = monitoring;
113 monitoring->enableBuffering(MONITORING_QUEUE_SIZE);
114 assert(registry.get<DeviceSpec const>().name.empty() == false);
115 monitoring->addGlobalTag("pipeline_id", std::to_string(registry.get<DeviceSpec const>().inputTimesliceId));
116 monitoring->addGlobalTag("dataprocessor_name", registry.get<DeviceSpec const>().name);
117 monitoring->addGlobalTag("dpl_instance", options.GetPropertyAsString("shm-segment-id"));
118 return ServiceHandle{TypeIdHelpers::uniqueId<Monitoring>(), service};
119 },
120 .configure = noConfiguration(),
121 .start = [](ServiceRegistryRef services, void* service) {
122 auto* monitoring = (o2::monitoring::Monitoring*)service;
123
124 // Re-arm process monitoring: .stop takes the final measurement and stops
125 // the sampling thread, so without this a device would report nothing at
126 // all from its second run onwards. A no-op while already running.
127 auto interval = services.get<DeviceSpec const>().resourceMonitoringInterval;
129 using o2::monitoring::PmMeasurement;
130 monitoring->enableProcessMonitoring(interval, {PmMeasurement::Cpu, PmMeasurement::Mem, PmMeasurement::Smaps});
131 }
132
133 auto extRunNumber = services.get<RawDeviceService>().device()->fConfig->GetProperty<std::string>("runNumber", "unspecified");
134 if (extRunNumber == "unspecified") {
135 return;
136 }
137 try {
138 monitoring->setRunNumber(std::stoul(extRunNumber));
139 } catch (...) {
140 } },
141 // Final measurement here rather than in ~Monitoring() at .exit, which is
142 // not reliably reached before the process exits. Unlike postEOS this also
143 // covers devices that quit themselves via readyToQuit().
144 .stop = [](ServiceRegistryRef, void* service) {
145 auto* monitoring = reinterpret_cast<Monitoring*>(service);
146 monitoring->finalizeProcessMonitoring(); },
147 .exit = [](ServiceRegistryRef registry, void* service) {
148 auto* monitoring = reinterpret_cast<Monitoring*>(service);
149 monitoring->flushBuffer();
150 delete monitoring; },
151 .kind = ServiceKind::Serial};
152}
153
154// An asyncronous service that executes actions in at the end of the data processing
156{
157 return ServiceSpec{
158 .name = "async-queue",
159 .init = simpleServiceInit<AsyncQueue, AsyncQueue>(),
160 .configure = noConfiguration(),
161 .stop = [](ServiceRegistryRef services, void* service) {
162 auto& queue = services.get<AsyncQueue>();
164 },
165 .kind = ServiceKind::Serial};
166}
167
168// Make it a service so that it can be used easily from the analysis
169// FIXME: Moreover, it makes sense that this will be duplicated on a per thread
170// basis when we get to it.
172{
173 return ServiceSpec{
174 .name = "timing-info",
175 .uniqueId = simpleServiceId<TimingInfo>(),
176 .init = simpleServiceInit<TimingInfo, TimingInfo, ServiceKind::Stream>(),
177 .configure = noConfiguration(),
178 .kind = ServiceKind::Stream};
179}
180
182{
183 return ServiceSpec{
184 .name = "stream-context",
185 .uniqueId = simpleServiceId<StreamContext>(),
186 .init = simpleServiceInit<StreamContext, StreamContext, ServiceKind::Stream>(),
187 .configure = noConfiguration(),
188 .preProcessing = [](ProcessingContext& context, void* service) {
189 auto* stream = (StreamContext*)service;
190 auto& routes = context.services().get<DeviceSpec const>().outputs;
191 // Notice I need to do this here, because different invocation for
192 // the same stream might be referring to different data processors.
193 // We should probably have a context which is per stream of a specific
194 // data processor.
195 stream->routeDPLCreated.resize(routes.size());
196 stream->routeCreated.resize(routes.size());
197 // Reset the routeDPLCreated at every processing step
198 std::fill(stream->routeDPLCreated.begin(), stream->routeDPLCreated.end(), false);
199 std::fill(stream->routeCreated.begin(), stream->routeCreated.end(), false); },
200 .postProcessing = [](ProcessingContext& processingContext, void* service) {
201 auto* stream = (StreamContext*)service;
202 auto& routes = processingContext.services().get<DeviceSpec const>().outputs;
203 auto& timeslice = processingContext.services().get<TimingInfo>().timeslice;
204 auto& messageContext = processingContext.services().get<MessageContext>();
205 auto dispatchState = messageContext.dispatchState();
206 O2_SIGNPOST_ID_FROM_POINTER(cid, stream_context, service);
207 // Do not report discarded messages as missing outputs.
208 if (dispatchState == MessageContext::DispatchState::Discarded) {
209 O2_SIGNPOST_EVENT_EMIT_ERROR(stream_context, cid, "postProcessingCallbacks", "Output messages discarded.");
210 return;
211 }
212 // Check if we never created any data for this timeslice
213 // if we did not, but messages were dispatched,
214 // it means it was created out of band.
215 bool userDidCreate = false;
216 for (size_t ri = 0; ri < routes.size(); ++ri) {
217 if (stream->routeCreated[ri] == true && stream->routeDPLCreated[ri] == false) {
218 userDidCreate = true;
219 break;
220 }
221 }
222 O2_SIGNPOST_EVENT_EMIT(stream_context, cid, "postProcessingCallbacks", "userDidCreate == %d && didDispatch == %d",
223 userDidCreate,
225 if (userDidCreate == false && dispatchState == MessageContext::DispatchState::Dispatched) {
226 O2_SIGNPOST_EVENT_EMIT(stream_context, cid, "postProcessingCallbacks", "Data created out of band userDidCreate == %d && messageContext.didDispatch == %d",
227 userDidCreate,
229 return;
230 }
231 if (userDidCreate == false && dispatchState == MessageContext::DispatchState::NotDispatched) {
232 O2_SIGNPOST_ID_FROM_POINTER(cid, stream_context, service);
233 O2_SIGNPOST_EVENT_EMIT(stream_context, cid, "postProcessingCallbacks", "No data created.");
234 return;
235 }
236 for (size_t ri = 0; ri < routes.size(); ++ri) {
237 auto &route = routes[ri];
238 auto &matcher = route.matcher;
239 if (stream->routeDPLCreated[ri] == true) {
240 O2_SIGNPOST_EVENT_EMIT(stream_context, cid, "postProcessingCallbacks", "Data created by DPL. ri = %" PRIu64 ", %{public}s",
241 (uint64_t)ri, DataSpecUtils::describe(matcher).c_str());
242 continue;
243 }
244 if (stream->routeCreated[ri] == true) {
245 continue;
246 } if ((timeslice % route.maxTimeslices) != route.timeslice) {
247 O2_SIGNPOST_EVENT_EMIT(stream_context, cid, "postProcessingCallbacks", "Route ri = %" PRIu64 ", skipped because of pipelining.",
248 (uint64_t)ri);
249 continue;
250 }
251 if (matcher.lifetime == Lifetime::Timeframe) {
252 O2_SIGNPOST_EVENT_EMIT(stream_context, cid, "postProcessingCallbacks",
253 "Expected Lifetime::Timeframe data %{public}s was not created for timeslice %" PRIu64 " and might result in dropped timeframes",
254 DataSpecUtils::describe(matcher).c_str(), (uint64_t)timeslice);
255 LOGP(error, "Expected Lifetime::Timeframe data {} was not created for timeslice {} and might result in dropped timeframes", DataSpecUtils::describe(matcher), timeslice);
256 }
257 } },
258 .preEOS = [](EndOfStreamContext& context, void* service) {
259 // We need to reset the routeDPLCreated / routeCreated because the end of stream
260 // uses a different context which does not know about the routes.
261 // FIXME: This should be fixed in a different way, but for now it will
262 // allow TPC IDC to work.
263 auto* stream = (StreamContext*)service;
264 auto& routes = context.services().get<DeviceSpec const>().outputs;
265 // Notice I need to do this here, because different invocation for
266 // the same stream might be referring to different data processors.
267 // We should probably have a context which is per stream of a specific
268 // data processor.
269 stream->routeDPLCreated.resize(routes.size());
270 stream->routeCreated.resize(routes.size());
271 // Reset the routeCreated / routeDPLCreated at every processing step
272 std::fill(stream->routeCreated.begin(), stream->routeCreated.end(), false);
273 std::fill(stream->routeDPLCreated.begin(), stream->routeDPLCreated.end(), false); },
274 .kind = ServiceKind::Stream};
275}
276
278{
279 return ServiceSpec{
280 .name = "datataking-contex",
281 .uniqueId = simpleServiceId<DataTakingContext>(),
282 .init = simpleServiceInit<DataTakingContext, DataTakingContext, ServiceKind::Stream>(),
283 .configure = noConfiguration(),
284 .preProcessing = [](ProcessingContext& processingContext, void* service) {
285 auto& context = processingContext.services().get<DataTakingContext>();
286 for (auto const& ref : processingContext.inputs()) {
287 const o2::framework::DataProcessingHeader *dph = o2::header::get<DataProcessingHeader*>(ref.header);
288 const auto* dh = o2::header::get<o2::header::DataHeader*>(ref.header);
289 if (!dph || !dh) {
290 continue;
291 }
292 context.runNumber = fmt::format("{}", dh->runNumber);
293 break;
294 } },
295 // Notice this will be executed only once, because the service is declared upfront.
296 .start = [](ServiceRegistryRef services, void* service) {
297 auto& context = services.get<DataTakingContext>();
298
300
301 auto extRunNumber = services.get<RawDeviceService>().device()->fConfig->GetProperty<std::string>("runNumber", "unspecified");
302 if (extRunNumber != "unspecified" || context.runNumber == "0") {
303 context.runNumber = extRunNumber;
304 }
305 auto extLHCPeriod = services.get<RawDeviceService>().device()->fConfig->GetProperty<std::string>("lhc_period", "unspecified");
306 if (extLHCPeriod != "unspecified") {
307 context.lhcPeriod = extLHCPeriod;
308 } else {
309 static const char* months[12] = {"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
310 time_t now = time(nullptr);
311 auto ltm = gmtime(&now);
312 context.lhcPeriod = months[ltm->tm_mon];
313 LOG(info) << "LHCPeriod is not available, using current month " << context.lhcPeriod;
314 }
315
316 auto extRunType = services.get<RawDeviceService>().device()->fConfig->GetProperty<std::string>("run_type", "unspecified");
317 if (extRunType != "unspecified") {
318 context.runType = extRunType;
319 }
320 auto extEnvId = services.get<RawDeviceService>().device()->fConfig->GetProperty<std::string>("environment_id", "unspecified");
321 if (extEnvId != "unspecified") {
322 context.envId = extEnvId;
323 }
324 auto extDetectors = services.get<RawDeviceService>().device()->fConfig->GetProperty<std::string>("detectors", "unspecified");
325 if (extDetectors != "unspecified") {
326 context.detectors = extDetectors;
327 }
328 auto forcedRaw = services.get<RawDeviceService>().device()->fConfig->GetProperty<std::string>("force_run_as_raw", "false");
329 context.forcedRaw = forcedRaw == "true"; },
330 .kind = ServiceKind::Stream};
331}
332
334};
335
337{
338 return ServiceSpec{
339 .name = "configuration",
340 .init = [](ServiceRegistryRef services, DeviceState&, fair::mq::ProgOptions& options) -> ServiceHandle {
341 auto backend = options.GetPropertyAsString("configuration");
342 if (backend == "command-line") {
343 return ServiceHandle{0, nullptr};
344 }
345 return ServiceHandle{TypeIdHelpers::uniqueId<ConfigurationInterface>(),
346 ConfigurationFactory::getConfiguration(backend).release()};
347 },
348 .configure = noConfiguration(),
349 .driverStartup = [](ServiceRegistryRef registry, DeviceConfig const& dc) {
350 if (dc.options.count("configuration") == 0) {
351 registry.registerService(ServiceHandle{0, nullptr});
352 return;
353 }
354 auto backend = dc.options["configuration"].as<std::string>();
355 registry.registerService(ServiceHandle{TypeIdHelpers::uniqueId<ConfigurationInterface>(),
356 ConfigurationFactory::getConfiguration(backend).release()}); },
357 .kind = ServiceKind::Global};
358}
359
361{
362 return ServiceSpec{
363 .name = "driverClient",
364 .init = [](ServiceRegistryRef services, DeviceState& state, fair::mq::ProgOptions& options) -> ServiceHandle {
365 auto backend = options.GetPropertyAsString("driver-client-backend");
366 if (backend == "stdout://") {
367 return ServiceHandle{TypeIdHelpers::uniqueId<DriverClient>(),
368 new TextDriverClient(services, state)};
369 }
370 auto [ip, port] = o2::framework::parse_websocket_url(backend.c_str());
371 return ServiceHandle{TypeIdHelpers::uniqueId<DriverClient>(),
372 new WSDriverClient(services, ip.c_str(), port)};
373 },
374 .configure = noConfiguration(),
375 .kind = ServiceKind::Global};
376}
377
379{
380 return ServiceSpec{
381 .name = "control",
382 .init = [](ServiceRegistryRef services, DeviceState& state, fair::mq::ProgOptions& options) -> ServiceHandle {
383 return ServiceHandle{TypeIdHelpers::uniqueId<ControlService>(),
384 new ControlService(services, state)};
385 },
386 .configure = noConfiguration(),
387 .kind = ServiceKind::Serial};
388}
389
391{
392 return ServiceSpec{
393 .name = "localrootfile",
394 .init = simpleServiceInit<LocalRootFileService, LocalRootFileService>(),
395 .configure = noConfiguration(),
396 .kind = ServiceKind::Serial};
397}
398
400{
401 return ServiceSpec{
402 .name = "parallel",
403 .init = [](ServiceRegistryRef services, DeviceState&, fair::mq::ProgOptions& options) -> ServiceHandle {
404 auto& spec = services.get<DeviceSpec const>();
405 return ServiceHandle{TypeIdHelpers::uniqueId<ParallelContext>(),
406 new ParallelContext(spec.rank, spec.nSlots)};
407 },
408 .configure = noConfiguration(),
409 .kind = ServiceKind::Serial};
410}
411
413{
414 return ServiceSpec{
415 .name = "timesliceindex",
416 .init = [](ServiceRegistryRef services, DeviceState& state, fair::mq::ProgOptions& options) -> ServiceHandle {
417 auto& spec = services.get<DeviceSpec const>();
418 return ServiceHandle{TypeIdHelpers::uniqueId<TimesliceIndex>(),
419 new TimesliceIndex(InputRouteHelpers::maxLanes(spec.inputs), state.inputChannelInfos)};
420 },
421 .configure = noConfiguration(),
422 .kind = ServiceKind::Serial};
423}
424
426{
427 return ServiceSpec{
428 .name = "callbacks",
429 .init = simpleServiceInit<CallbackService, CallbackService>(),
430 .configure = noConfiguration(),
431 .kind = ServiceKind::Serial};
432}
433
435{
436 return ServiceSpec{
437 .name = "datarelayer",
438 .init = [](ServiceRegistryRef services, DeviceState&, fair::mq::ProgOptions& options) -> ServiceHandle {
439 auto& spec = services.get<DeviceSpec const>();
440 int pipelineLength = DefaultsHelpers::pipelineLength(options);
441 return ServiceHandle{TypeIdHelpers::uniqueId<DataRelayer>(),
442 new DataRelayer(spec.completionPolicy,
443 spec.inputs,
444 services.get<TimesliceIndex>(),
445 services,
446 pipelineLength)};
447 },
448 .configure = noConfiguration(),
449 .kind = ServiceKind::Serial};
450}
451
453{
454 return ServiceSpec{
455 .name = "datasender",
456 .init = [](ServiceRegistryRef services, DeviceState&, fair::mq::ProgOptions& options) -> ServiceHandle {
457 return ServiceHandle{TypeIdHelpers::uniqueId<DataSender>(),
458 new DataSender(services)};
459 },
460 .configure = noConfiguration(),
461 .preProcessing = [](ProcessingContext&, void* service) {
462 auto& dataSender = *reinterpret_cast<DataSender*>(service);
463 dataSender.reset(); },
464 .postDispatching = [](ProcessingContext& ctx, void* service) {
465 auto& dataSender = *reinterpret_cast<DataSender*>(service);
466 // If the quit was requested, the post dispatching can still happen
467 // but with an empty set of data.
468 if (ctx.services().get<DeviceState>().quitRequested == false) {
469 dataSender.verifyMissingSporadic();
470 } },
472}
473
477
479{
480 return ServiceSpec{
481 .name = "tracing",
482 .init = [](ServiceRegistryRef, DeviceState&, fair::mq::ProgOptions&) -> ServiceHandle {
483 return ServiceHandle{.hash = TypeIdHelpers::uniqueId<TracingInfrastructure>(),
484 .instance = new TracingInfrastructure(),
485 .kind = ServiceKind::Serial};
486 },
487 .configure = noConfiguration(),
488 .preProcessing = [](ProcessingContext&, void* service) {
489 auto* t = reinterpret_cast<TracingInfrastructure*>(service);
490 t->processingCount += 1; },
491 .postProcessing = [](ProcessingContext&, void* service) {
492 auto* t = reinterpret_cast<TracingInfrastructure*>(service);
493 t->processingCount += 1; },
494 .kind = ServiceKind::Serial};
495}
496
498};
499
500// CCDB Support service
502{
503 return ServiceSpec{
504 .name = "ccdb-support",
505 .init = [](ServiceRegistryRef services, DeviceState&, fair::mq::ProgOptions&) -> ServiceHandle {
506 // iterate on all the outputs matchers
507 auto& spec = services.get<DeviceSpec const>();
508 for (auto& output : spec.outputs) {
509 if (DataSpecUtils::match(output.matcher, ConcreteDataTypeMatcher{"FLP", "DISTSUBTIMEFRAME"})) {
510 LOGP(debug, "Optional inputs support enabled");
511 return ServiceHandle{.hash = TypeIdHelpers::uniqueId<CCDBSupport>(), .instance = new CCDBSupport, .kind = ServiceKind::Serial};
512 }
513 }
514 return ServiceHandle{.hash = TypeIdHelpers::uniqueId<CCDBSupport>(), .instance = nullptr, .kind = ServiceKind::Serial};
515 },
516 .configure = noConfiguration(),
517 .finaliseOutputs = [](ProcessingContext& pc, void* service) {
518 if (!service) {
519 return;
520 }
521 if (pc.outputs().countDeviceOutputs(true) == 0) {
522 LOGP(debug, "We are w/o outputs, do not automatically add DISTSUBTIMEFRAME to outgoing messages");
523 return;
524 }
525 auto& timingInfo = pc.services().get<TimingInfo>();
526
527 // For any output that is a FLP/DISTSUBTIMEFRAME with subspec != 0,
528 // we create a new message.
529 InputSpec matcher{"matcher", ConcreteDataTypeMatcher{"FLP", "DISTSUBTIMEFRAME"}};
530 auto& streamContext = pc.services().get<StreamContext>();
531 for (size_t oi = 0; oi < pc.services().get<DeviceSpec const>().outputs.size(); ++oi) {
532 OutputRoute const& output = pc.services().get<DeviceSpec const>().outputs[oi];
533 if ((output.timeslice % output.maxTimeslices) != 0) {
534 continue;
535 }
536 if (DataSpecUtils::match(output.matcher, ConcreteDataTypeMatcher{"FLP", "DISTSUBTIMEFRAME"})) {
537 auto concrete = DataSpecUtils::asConcreteDataMatcher(output.matcher);
538 if (concrete.subSpec == 0) {
539 continue;
540 }
541 auto& stfDist = pc.outputs().make<o2::header::STFHeader>(Output{concrete.origin, concrete.description, concrete.subSpec});
542 stfDist.id = timingInfo.timeslice;
543 stfDist.firstOrbit = timingInfo.firstTForbit;
544 stfDist.runNumber = timingInfo.runNumber;
545 // We mark it as not created, because we do should not account for it when
546 // checking if we created all the data for a timeslice.
547 O2_SIGNPOST_ID_FROM_POINTER(sid, stream_context, &streamContext);
548 O2_SIGNPOST_EVENT_EMIT(stream_context, sid, "finaliseOutputs", "Route %" PRIu64 " (%{public}s) was created by DPL.", (uint64_t)oi,
549 DataSpecUtils::describe(output.matcher).c_str());
550 streamContext.routeDPLCreated[oi] = true;
551 }
552 } },
553 .kind = ServiceKind::Global};
554}
559
560auto decongestionCallback = [](AsyncTask& task, size_t id) -> void {
561 auto& oldestPossibleOutput = task.user<DecongestionContext>().oldestPossibleOutput;
562 auto& ref = task.user<DecongestionContext>().ref;
563
564 auto& decongestion = ref.get<DecongestionService>();
565 auto& proxy = ref.get<FairMQDeviceProxy>();
566
567 O2_SIGNPOST_ID_GENERATE(cid, async_queue);
568 cid.value = id;
569 if (decongestion.lastTimeslice >= oldestPossibleOutput.timeslice.value) {
570 O2_SIGNPOST_EVENT_EMIT(async_queue, cid, "oldest_possible_timeslice", "Not sending already sent value: %" PRIu64 "> %" PRIu64,
571 decongestion.lastTimeslice, (uint64_t)oldestPossibleOutput.timeslice.value);
572 return;
573 }
574 O2_SIGNPOST_EVENT_EMIT(async_queue, cid, "oldest_possible_timeslice", "Running oldest possible timeslice %" PRIu64 " propagation.",
575 (uint64_t)oldestPossibleOutput.timeslice.value);
576 DataProcessingHelpers::broadcastOldestPossibleTimeslice(ref, oldestPossibleOutput.timeslice.value);
577
578 for (int fi = 0; fi < proxy.getNumForwardChannels(); fi++) {
579 auto& info = proxy.getForwardChannelInfo(ChannelIndex{fi});
580 auto& state = proxy.getForwardChannelState(ChannelIndex{fi});
581 // TODO: this we could cache in the proxy at the bind moment.
582 if (info.channelType != ChannelAccountingType::DPL) {
583 O2_SIGNPOST_EVENT_EMIT(async_queue, cid, "oldest_possible_timeslice", "Skipping channel %{public}s", info.name.c_str());
584 continue;
585 }
586 if (DataProcessingHelpers::sendOldestPossibleTimeframe(ref, info, state, oldestPossibleOutput.timeslice.value)) {
587 O2_SIGNPOST_EVENT_EMIT(async_queue, cid, "oldest_possible_timeslice",
588 "Forwarding to channel %{public}s oldest possible timeslice %" PRIu64 ", priority %d",
589 info.name.c_str(), (uint64_t)oldestPossibleOutput.timeslice.value, 20);
590 }
591 }
592 decongestion.lastTimeslice = oldestPossibleOutput.timeslice.value;
593};
594
595auto decongestionCallbackOrdered = [](AsyncTask& task, size_t id) -> void {
596 auto& oldestPossibleOutput = task.user<DecongestionContext>().oldestPossibleOutput;
597 auto& ref = task.user<DecongestionContext>().ref;
598
599 auto& decongestion = ref.get<DecongestionService>();
600 auto& state = ref.get<DeviceState>();
601 auto& timesliceIndex = ref.get<TimesliceIndex>();
602 O2_SIGNPOST_ID_GENERATE(cid, async_queue);
603 int64_t oldNextTimeslice = decongestion.nextTimeslice;
604 decongestion.nextTimeslice = std::max(decongestion.nextTimeslice, (int64_t)oldestPossibleOutput.timeslice.value);
605 if (oldNextTimeslice != decongestion.nextTimeslice) {
606 if (state.transitionHandling != TransitionHandlingState::NoTransition && DefaultsHelpers::onlineDeploymentMode()) {
607 O2_SIGNPOST_EVENT_EMIT_WARN(async_queue, cid, "oldest_possible_timeslice", "Stop transition requested. Some Lifetime::Timeframe data got dropped starting at %" PRIi64, oldNextTimeslice);
608 } else {
609 O2_SIGNPOST_EVENT_EMIT_CRITICAL(async_queue, cid, "oldest_possible_timeslice", "Some Lifetime::Timeframe data got dropped starting at %" PRIi64, oldNextTimeslice);
610 }
611 timesliceIndex.rescan();
612 }
613};
614
615// Callback for consumeWhenPastOldestPossibleTimeframe.
616// Runs in the async queue at the beginning of the next iteration,
617// after Retry slots unblocked by an oldestPossibleInput change have
618// been consumed and freed. Rescans all slots and forwards the
619// (now up-to-date) oldestPossibleOutput downstream.
620auto decongestionCallbackPastOldest = [](AsyncTask& task, size_t id) -> void {
621 auto& ref = task.user<DecongestionContext>().ref;
622
623 auto& decongestion = ref.get<DecongestionService>();
624 auto& timesliceIndex = ref.get<TimesliceIndex>();
625 auto& relayer = ref.get<DataRelayer>();
626 auto& proxy = ref.get<FairMQDeviceProxy>();
627 O2_SIGNPOST_ID_GENERATE(cid, async_queue);
628
629 timesliceIndex.rescan();
630 timesliceIndex.updateOldestPossibleOutput(decongestion.nextEnumerationTimesliceRewinded);
631 auto oldestPossibleOutput = relayer.getOldestPossibleOutput();
632
633 if (oldestPossibleOutput.timeslice.value <= decongestion.lastTimeslice) {
634 O2_SIGNPOST_EVENT_EMIT(async_queue, cid, "oldest_possible_timeslice",
635 "consumeWhenPastOldestPossibleTimeframe: not forwarding already sent value %" PRIu64,
636 (uint64_t)oldestPossibleOutput.timeslice.value);
637 return;
638 }
639 O2_SIGNPOST_EVENT_EMIT(async_queue, cid, "oldest_possible_timeslice",
640 "consumeWhenPastOldestPossibleTimeframe: forwarding oldest possible timeslice %" PRIu64,
641 (uint64_t)oldestPossibleOutput.timeslice.value);
642 DataProcessingHelpers::broadcastOldestPossibleTimeslice(ref, oldestPossibleOutput.timeslice.value);
643
644 for (int fi = 0; fi < proxy.getNumForwardChannels(); fi++) {
645 auto& info = proxy.getForwardChannelInfo(ChannelIndex{fi});
646 auto& state = proxy.getForwardChannelState(ChannelIndex{fi});
647 if (info.channelType != ChannelAccountingType::DPL) {
648 continue;
649 }
650 DataProcessingHelpers::sendOldestPossibleTimeframe(ref, info, state, oldestPossibleOutput.timeslice.value);
651 }
652 decongestion.lastTimeslice = oldestPossibleOutput.timeslice.value;
653};
654
655// Decongestion service
656// If we do not have any Timeframe input, it means we must be creating timeslices
657// in order and that we should propagate the oldest possible timeslice at the end
658// of each processing step.
661{
662 return ServiceSpec{
663 .name = "decongestion",
664 .init = [](ServiceRegistryRef services, DeviceState&, fair::mq::ProgOptions& options) -> ServiceHandle {
665 auto* decongestion = new DecongestionService();
666 for (auto& input : services.get<DeviceSpec const>().inputs) {
667 if (input.matcher.lifetime == Lifetime::Timeframe || input.matcher.lifetime == Lifetime::QA || input.matcher.lifetime == Lifetime::Sporadic || input.matcher.lifetime == Lifetime::Optional) {
668 LOGP(detail, "Found a real data input, we cannot update the oldest possible timeslice when sending messages");
669 decongestion->isFirstInTopology = false;
670 break;
671 }
672 }
673 for (const auto& label : services.get<DeviceSpec const>().labels) {
675 decongestion->suppressDomainInfo = true;
676 break;
677 }
678 }
679 auto& queue = services.get<AsyncQueue>();
680 decongestion->oldestPossibleTimesliceTask = AsyncQueueHelpers::create(queue, {.name = "oldest-possible-timeslice", .score = 100});
681 return ServiceHandle{TypeIdHelpers::uniqueId<DecongestionService>(), decongestion, ServiceKind::Serial};
682 },
683 .postForwarding = [](ProcessingContext& ctx, void* service) {
684 auto* decongestion = reinterpret_cast<DecongestionService*>(service);
685 if (O2_BUILTIN_LIKELY(decongestion->isFirstInTopology == false)) {
686 return;
687 }
688 O2_SIGNPOST_ID_FROM_POINTER(cid, data_processor_context, service);
689 O2_SIGNPOST_EVENT_EMIT(data_processor_context, cid, "postForwardingCallbacks", "We are the first one in the topology, we need to update the oldest possible timeslice");
690 auto& timesliceIndex = ctx.services().get<TimesliceIndex>();
691 auto& relayer = ctx.services().get<DataRelayer>();
692 timesliceIndex.updateOldestPossibleOutput(decongestion->nextEnumerationTimesliceRewinded);
693 auto& proxy = ctx.services().get<FairMQDeviceProxy>();
694 auto oldestPossibleOutput = relayer.getOldestPossibleOutput();
695 if (decongestion->nextEnumerationTimesliceRewinded && decongestion->nextEnumerationTimeslice < oldestPossibleOutput.timeslice.value) {
696 LOGP(detail, "Not sending oldestPossible if nextEnumerationTimeslice was rewinded");
697 return;
698 }
699
700 if (decongestion->lastTimeslice && oldestPossibleOutput.timeslice.value == decongestion->lastTimeslice) {
701 O2_SIGNPOST_EVENT_EMIT(data_processor_context, cid, "oldest_possible_timeslice",
702 "Not sending already sent value for oldest possible timeslice: %" PRIu64,
703 (uint64_t)oldestPossibleOutput.timeslice.value);
704 return;
705 }
706 if (oldestPossibleOutput.timeslice.value < decongestion->lastTimeslice) {
707 LOGP(error, "We are trying to send an oldest possible timeslice {} that is older than the last one we already sent {}",
708 oldestPossibleOutput.timeslice.value, decongestion->lastTimeslice);
709 return;
710 }
711
712 O2_SIGNPOST_EVENT_EMIT(data_processor_context, cid, "oldest_possible_timeslice", "Broadcasting oldest posssible output %" PRIu64 " due to %{public}s (%" PRIu64 ")",
713 (uint64_t)oldestPossibleOutput.timeslice.value,
714 oldestPossibleOutput.slot.index == -1 ? "channel" : "slot",
715 (uint64_t)(oldestPossibleOutput.slot.index == -1 ? oldestPossibleOutput.channel.value : oldestPossibleOutput.slot.index));
716 O2_SIGNPOST_EVENT_EMIT(data_processor_context, cid, "oldest_possible_timeslice", "Ordered active %d", decongestion->orderedCompletionPolicyActive);
717 if (decongestion->orderedCompletionPolicyActive) {
718 auto oldNextTimeslice = decongestion->nextTimeslice;
719 decongestion->nextTimeslice = std::max(decongestion->nextTimeslice, (int64_t)oldestPossibleOutput.timeslice.value);
720 O2_SIGNPOST_EVENT_EMIT(data_processor_context, cid, "oldest_possible_timeslice", "Next timeslice %" PRIi64, decongestion->nextTimeslice);
721 if (oldNextTimeslice != decongestion->nextTimeslice) {
722 auto& state = ctx.services().get<DeviceState>();
724 O2_SIGNPOST_EVENT_EMIT_WARN(data_processor_context, cid, "oldest_possible_timeslice", "Stop transition requested. Some Lifetime::Timeframe data got dropped starting at %" PRIi64, oldNextTimeslice);
725 } else {
726 O2_SIGNPOST_EVENT_EMIT_CRITICAL(data_processor_context, cid, "oldest_possible_timeslice", "Some Lifetime::Timeframe data got dropped starting at %" PRIi64, oldNextTimeslice);
727 }
728 timesliceIndex.rescan();
729 }
730 }
731 DataProcessingHelpers::broadcastOldestPossibleTimeslice(ctx.services(), oldestPossibleOutput.timeslice.value);
732
733 for (int fi = 0; fi < proxy.getNumForwardChannels(); fi++) {
734 auto& info = proxy.getForwardChannelInfo(ChannelIndex{fi});
735 auto& state = proxy.getForwardChannelState(ChannelIndex{fi});
736 // TODO: this we could cache in the proxy at the bind moment.
737 if (info.channelType != ChannelAccountingType::DPL) {
738 O2_SIGNPOST_EVENT_EMIT(data_processor_context, cid, "oldest_possible_timeslice", "Skipping channel %{public}s", info.name.c_str());
739 continue;
740 }
741 if (DataProcessingHelpers::sendOldestPossibleTimeframe(ctx.services(), info, state, oldestPossibleOutput.timeslice.value)) {
742 O2_SIGNPOST_EVENT_EMIT(data_processor_context, cid, "oldest_possible_timeslice",
743 "Forwarding to channel %{public}s oldest possible timeslice %" PRIu64 ", priority %d",
744 info.name.c_str(), (uint64_t)oldestPossibleOutput.timeslice.value, 20);
745 }
746 }
747 decongestion->lastTimeslice = oldestPossibleOutput.timeslice.value; },
748 .stop = [](ServiceRegistryRef services, void* service) {
749 auto* decongestion = (DecongestionService*)service;
750 services.get<TimesliceIndex>().reset();
751 decongestion->nextEnumerationTimeslice = 0;
752 decongestion->nextEnumerationTimesliceRewinded = false;
753 decongestion->lastTimeslice = 0;
754 decongestion->nextTimeslice = 0;
755 decongestion->oldestPossibleTimesliceTask = {0};
756 auto &state = services.get<DeviceState>();
757 for (auto &channel : state.inputChannelInfos) {
758 channel.oldestForChannel = {0};
759 } },
760 .domainInfoUpdated = [](ServiceRegistryRef services, size_t oldestPossibleTimeslice, ChannelIndex channel) {
761 auto& decongestion = services.get<DecongestionService>();
762 auto& relayer = services.get<DataRelayer>();
763 auto& timesliceIndex = services.get<TimesliceIndex>();
764 O2_SIGNPOST_ID_FROM_POINTER(cid, data_processor_context, &decongestion);
765 O2_SIGNPOST_EVENT_EMIT(data_processor_context, cid, "oldest_possible_timeslice", "Received oldest possible timeframe %" PRIu64 " from channel %d",
766 (uint64_t)oldestPossibleTimeslice, channel.value);
767 relayer.setOldestPossibleInput({oldestPossibleTimeslice}, channel);
768 timesliceIndex.updateOldestPossibleOutput(decongestion.nextEnumerationTimesliceRewinded);
769 auto oldestPossibleOutput = relayer.getOldestPossibleOutput();
770
771 // When consumeWhenPastOldestPossibleTimeframe is active, we always
772 // schedule the callback even when oldestPossibleOutput has not changed
773 // yet. Retry slots held by this policy will be consumed after this
774 // domainInfoUpdated call (once getReadyToProcess re-checks them), and
775 // the callback — running in the next iteration — will recompute
776 // oldestPossibleOutput and forward the updated value downstream.
777 if (decongestion.consumeWhenPastOldestPossibleTimeframeActive) {
778 auto& queue = services.get<AsyncQueue>();
780 queue, AsyncTask{.timeslice = TimesliceId{oldestPossibleTimeslice},
781 .id = decongestion.oldestPossibleTimesliceTask,
782 .debounce = -1,
784 .user<DecongestionContext>({.ref = services, .oldestPossibleOutput = oldestPossibleOutput}));
785 }
786
787 if (oldestPossibleOutput.timeslice.value == decongestion.lastTimeslice) {
788 O2_SIGNPOST_EVENT_EMIT(data_processor_context, cid, "oldest_possible_timeslice", "Synchronous: Not sending already sent value: %" PRIu64, (uint64_t)oldestPossibleOutput.timeslice.value);
789 return;
790 }
791 if (oldestPossibleOutput.timeslice.value < decongestion.lastTimeslice) {
792 LOGP(error, "We are trying to send an oldest possible timeslice {} that is older than the last one we sent {}",
793 oldestPossibleOutput.timeslice.value, decongestion.lastTimeslice);
794 return;
795 }
796 auto& queue = services.get<AsyncQueue>();
797 const auto& state = services.get<DeviceState>();
800 O2_SIGNPOST_EVENT_EMIT(data_processor_context, cid, "oldest_possible_timeslice", "Queueing oldest possible timeslice %" PRIu64 " propagation for execution.",
801 (uint64_t)oldestPossibleOutput.timeslice.value);
803 queue, AsyncTask{ .timeslice = TimesliceId{oldestPossibleTimeslice},
804 .id = decongestion.oldestPossibleTimesliceTask,
805 .debounce = -1, .callback = decongestionCallback}
806 .user<DecongestionContext>(DecongestionContext{.ref = services, .oldestPossibleOutput = oldestPossibleOutput}));
807
808 if (decongestion.orderedCompletionPolicyActive) {
810 queue, AsyncTask{.timeslice = TimesliceId{oldestPossibleOutput.timeslice.value},.id = decongestion.oldestPossibleTimesliceTask, .debounce = -1,
811 .callback = decongestionCallbackOrdered}
812 .user<DecongestionContext>({.ref = services, .oldestPossibleOutput = oldestPossibleOutput}));
813 } },
814 .kind = ServiceKind::Serial};
815}
816
817// FIXME: allow configuring the default number of threads per device
818// This should probably be done by overriding the preFork
819// callback and using the boost program options there to
820// get the default number of threads.
822{
823 return ServiceSpec{
824 .name = "threadpool",
825 .init = [](ServiceRegistryRef services, DeviceState&, fair::mq::ProgOptions& options) -> ServiceHandle {
826 auto* pool = new ThreadPool();
827 // FIXME: this will require some extra argument for the configuration context of a service
828 pool->poolSize = 1;
829 return ServiceHandle{TypeIdHelpers::uniqueId<ThreadPool>(), pool};
830 },
831 .configure = [](InitContext&, void* service) -> void* {
832 auto* t = reinterpret_cast<ThreadPool*>(service);
833 // FIXME: this will require some extra argument for the configuration context of a service
834 t->poolSize = 1;
835 return service;
836 },
837 .postForkParent = [](ServiceRegistryRef services) -> void {
838 // FIXME: this will require some extra argument for the configuration context of a service
839 auto numWorkersS = std::to_string(1);
840 setenv("UV_THREADPOOL_SIZE", numWorkersS.c_str(), 0);
841 },
842 .kind = ServiceKind::Serial};
843}
844
845namespace
846{
847auto sendRelayerMetrics(ServiceRegistryRef registry, DataProcessingStats& stats) -> void
848{
849 // Update the timer to make sure we have the correct time when sending out the stats.
850 uv_update_time(registry.get<DeviceState>().loop);
851 // Derive the amount of shared memory used
852 auto& runningWorkflow = registry.get<RunningWorkflowInfo const>();
853 using namespace fair::mq::shmem;
854 auto& spec = registry.get<DeviceSpec const>();
855
856 // FIXME: Ugly, but we do it only every 5 seconds...
857 if (stats.hasAvailSHMMetric) {
858 auto device = registry.get<RawDeviceService>().device();
859 long freeMemory = -1;
860 try {
861 freeMemory = fair::mq::shmem::Monitor::GetFreeMemory(ShmId{makeShmIdStr(device->fConfig->GetProperty<uint64_t>("shmid"))}, runningWorkflow.shmSegmentId);
862 } catch (...) {
863 }
864 if (freeMemory == -1) {
865 try {
866 freeMemory = fair::mq::shmem::Monitor::GetFreeMemory(SessionId{device->fConfig->GetProperty<std::string>("session")}, runningWorkflow.shmSegmentId);
867 } catch (...) {
868 }
869 }
870 stats.updateStats({static_cast<unsigned short>(static_cast<int>(ProcessingStatsId::AVAILABLE_MANAGED_SHM_BASE) + (runningWorkflow.shmSegmentId % 512)), DataProcessingStats::Op::SetIfPositive, freeMemory});
871 }
872
873 auto device = registry.get<RawDeviceService>().device();
874
875 int64_t totalBytesIn = 0;
876 int64_t totalBytesOut = 0;
877
878 for (auto& channel : device->GetChannels()) {
879 totalBytesIn += channel.second[0].GetBytesRx();
880 totalBytesOut += channel.second[0].GetBytesTx();
881 }
882
883 stats.updateStats({static_cast<short>(ProcessingStatsId::TOTAL_BYTES_IN), DataProcessingStats::Op::Set, totalBytesIn / 1000000});
884 stats.updateStats({static_cast<short>(ProcessingStatsId::TOTAL_BYTES_OUT), DataProcessingStats::Op::Set, totalBytesOut / 1000000});
885
886 stats.updateStats({static_cast<short>(ProcessingStatsId::TOTAL_RATE_IN_MB_S), DataProcessingStats::Op::InstantaneousRate, totalBytesIn / 1000000});
887 stats.updateStats({static_cast<short>(ProcessingStatsId::TOTAL_RATE_OUT_MB_S), DataProcessingStats::Op::InstantaneousRate, totalBytesOut / 1000000});
888};
889
890auto flushStates(ServiceRegistryRef registry, DataProcessingStates& states) -> void
891{
892 if (!registry.get<DriverConfig const>().driverHasGUI) {
893 return;
894 }
895 states.flushChangedStates([&states, registry](std::string const& spec, int64_t timestamp, std::string_view value) mutable -> void {
896 auto& client = registry.get<ControlService>();
897 client.push(spec, value, timestamp);
898 });
899}
900
901O2_DECLARE_DYNAMIC_LOG(monitoring_service);
902
904auto flushMetrics(ServiceRegistryRef registry, DataProcessingStats& stats) -> void
905{
906 // Flushing metrics should only happen on main thread to avoid
907 // having to have a mutex for the communication with the driver.
908 O2_SIGNPOST_ID_GENERATE(sid, monitoring_service);
909 O2_SIGNPOST_START(monitoring_service, sid, "flush", "flushing metrics");
910 if (registry.isMainThread() == false) {
911 LOGP(fatal, "Flushing metrics should only happen on the main thread.");
912 }
913 auto& monitoring = registry.get<Monitoring>();
914 auto& relayer = registry.get<DataRelayer>();
915
916 // Send all the relevant metrics for the relayer to update the GUI
917 stats.flushChangedMetrics([&monitoring, sid](DataProcessingStats::MetricSpec const& spec, int64_t timestamp, int64_t value) mutable -> void {
918 // convert timestamp to a time_point
919 auto tp = std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds>(std::chrono::milliseconds(timestamp));
920 auto metric = o2::monitoring::Metric{spec.name, Metric::DefaultVerbosity, tp};
921 if (spec.kind == DataProcessingStats::Kind::UInt64) {
922 if (value < 0) {
923 O2_SIGNPOST_EVENT_EMIT(monitoring_service, sid, "flushChangedMetrics", "Value for %{public}s is negative, setting to 0",
924 spec.name.c_str());
925 value = 0;
926 }
927 metric.addValue((uint64_t)value, "value");
928 } else {
929 if (value > (int64_t)std::numeric_limits<int>::max()) {
930 O2_SIGNPOST_EVENT_EMIT(monitoring_service, sid, "flushChangedMetrics", "Value for %{public}s is too large, setting to INT_MAX",
931 spec.name.c_str());
932 value = (int64_t)std::numeric_limits<int>::max();
933 }
934 if (value < (int64_t)std::numeric_limits<int>::min()) {
935 O2_SIGNPOST_EVENT_EMIT(monitoring_service, sid, "flushChangedMetrics", "Value for %{public}s is too small, setting to INT_MIN",
936 spec.name.c_str());
937 value = (int64_t)std::numeric_limits<int>::min();
938 }
939 metric.addValue((int)value, "value");
940 }
941 if (spec.scope == DataProcessingStats::Scope::DPL) {
942 metric.addTag(o2::monitoring::tags::Key::Subsystem, o2::monitoring::tags::Value::DPL);
943 }
944 O2_SIGNPOST_EVENT_EMIT(monitoring_service, sid, "flushChangedMetrics", "Flushing metric %{public}s", spec.name.c_str());
945 monitoring.send(std::move(metric));
946 });
947 relayer.sendContextState();
948 monitoring.flushBuffer();
949 O2_SIGNPOST_END(monitoring_service, sid, "flush", "done flushing metrics");
950};
951} // namespace
952
954{
955 return ServiceSpec{
956 .name = "data-processing-stats",
957 .init = [](ServiceRegistryRef services, DeviceState& state, fair::mq::ProgOptions& options) -> ServiceHandle {
958 timespec now;
959 clock_gettime(CLOCK_REALTIME, &now);
960 uv_update_time(state.loop);
961 uint64_t offset = now.tv_sec * 1000 - uv_now(state.loop);
963 .minOnlinePublishInterval = std::stoi(options.GetProperty<std::string>("dpl-stats-min-online-publishing-interval").c_str()) * 1000};
966 config);
967 auto& runningWorkflow = services.get<RunningWorkflowInfo const>();
968
969 // It makes no sense to update the stats more often than every 5s
970 int quickUpdateInterval = 5000;
971 uint64_t quickRefreshInterval = 7000;
972 uint64_t onlineRefreshLatency = 60000; // For metrics which are reported online, we flush them every 60s regardless of their state.
973 using MetricSpec = DataProcessingStats::MetricSpec;
974 using Kind = DataProcessingStats::Kind;
975 using Scope = DataProcessingStats::Scope;
976
977#ifdef NDEBUG
978 bool enableDebugMetrics = false;
979#else
980 bool enableDebugMetrics = true;
981#endif
982 bool arrowAndResourceLimitingMetrics = false;
984 arrowAndResourceLimitingMetrics = true;
985 }
986
987 int64_t consumedTimeframesPublishInterval = 0;
989 consumedTimeframesPublishInterval = 5000;
990 }
991 // Input proxies should not report cpu_usage_fraction,
992 // because of the rate limiting which biases the measurement.
993 auto& spec = services.get<DeviceSpec const>();
994 bool enableCPUUsageFraction = true;
995 auto isProxy = [](DataProcessorLabel const& label) -> bool { return label == DataProcessorLabel{"input-proxy"}; };
996 if (std::find_if(spec.labels.begin(), spec.labels.end(), isProxy) != spec.labels.end()) {
997 O2_SIGNPOST_ID_GENERATE(mid, policies);
998 O2_SIGNPOST_EVENT_EMIT(policies, mid, "metrics", "Disabling cpu_usage_fraction metric for proxy %{public}s", spec.name.c_str());
999 enableCPUUsageFraction = false;
1000 }
1001
1002 std::vector<DataProcessingStats::MetricSpec> metrics = {
1003 MetricSpec{.name = "errors",
1005 .kind = Kind::UInt64,
1006 .scope = Scope::Online,
1007 .minPublishInterval = quickUpdateInterval,
1008 .maxRefreshLatency = quickRefreshInterval},
1009 MetricSpec{.name = "exceptions",
1011 .kind = Kind::UInt64,
1012 .scope = Scope::Online,
1013 .minPublishInterval = quickUpdateInterval},
1014 MetricSpec{.name = "inputs/relayed/pending",
1016 .kind = Kind::UInt64,
1017 .minPublishInterval = quickUpdateInterval},
1018 MetricSpec{.name = "inputs/relayed/incomplete",
1020 .kind = Kind::UInt64,
1021 .minPublishInterval = quickUpdateInterval},
1022 MetricSpec{.name = "inputs/relayed/total",
1024 .kind = Kind::UInt64,
1025 .minPublishInterval = quickUpdateInterval},
1026 MetricSpec{.name = "elapsed_time_ms",
1028 .kind = Kind::UInt64,
1029 .minPublishInterval = quickUpdateInterval},
1030 MetricSpec{.name = "total_wall_time_ms",
1032 .kind = Kind::UInt64,
1033 .minPublishInterval = quickUpdateInterval},
1034 MetricSpec{.name = "last_processed_input_size_byte",
1036 .kind = Kind::UInt64,
1037 .minPublishInterval = quickUpdateInterval},
1038 MetricSpec{.name = "total_processed_input_size_byte",
1040 .kind = Kind::UInt64,
1041 .scope = Scope::Online,
1042 .minPublishInterval = quickUpdateInterval},
1043 MetricSpec{.name = "total_sigusr1",
1045 .kind = Kind::UInt64,
1046 .minPublishInterval = quickUpdateInterval},
1047 MetricSpec{.name = "consumed-timeframes",
1049 .kind = Kind::UInt64,
1050 .minPublishInterval = consumedTimeframesPublishInterval,
1051 .maxRefreshLatency = quickRefreshInterval,
1052 .sendInitialValue = true},
1053 MetricSpec{.name = "min_input_latency_ms",
1055 .kind = Kind::UInt64,
1056 .scope = Scope::Online,
1057 .minPublishInterval = quickUpdateInterval},
1058 MetricSpec{.name = "max_input_latency_ms",
1060 .kind = Kind::UInt64,
1061 .minPublishInterval = quickUpdateInterval},
1062 MetricSpec{.name = "total_rate_in_mb_s",
1064 .kind = Kind::Rate,
1065 .scope = Scope::Online,
1066 .minPublishInterval = quickUpdateInterval,
1067 .maxRefreshLatency = onlineRefreshLatency,
1068 .sendInitialValue = true},
1069 MetricSpec{.name = "total_rate_out_mb_s",
1071 .kind = Kind::Rate,
1072 .scope = Scope::Online,
1073 .minPublishInterval = quickUpdateInterval,
1074 .maxRefreshLatency = onlineRefreshLatency,
1075 .sendInitialValue = true},
1076 MetricSpec{.name = "processing_rate_hz",
1078 .kind = Kind::Rate,
1079 .scope = Scope::Online,
1080 .minPublishInterval = quickUpdateInterval,
1081 .maxRefreshLatency = onlineRefreshLatency,
1082 .sendInitialValue = true},
1083 MetricSpec{.name = "cpu_usage_fraction",
1084 .enabled = enableCPUUsageFraction,
1086 .kind = Kind::Rate,
1087 .scope = Scope::Online,
1088 .minPublishInterval = quickUpdateInterval,
1089 .maxRefreshLatency = onlineRefreshLatency,
1090 .sendInitialValue = true},
1091 MetricSpec{.name = "performed_computations",
1093 .kind = Kind::UInt64,
1094 .scope = Scope::Online,
1095 .minPublishInterval = quickUpdateInterval,
1096 .maxRefreshLatency = onlineRefreshLatency,
1097 .sendInitialValue = true},
1098 MetricSpec{.name = "total_bytes_in",
1100 .kind = Kind::UInt64,
1101 .scope = Scope::Online,
1102 .minPublishInterval = quickUpdateInterval,
1103 .maxRefreshLatency = onlineRefreshLatency,
1104 .sendInitialValue = true},
1105 MetricSpec{.name = "total_bytes_out",
1107 .kind = Kind::UInt64,
1108 .scope = Scope::Online,
1109 .minPublishInterval = quickUpdateInterval,
1110 .maxRefreshLatency = onlineRefreshLatency,
1111 .sendInitialValue = true},
1112 MetricSpec{.name = fmt::format("available_managed_shm_{}", runningWorkflow.shmSegmentId),
1113 .metricId = (int)ProcessingStatsId::AVAILABLE_MANAGED_SHM_BASE + (runningWorkflow.shmSegmentId % 512),
1114 .kind = Kind::UInt64,
1115 .scope = Scope::Online,
1116 .minPublishInterval = 500,
1117 .maxRefreshLatency = onlineRefreshLatency,
1118 .sendInitialValue = true},
1119 MetricSpec{.name = "malformed_inputs", .metricId = static_cast<short>(ProcessingStatsId::MALFORMED_INPUTS), .kind = Kind::UInt64, .minPublishInterval = quickUpdateInterval},
1120 MetricSpec{.name = "dropped_computations", .metricId = static_cast<short>(ProcessingStatsId::DROPPED_COMPUTATIONS), .kind = Kind::UInt64, .minPublishInterval = quickUpdateInterval},
1121 MetricSpec{.name = "dropped_incoming_messages", .metricId = static_cast<short>(ProcessingStatsId::DROPPED_INCOMING_MESSAGES), .kind = Kind::UInt64, .minPublishInterval = quickUpdateInterval},
1122 MetricSpec{.name = "relayed_messages", .metricId = static_cast<short>(ProcessingStatsId::RELAYED_MESSAGES), .kind = Kind::UInt64, .minPublishInterval = quickUpdateInterval},
1123 MetricSpec{.name = "arrow-bytes-destroyed",
1124 .enabled = arrowAndResourceLimitingMetrics,
1125 .metricId = static_cast<short>(ProcessingStatsId::ARROW_BYTES_DESTROYED),
1126 .kind = Kind::UInt64,
1127 .scope = Scope::DPL,
1128 .minPublishInterval = 0,
1129 .maxRefreshLatency = 10000,
1130 .sendInitialValue = true},
1131 MetricSpec{.name = "arrow-messages-destroyed",
1132 .enabled = arrowAndResourceLimitingMetrics,
1133 .metricId = static_cast<short>(ProcessingStatsId::ARROW_MESSAGES_DESTROYED),
1134 .kind = Kind::UInt64,
1135 .scope = Scope::DPL,
1136 .minPublishInterval = 0,
1137 .maxRefreshLatency = 10000,
1138 .sendInitialValue = true},
1139 MetricSpec{.name = "arrow-bytes-created",
1140 .enabled = arrowAndResourceLimitingMetrics,
1141 .metricId = static_cast<short>(ProcessingStatsId::ARROW_BYTES_CREATED),
1142 .kind = Kind::UInt64,
1143 .scope = Scope::DPL,
1144 .minPublishInterval = 0,
1145 .maxRefreshLatency = 10000,
1146 .sendInitialValue = true},
1147 MetricSpec{.name = "arrow-messages-created",
1148 .enabled = arrowAndResourceLimitingMetrics,
1149 .metricId = static_cast<short>(ProcessingStatsId::ARROW_MESSAGES_CREATED),
1150 .kind = Kind::UInt64,
1151 .scope = Scope::DPL,
1152 .minPublishInterval = 0,
1153 .maxRefreshLatency = 10000,
1154 .sendInitialValue = true},
1155 MetricSpec{.name = "arrow-bytes-expired",
1156 .enabled = arrowAndResourceLimitingMetrics,
1157 .metricId = static_cast<short>(ProcessingStatsId::ARROW_BYTES_EXPIRED),
1158 .kind = Kind::UInt64,
1159 .scope = Scope::DPL,
1160 .minPublishInterval = 0,
1161 .maxRefreshLatency = 10000,
1162 .sendInitialValue = true},
1163 MetricSpec{.name = "shm-offer-bytes-consumed",
1164 .enabled = arrowAndResourceLimitingMetrics,
1165 .metricId = static_cast<short>(ProcessingStatsId::SHM_OFFER_BYTES_CONSUMED),
1166 .kind = Kind::UInt64,
1167 .scope = Scope::DPL,
1168 .minPublishInterval = 0,
1169 .maxRefreshLatency = 10000,
1170 .sendInitialValue = true},
1171 MetricSpec{.name = "timeslice-offer-number-consumed",
1172 .enabled = arrowAndResourceLimitingMetrics,
1173 .metricId = static_cast<short>(ProcessingStatsId::TIMESLICE_OFFER_NUMBER_CONSUMED),
1174 .kind = Kind::UInt64,
1175 .scope = Scope::DPL,
1176 .minPublishInterval = 0,
1177 .maxRefreshLatency = 10000,
1178 .sendInitialValue = true},
1179 MetricSpec{.name = "timeslices-expired",
1180 .enabled = arrowAndResourceLimitingMetrics,
1181 .metricId = static_cast<short>(ProcessingStatsId::TIMESLICE_NUMBER_EXPIRED),
1182 .kind = Kind::UInt64,
1183 .scope = Scope::DPL,
1184 .minPublishInterval = 0,
1185 .maxRefreshLatency = 10000,
1186 .sendInitialValue = true},
1187 MetricSpec{.name = "timeslices-started",
1188 .enabled = arrowAndResourceLimitingMetrics,
1189 .metricId = static_cast<short>(ProcessingStatsId::TIMESLICE_NUMBER_STARTED),
1190 .kind = Kind::UInt64,
1191 .scope = Scope::DPL,
1192 .minPublishInterval = 0,
1193 .maxRefreshLatency = 10000,
1194 .sendInitialValue = true},
1195 MetricSpec{.name = "timeslices-done",
1196 .enabled = arrowAndResourceLimitingMetrics,
1197 .metricId = static_cast<short>(ProcessingStatsId::TIMESLICE_NUMBER_DONE),
1198 .kind = Kind::UInt64,
1199 .scope = Scope::DPL,
1200 .minPublishInterval = 0,
1201 .maxRefreshLatency = 10000,
1202 .sendInitialValue = true},
1203 MetricSpec{.name = "resources-missing",
1204 .enabled = enableDebugMetrics,
1205 .metricId = static_cast<short>(ProcessingStatsId::RESOURCES_MISSING),
1206 .kind = Kind::UInt64,
1207 .scope = Scope::DPL,
1208 .minPublishInterval = 1000,
1209 .maxRefreshLatency = 1000,
1210 .sendInitialValue = true},
1211 MetricSpec{.name = "resources-insufficient",
1212 .enabled = enableDebugMetrics,
1213 .metricId = static_cast<short>(ProcessingStatsId::RESOURCES_INSUFFICIENT),
1214 .kind = Kind::UInt64,
1215 .scope = Scope::DPL,
1216 .minPublishInterval = 1000,
1217 .maxRefreshLatency = 1000,
1218 .sendInitialValue = true},
1219 MetricSpec{.name = "resources-satisfactory",
1220 .enabled = enableDebugMetrics,
1221 .metricId = static_cast<short>(ProcessingStatsId::RESOURCES_SATISFACTORY),
1222 .kind = Kind::UInt64,
1223 .scope = Scope::DPL,
1224 .minPublishInterval = 1000,
1225 .maxRefreshLatency = 1000,
1226 .sendInitialValue = true},
1227 MetricSpec{.name = "resource-offer-expired",
1228 .enabled = arrowAndResourceLimitingMetrics,
1229 .metricId = static_cast<short>(ProcessingStatsId::RESOURCE_OFFER_EXPIRED),
1230 .kind = Kind::UInt64,
1231 .scope = Scope::DPL,
1232 .minPublishInterval = 0,
1233 .maxRefreshLatency = 10000,
1234 .sendInitialValue = true},
1235 MetricSpec{.name = "ccdb-cache-hit",
1236 .enabled = true,
1237 .metricId = static_cast<short>(ProcessingStatsId::CCDB_CACHE_HIT),
1238 .kind = Kind::UInt64,
1239 .scope = Scope::DPL,
1240 .minPublishInterval = 1000,
1241 .maxRefreshLatency = 10000,
1242 .sendInitialValue = true},
1243 MetricSpec{.name = "ccdb-cache-miss",
1244 .enabled = true,
1245 .metricId = static_cast<short>(ProcessingStatsId::CCDB_CACHE_MISS),
1246 .kind = Kind::UInt64,
1247 .scope = Scope::DPL,
1248 .minPublishInterval = 1000,
1249 .maxRefreshLatency = 10000,
1250 .sendInitialValue = true},
1251 MetricSpec{.name = "ccdb-cache-failure",
1252 .enabled = true,
1253 .metricId = static_cast<short>(ProcessingStatsId::CCDB_CACHE_FAILURE),
1254 .kind = Kind::UInt64,
1255 .scope = Scope::DPL,
1256 .minPublishInterval = 1000,
1257 .maxRefreshLatency = 10000,
1258 .sendInitialValue = true},
1259 MetricSpec{.name = "ccdb-cache-fetched-bytes",
1260 .enabled = true,
1261 .metricId = static_cast<short>(ProcessingStatsId::CCDB_CACHE_FETCHED_BYTES),
1262 .kind = Kind::UInt64,
1263 .scope = Scope::DPL,
1264 .minPublishInterval = 1000,
1265 .maxRefreshLatency = 10000,
1266 .sendInitialValue = true},
1267 MetricSpec{.name = "ccdb-cache-requested-bytes",
1268 .enabled = true,
1269 .metricId = static_cast<short>(ProcessingStatsId::CCDB_CACHE_REQUESTED_BYTES),
1270 .kind = Kind::UInt64,
1271 .scope = Scope::DPL,
1272 .minPublishInterval = 1000,
1273 .maxRefreshLatency = 10000,
1274 .sendInitialValue = true}};
1275
1276 for (auto& metric : metrics) {
1277 if (metric.metricId == (int)ProcessingStatsId::AVAILABLE_MANAGED_SHM_BASE + (runningWorkflow.shmSegmentId % 512)) {
1278 if (spec.name.compare("readout-proxy") == 0) {
1279 stats->hasAvailSHMMetric = true;
1280 } else {
1281 continue;
1282 }
1283 }
1284 stats->registerMetric(metric);
1285 }
1286
1287 return ServiceHandle{TypeIdHelpers::uniqueId<DataProcessingStats>(), stats};
1288 },
1289 .configure = noConfiguration(),
1290 .postProcessing = [](ProcessingContext& context, void* service) {
1291 auto* stats = (DataProcessingStats*)service;
1293 .preDangling = [](DanglingContext& context, void* service) {
1294 auto* stats = (DataProcessingStats*)service;
1295 sendRelayerMetrics(context.services(), *stats);
1296 flushMetrics(context.services(), *stats); },
1297 .postDangling = [](DanglingContext& context, void* service) {
1298 auto* stats = (DataProcessingStats*)service;
1299 sendRelayerMetrics(context.services(), *stats);
1300 flushMetrics(context.services(), *stats); },
1301 .preEOS = [](EndOfStreamContext& context, void* service) {
1302 auto* stats = (DataProcessingStats*)service;
1303 sendRelayerMetrics(context.services(), *stats);
1304 flushMetrics(context.services(), *stats); },
1305 .preLoop = [](ServiceRegistryRef ref, void* service) {
1306 auto* stats = (DataProcessingStats*)service;
1307 flushMetrics(ref, *stats); },
1308 .kind = ServiceKind::Serial};
1309}
1310
1311// This is similar to the dataProcessingStats, but it designed to synchronize
1312// history-less metrics which are e.g. used for the GUI.
1314{
1315 return ServiceSpec{
1316 .name = "data-processing-states",
1317 .init = [](ServiceRegistryRef services, DeviceState& state, fair::mq::ProgOptions& options) -> ServiceHandle {
1318 timespec now;
1319 clock_gettime(CLOCK_REALTIME, &now);
1320 uv_update_time(state.loop);
1321 uint64_t offset = now.tv_sec * 1000 - uv_now(state.loop);
1324 states->registerState({"dummy_state", (short)ProcessingStateId::DUMMY_STATE});
1325 return ServiceHandle{TypeIdHelpers::uniqueId<DataProcessingStates>(), states};
1326 },
1327 .configure = noConfiguration(),
1328 .postProcessing = [](ProcessingContext& context, void* service) {
1329 auto* states = (DataProcessingStates*)service;
1330 states->processCommandQueue(); },
1331 .preDangling = [](DanglingContext& context, void* service) {
1332 auto* states = (DataProcessingStates*)service;
1333 flushStates(context.services(), *states); },
1334 .postDangling = [](DanglingContext& context, void* service) {
1335 auto* states = (DataProcessingStates*)service;
1336 flushStates(context.services(), *states); },
1337 .preEOS = [](EndOfStreamContext& context, void* service) {
1338 auto* states = (DataProcessingStates*)service;
1339 flushStates(context.services(), *states); },
1340 .kind = ServiceKind::Global};
1341}
1342
1344};
1345
1347{
1348 return ServiceSpec{
1349 .name = "gui-metrics",
1350 .init = [](ServiceRegistryRef services, DeviceState&, fair::mq::ProgOptions& options) -> ServiceHandle {
1351 auto* stats = new GUIMetrics();
1352 auto& monitoring = services.get<Monitoring>();
1353 auto& spec = services.get<DeviceSpec const>();
1354 monitoring.send({(int)spec.inputChannels.size(), fmt::format("oldest_possible_timeslice/h"), o2::monitoring::Verbosity::Debug});
1355 monitoring.send({(int)1, fmt::format("oldest_possible_timeslice/w"), o2::monitoring::Verbosity::Debug});
1356 monitoring.send({(int)spec.outputChannels.size(), fmt::format("oldest_possible_output/h"), o2::monitoring::Verbosity::Debug});
1357 monitoring.send({(int)1, fmt::format("oldest_possible_output/w"), o2::monitoring::Verbosity::Debug});
1358 return ServiceHandle{TypeIdHelpers::uniqueId<GUIMetrics>(), stats};
1359 },
1360 .configure = noConfiguration(),
1361 .postProcessing = [](ProcessingContext& context, void* service) {
1362 auto& relayer = context.services().get<DataRelayer>();
1363 auto& monitoring = context.services().get<Monitoring>();
1364 auto& spec = context.services().get<DeviceSpec const>();
1365 auto oldestPossibleOutput = relayer.getOldestPossibleOutput();
1366 for (size_t ci; ci < spec.outputChannels.size(); ++ci) {
1367 monitoring.send({(uint64_t)oldestPossibleOutput.timeslice.value, fmt::format("oldest_possible_output/{}", ci), o2::monitoring::Verbosity::Debug});
1368 } },
1369 .domainInfoUpdated = [](ServiceRegistryRef registry, size_t timeslice, ChannelIndex channel) {
1370 auto& monitoring = registry.get<Monitoring>();
1371 monitoring.send({(uint64_t)timeslice, fmt::format("oldest_possible_timeslice/{}", channel.value), o2::monitoring::Verbosity::Debug}); },
1372 .active = false,
1373 .kind = ServiceKind::Serial};
1374}
1375
1377{
1378 return ServiceSpec{
1379 .name = "object-cache",
1380 .init = [](ServiceRegistryRef, DeviceState&, fair::mq::ProgOptions&) -> ServiceHandle {
1381 auto* cache = new ObjectCache();
1382 return ServiceHandle{TypeIdHelpers::uniqueId<ObjectCache>(), cache};
1383 },
1384 .configure = noConfiguration(),
1386}
1387
1389{
1390 return ServiceSpec{
1391 .name = "data-processing-context",
1392 .init = [](ServiceRegistryRef, DeviceState&, fair::mq::ProgOptions&) -> ServiceHandle {
1393 return ServiceHandle{TypeIdHelpers::uniqueId<DataProcessorContext>(), new DataProcessorContext()};
1394 },
1395 .configure = noConfiguration(),
1396 .exit = [](ServiceRegistryRef, void* service) { auto* context = (DataProcessorContext*)service; delete context; },
1397 .kind = ServiceKind::Serial};
1398}
1399
1401{
1402 return ServiceSpec{
1403 .name = "data-allocator",
1404 .uniqueId = simpleServiceId<DataAllocator>(),
1405 .init = [](ServiceRegistryRef ref, DeviceState&, fair::mq::ProgOptions&) -> ServiceHandle {
1406 return ServiceHandle{
1407 .hash = TypeIdHelpers::uniqueId<DataAllocator>(),
1408 .instance = new DataAllocator(ref),
1409 .kind = ServiceKind::Stream,
1410 .name = "data-allocator",
1411 };
1412 },
1413 .configure = noConfiguration(),
1414 .kind = ServiceKind::Stream};
1415}
1416
1418std::vector<ServiceSpec> CommonServices::defaultServices(std::string extraPlugins, int numThreads)
1419{
1420 std::vector<ServiceSpec> specs{
1424 asyncQueue(),
1431 controlSpec(),
1432 rootFileSpec(),
1433 parallelSpec(),
1434 callbacksSpec(),
1437 dataRelayer(),
1439 dataSender(),
1440 objectCache(),
1441 ccdbSupportSpec()};
1442
1444 specs.push_back(ArrowSupport::arrowBackendSpec());
1445 }
1448 specs.push_back(decongestionSpec());
1449
1450 std::string loadableServicesStr = extraPlugins;
1451 // Do not load InfoLogger by default if we are not at P2.
1453 if (loadableServicesStr.empty() == false) {
1454 loadableServicesStr += ",";
1455 }
1456 loadableServicesStr += "O2FrameworkDataTakingSupport:InfoLoggerContext,O2FrameworkDataTakingSupport:InfoLogger";
1457 }
1458 // Load plugins depending on the environment
1459 std::vector<LoadablePlugin> loadablePlugins = {};
1460 char* loadableServicesEnv = getenv("DPL_LOAD_SERVICES");
1461 // String to define the services to load is:
1462 //
1463 // library1:name1,library2:name2,...
1464 if (loadableServicesEnv) {
1465 if (loadableServicesStr.empty() == false) {
1466 loadableServicesStr += ",";
1467 }
1468 loadableServicesStr += loadableServicesEnv;
1469 }
1470 loadablePlugins = PluginManager::parsePluginSpecString(loadableServicesStr.c_str());
1471 PluginManager::loadFromPlugin<ServiceSpec, ServicePlugin>(loadablePlugins, specs);
1472 // I should make it optional depending wether the GUI is there or not...
1473 specs.push_back(CommonServices::guiMetricsSpec());
1474 if (numThreads) {
1475 specs.push_back(threadPool(numThreads));
1476 }
1477 return specs;
1478}
1479
1480std::vector<ServiceSpec> CommonServices::arrowServices()
1481{
1482 return {
1485 };
1486}
1487
1488} // namespace o2::framework
std::vector< std::string > labels
benchmark::State & state
std::vector< OutputRoute > routes
o2::monitoring::tags::Key Key
o2::monitoring::Metric Metric
o2::monitoring::tags::Value Value
#define MONITORING_QUEUE_SIZE
#define O2_BUILTIN_LIKELY(x)
std::ostringstream debug
int16_t time
Definition RawEventData.h:4
void output(const std::map< std::string, ChannelStat > &channels)
Definition rawdump.cxx:197
#define O2_SIGNPOST_EVENT_EMIT_ERROR(log, id, name, format,...)
Definition Signpost.h:554
#define O2_DECLARE_DYNAMIC_LOG(name)
Definition Signpost.h:490
#define O2_SIGNPOST_ID_FROM_POINTER(name, log, pointer)
Definition Signpost.h:506
#define O2_SIGNPOST_END(log, id, name, format,...)
Definition Signpost.h:609
#define O2_SIGNPOST_ID_GENERATE(name, log)
Definition Signpost.h:507
#define O2_SIGNPOST_EVENT_EMIT_CRITICAL(log, id, name, format,...)
Definition Signpost.h:574
#define O2_SIGNPOST_EVENT_EMIT_WARN(log, id, name, format,...)
Definition Signpost.h:564
#define O2_SIGNPOST_EVENT_EMIT(log, id, name, format,...)
Definition Signpost.h:523
#define O2_SIGNPOST_START(log, id, name, format,...)
Definition Signpost.h:603
o2::monitoring::Monitoring Monitoring
decltype(auto) make(const Output &spec, Args... args)
int countDeviceOutputs(bool excludeDPLOrigin=false)
Allow injecting policies on send.
Definition DataSender.h:34
DataAllocator & outputs()
The data allocator is used to allocate memory for the output data.
InputRecord & inputs()
The inputs associated with this processing context.
ServiceRegistryRef services()
The services registry associated with this processing context.
void registerService(ServiceTypeHash typeHash, void *service, ServiceKind kind, char const *name=nullptr) const
A text based way of communicating with the driver.
OldestOutputInfo getOldestPossibleOutput() const
GLsizei GLenum const void GLuint GLsizei GLfloat * metrics
Definition glcorearb.h:5500
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLintptr offset
Definition glcorearb.h:660
GLuint GLsizei const GLchar * label
Definition glcorearb.h:2519
GLuint GLuint stream
Definition glcorearb.h:1806
GLint ref
Definition glcorearb.h:291
GLuint id
Definition glcorearb.h:650
GLuint * states
Definition glcorearb.h:4932
Defining ITS Vertex explicitly as messageable.
Definition Cartesian.h:288
@ DPL
The channel is a normal input channel.
const DataProcessorLabel suppressDomainInfoLabel
std::pair< std::string, unsigned short > parse_websocket_url(char const *url)
@ NoTransition
No pending transitions.
auto decongestionCallbackPastOldest
auto decongestionCallbackOrdered
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
static ServiceSpec arrowTableSlicingCacheSpec()
static ServiceSpec arrowBackendSpec()
static ServiceSpec arrowTableSlicingCacheDefSpec()
static AsyncTaskId create(AsyncQueue &queue, AsyncTaskSpec spec)
static void post(AsyncQueue &queue, AsyncTask const &task)
static void reset(AsyncQueue &queue)
Reset the queue to its initial state.
An actuatual task to be executed.
Definition AsyncQueue.h:32
static ServiceSpec dataRelayer()
static ServiceSpec callbacksSpec()
static ServiceSpec monitoringSpec()
static ServiceSpec dataSender()
static ServiceSpec timesliceIndex()
static std::vector< ServiceSpec > defaultServices(std::string extraPlugins="", int numWorkers=0)
Split a string into a vector of strings using : as a separator.
static ServiceSpec timingInfoSpec()
static ServiceConfigureCallback noConfiguration()
static ServiceSpec asyncQueue()
static ServiceSpec decongestionSpec()
static ServiceSpec dataProcessorContextSpec()
static ServiceSpec dataProcessingStats()
static std::vector< ServiceSpec > arrowServices()
static ServiceSpec rootFileSpec()
static ServiceSpec controlSpec()
static ServiceSpec configurationSpec()
static ServiceSpec ccdbSupportSpec()
static ServiceSpec datatakingContextSpec()
static ServiceSpec guiMetricsSpec()
static ServiceSpec dataProcessingStates()
static ServiceSpec tracingSpec()
static ServiceSpec dataAllocatorSpec()
static ServiceSpec driverClientSpec()
static ServiceSpec streamContextSpec()
static ServiceSpec threadPool(int numWorkers)
static ServiceSpec parallelSpec()
static bool sendOldestPossibleTimeframe(ServiceRegistryRef const &ref, ForwardChannelInfo const &info, ForwardChannelState &state, size_t timeslice)
static void broadcastOldestPossibleTimeslice(ServiceRegistryRef const &ref, size_t timeslice)
Broadcast the oldest possible timeslice to all channels in output.
Helper struct to hold statistics about the data processing happening.
@ SetIfPositive
Set the value to the specified value.
@ InstantaneousRate
Update the rate of the metric given the cumulative value since last time it got published.
@ Add
Update the rate of the metric given the amount since the last time.
A label that can be associated to a DataProcessorSpec.
static std::string describe(InputSpec const &spec)
static ConcreteDataMatcher asConcreteDataMatcher(InputSpec const &input)
static bool match(InputSpec const &spec, ConcreteDataMatcher const &target)
DeploymentMode deploymentMode
Where we thing this is running.
TimesliceIndex::OldestOutputInfo oldestPossibleOutput
static DeploymentMode deploymentMode()
static unsigned int pipelineLength(unsigned int minLength)
get max number of timeslices in the queue
static bool onlineDeploymentMode()
@true if running online
std::string name
The name of the associated DataProcessorSpec.
Definition DeviceSpec.h:50
size_t inputTimesliceId
The time pipelining id of this particular device.
Definition DeviceSpec.h:68
Running state information of a given device.
Definition DeviceState.h:34
static size_t maxLanes(std::vector< InputRoute > const &routes)
header::DataOrigin origin
Definition Output.h:28
static std::vector< LoadablePlugin > parsePluginSpecString(char const *str)
Parse a comma separated list of <library>:<plugin-name> plugin declarations.
static bool isResourcesMonitoringEnabled(unsigned short interval) noexcept
Information about the running workflow.
ServiceKind kind
Kind of service.
unsigned int hash
Unique hash associated to the type of service.
std::string name
Name of the service.
ServicePostDispatching postDispatching
ServiceKind kind
Kind of service being specified.
static std::function< int64_t(int64_t base, int64_t offset)> defaultCPUTimeConfigurator(uv_loop_t *loop)
static std::function< void(int64_t &base, int64_t &offset)> defaultRealtimeBaseConfigurator(uint64_t offset, uv_loop_t *loop)
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"