46#include <Monitoring/Metric.h>
47#include <Monitoring/Monitoring.h>
49#include <fairlogger/Logger.h>
50#include <fairmq/Channel.h>
52#include <fairmq/shmem/Message.h>
53#include <fairmq/Device.h>
54#include <fmt/format.h>
55#include <fmt/ostream.h>
74 std::vector<InputRoute>
const&
routes,
79 mTimesliceIndex{
index},
80 mCompletionPolicy{policy},
85 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
88 if (pipelineLength == -1) {
89 auto getPipelineLengthHelper = [&services]() {
96 static int detectedPipelineLength = getPipelineLengthHelper();
97 pipelineLength = detectedPipelineLength;
105 auto numInputTypes = mDistinctRoutesIndex.size();
107 std::string queries =
"";
108 for (
short i = 0;
i < numInputTypes; ++
i) {
110 assert(mDistinctRoutesIndex[
i] <
routes.size());
111 mInputs.push_back(
routes[mDistinctRoutesIndex[
i]].matcher);
112 auto& matcher =
routes[mDistinctRoutesIndex[
i]].matcher;
118 states.registerState({.name =
"data_queries", .stateId = stateId, .sendInitialValue =
true, .defaultEnabled =
true});
120 states.processCommandQueue();
125 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
133 LOGP(
debug,
"DataRelayer::processDanglingInputs");
134 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
139 if (expirationHandlers.empty()) {
140 LOGP(
debug,
"DataRelayer::processDanglingInputs: No expiration handlers");
144 std::vector<TimesliceSlot> slotsCreatedByHandlers;
146 LOGP(
debug,
"Creating new slot");
147 for (
auto& handler : expirationHandlers) {
148 LOGP(
debug,
"handler.creator for {}", handler.name);
149 auto channelIndex = deviceProxy.getInputChannelIndex(handler.routeIndex);
150 slotsCreatedByHandlers.push_back(handler.creator(services, channelIndex));
155 for (
auto slot : slotsCreatedByHandlers) {
161 if (validSlots > 0) {
163 LOGP(
debug,
"DataRelayer::processDanglingInputs: {} slots created by handler", validSlots);
165 LOGP(
debug,
"DataRelayer::processDanglingInputs: no slots created by handler");
169 int headerPresent = 0;
170 int payloadPresent = 0;
173 int checkerDenied = 0;
174 for (
size_t ti = 0; ti < mTimesliceIndex.
size(); ++ti) {
176 if (mTimesliceIndex.
isValid(slot) ==
false) {
179 assert(mDistinctRoutesIndex.empty() ==
false);
183 for (
size_t ei = 0; ei < expirationHandlers.size(); ++ei) {
184 auto& expirator = expirationHandlers[ei];
187 auto& part = mCache[ti * mDistinctRoutesIndex.size() + expirator.routeIndex.value];
188 if (!part.empty() && (part |
get_header{0}) !=
nullptr) {
192 if (!part.empty() && (part |
get_payload{0, 0}) !=
nullptr) {
197 if (!expirator.checker) {
201 if (slotsCreatedByHandlers[ei] != slot) {
206 auto getPartialRecord = [&cache = mCache, numInputTypes = mDistinctRoutesIndex.size()](
int li) -> std::span<std::vector<fair::mq::MessagePtr>
const> {
207 auto offset = li * numInputTypes;
208 assert(cache.size() >=
offset + numInputTypes);
210 auto const end = cache.data() +
offset + numInputTypes;
214 auto partial = getPartialRecord(ti);
215 auto nPartsGetter = [&partial](
size_t idx) {
218 auto refCountGetter = [&partial](
size_t idx) ->
int {
219 auto& header =
static_cast<const fair::mq::shmem::Message&
>(*(partial[idx] |
get_header{0}));
220 return header.GetRefCount();
223 if (!partial[idx].
empty()) {
224 auto const& headerMsg = partial[idx][
indices.headerIdx];
225 auto const& payloadMsg = partial[idx][
indices.payloadIdx];
228 reinterpret_cast<const char*
>(headerMsg->GetData()),
229 payloadMsg ?
reinterpret_cast<char const*
>(payloadMsg->GetData()) :
nullptr,
230 payloadMsg ? payloadMsg->GetSize() : 0};
237 return next.headerIdx < partial[idx].size() ? next :
DataRefIndices{size_t(-1), size_t(-1)};
239 auto payloadGetter = [&partial](
size_t idx,
DataRefIndices current) -> fair::mq::Message* {
240 auto const& msgs = partial[idx];
241 if (msgs.size() <= current.payloadIdx || !msgs[current.payloadIdx]) {
244 return msgs[current.payloadIdx].get();
246 InputSpan span{nPartsGetter, refCountGetter, indicesGetter, nextIndicesGetter, payloadGetter,
static_cast<size_t>(partial.size())};
249 if (expirator.checker(services, timestamp.value, span) ==
false) {
254 assert(ti * mDistinctRoutesIndex.size() + expirator.routeIndex.value < mCache.size());
255 assert(expirator.handler);
257 expirator.handler(services, newRef, variables);
259 part.emplace_back(std::move(newRef.
header));
260 part.emplace_back(std::move(newRef.
payload));
261 activity.expiredSlots++;
268 LOGP(
debug,
"DataRelayer::processDanglingInputs headerPresent:{}, payloadPresent:{}, noCheckers:{}, badSlot:{}, checkerDenied:{}",
269 headerPresent, payloadPresent, noCheckers, badSlot, checkerDenied);
277 std::vector<DataDescriptorMatcher>
const&
matchers,
278 std::vector<size_t>
const&
index,
281 for (
size_t ri = 0, re =
index.size(); ri < re; ++ri) {
284 if (matcher.match(
reinterpret_cast<char const*
>(
data), context)) {
297 static const std::string nullstring{
"null"};
301 static std::string
state =
"";
304 auto var = variables.
get(
i);
305 if (
auto pval = std::get_if<uint64_t>(&var)) {
307 }
else if (
auto pval = std::get_if<uint32_t>(&var)) {
309 }
else if (
auto pval2 = std::get_if<std::string>(&var)) {
317 .data =
state.data()});
325 LOGP(
debug,
"DataRelayer::setOldestPossibleInput {} from channel {}", newOldest.timeslice.value, newOldest.channel.value);
326 static bool dontDrop = getenv(
"DPL_DONT_DROP_OLD_TIMESLICE") && atoi(getenv(
"DPL_DONT_DROP_OLD_TIMESLICE"));
330 for (
size_t si = 0; si < mCache.size() / mInputs.size(); ++si) {
335 if (mTimesliceIndex.
isValid({si})) {
336 LOGP(
debug,
"Keeping slot {} because data has timestamp {} while oldest possible timestamp is {}", si, timestamp.value, newOldest.timeslice.value);
340 mPruneOps.push_back(
PruneOp{si});
341 bool didDrop =
false;
342 for (
size_t mi = 0; mi < mInputs.size(); ++mi) {
343 auto& input = mInputs[mi];
344 auto&
element = mCache[si * mInputs.size() + mi];
346 if (input.lifetime != Lifetime::Condition && mCompletionPolicy.
name !=
"internal-dpl-injected-dummy-sink") {
350 LOGP(warning,
"Stop transition requested. Dropping incomplete {} Lifetime::{} data in slot {} with timestamp {} < {} as it will never be completed.",
DataSpecUtils::describe(input), input.lifetime, si, timestamp.value, newOldest.timeslice.value);
352 LOGP(error,
"Dropping incomplete {} Lifetime::{} data in slot {} with timestamp {} < {} as it can never be completed.",
DataSpecUtils::describe(input), input.lifetime, si, timestamp.value, newOldest.timeslice.value);
356 "Silently dropping data {} in pipeline slot {} because it has timeslice {} < {} after receiving data from channel {}."
357 "Because Lifetime::Timeframe data not there and not expected (e.g. due to sampling) we drop non sampled, non timeframe data (e.g. Conditions).",
365 for (
size_t mi = 0; mi < mInputs.size(); ++mi) {
366 auto& input = mInputs[mi];
367 if (input.lifetime == Lifetime::Timer) {
370 auto&
element = mCache[si * mInputs.size() + mi];
376 O2_SIGNPOST_EVENT_EMIT(calibration, cid,
"expected_missing_data",
"Expected missing %{public}s (lifetime:%d) while dropping non-calibration data in slot %zu with timestamp %zu < %zu.",
379 LOGP(info,
"Missing {} (lifetime:{}) while dropping incomplete data in slot {} with timestamp {} < {}.",
DataSpecUtils::describe(input), input.lifetime, si, timestamp.value, newOldest.timeslice.value);
384 O2_SIGNPOST_EVENT_EMIT_INFO(calibration, cid,
"expected_missing_data",
"Not processing in calibration mode: missing %s (lifetime:%d) while dropping incomplete data in slot %zu with timestamp %zu < %zu.",
387 LOGP(error,
"Missing {} (lifetime:{}) while dropping incomplete data in slot {} with timestamp {} < {}.",
DataSpecUtils::describe(input), input.lifetime, si, timestamp.value, newOldest.timeslice.value);
403 for (
auto&
op : mPruneOps) {
416 &cachedStateMetrics = mCachedStateMetrics,
417 numInputTypes = mDistinctRoutesIndex.size(),
418 &
index = mTimesliceIndex,
421 auto oldestPossibleTimeslice =
index.getOldestPossibleOutput();
423 std::vector<std::vector<fair::mq::MessagePtr>> dropped(numInputTypes);
424 for (
size_t ai = 0, ae = numInputTypes; ai != ae; ++ai) {
425 auto cacheId = slot.
index * numInputTypes + ai;
429 if (!cache[cacheId].
empty()) {
430 dropped[ai] = std::move(cache[cacheId]);
433 bool anyDropped = std::any_of(dropped.begin(), dropped.end(), [](
auto&
m) { return !m.empty(); });
436 O2_SIGNPOST_EVENT_EMIT(data_relayer, aid,
"pruneCache",
"Dropping stuff from slot %zu with timeslice %zu", slot.
index, oldestPossibleTimeslice.timeslice.value);
437 std::vector<std::span<fair::mq::MessagePtr>> droppedSpans(dropped.size());
438 for (
size_t ai = 0, ae = dropped.size(); ai != ae; ++ai) {
439 droppedSpans[ai] = dropped[ai];
441 onDrop(slot, droppedSpans, oldestPossibleTimeslice);
444 assert(cache.empty() ==
false);
445 assert(
index.size() * numInputTypes == cache.size());
449 assert(numInputTypes * slot.
index < cache.size());
450 for (
size_t ai = slot.
index * numInputTypes, ae = ai + numInputTypes; ai != ae; ++ai) {
461 auto* dph = o2::header::get<DataProcessingHeader*>(
first->GetData());
467 std::unique_ptr<fair::mq::Message>* messages,
474 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
480 return (slot.index % maxLanes) == (currentLane % maxLanes);
485 auto getInputTimeslice = [&
matchers = mInputMatchers,
486 &distinctRoutes = mDistinctRoutesIndex,
489 -> std::tuple<int, TimesliceId> {
502 if (
auto pval = std::get_if<uint64_t>(&context.get(0))) {
504 return {input, timeslice};
514 auto saveInSlot = [&cachedStateMetrics = mCachedStateMetrics,
520 &services = mContext,
523 O2_SIGNPOST_EVENT_EMIT(data_relayer, aid,
"saveInSlot",
"saving %{public}s@%zu in slot %zu from %{public}s",
524 fmt::format(
"{:x}", *o2::header::get<DataHeader*>(messages[0]->GetData())).c_str(),
525 timeslice.value, slot.index,
527 auto cacheIdx = numInputTypes * slot.index + input;
528 auto&
target = cache[cacheIdx];
532 assert(nPayloads > 0);
536 auto allMessages = std::span<fair::mq::MessagePtr>(messages, messages + nMessages);
538 onInsertion(services, allMessages);
540 for (
size_t mi = 0; mi < nMessages; ++mi) {
541 assert(mi + nPayloads < nMessages);
547 "Dropping incoming %zu messages because they are data processing.", nPayloads);
549 for (
size_t i = mi;
i < mi + nPayloads + 1;
i++) {
550 auto discard = std::move(messages[
i]);
555 auto span = std::span<fair::mq::MessagePtr>(messages + mi, messages + mi + nPayloads + 1);
558 for (
size_t i = 0;
i < nPayloads + 1; ++
i) {
559 target.emplace_back(std::move(span[
i]));
598 auto&
index = mTimesliceIndex;
600 bool needsCleaning =
false;
603 for (
size_t ci = 0; ci <
index.size(); ++ci) {
605 if (!isSlotInLane(slot)) {
608 if (
index.isValid(slot) ==
false) {
611 std::tie(input, timeslice) = getInputTimeslice(
index.getVariablesForSlot(slot));
620 for (
size_t ci = 0; ci <
index.size(); ++ci) {
622 if (
index.isValid(slot) ==
true) {
625 if (!isSlotInLane(slot)) {
628 std::tie(input, timeslice) = getInputTimeslice(
index.getVariablesForSlot(slot));
630 needsCleaning =
true;
641 mPruneOps.erase(std::remove_if(mPruneOps.begin(), mPruneOps.end(), [slot](
const auto&
x) { return x.slot == slot; }), mPruneOps.end());
643 size_t saved = saveInSlot(timeslice, input, slot, info);
647 index.publishSlot(slot);
648 index.markAsDirty(slot,
true);
656 std::tie(input, timeslice) = getInputTimeslice(pristineContext);
658 auto DataHeaderInfo = [&rawHeader]() {
661 const auto* dh = o2::header::get<o2::header::DataHeader*>(rawHeader);
663 error += fmt::format(
"{}/{}/{}", dh->dataOrigin, dh->dataDescription, dh->subSpecification);
665 error +=
"invalid header";
671 LOG(error) <<
"Could not match incoming data to any input route: " << DataHeaderInfo();
674 for (
size_t pi = 0; pi < nMessages; ++pi) {
675 messages[pi].reset(
nullptr);
681 LOG(error) <<
"Could not determine the timeslice for input: " << DataHeaderInfo();
684 for (
size_t pi = 0; pi < nMessages; ++pi) {
685 messages[pi].reset(
nullptr);
692 std::tie(action, slot) =
index.replaceLRUWith(pristineContext, timeslice);
693 uint64_t
const* debugTimestamp = std::get_if<uint64_t>(&pristineContext.
get(0));
696 "Slot %zu updated with %zu using action %d, %" PRIu64, slot.index, timeslice.value, (
int)action, *debugTimestamp);
699 updateStatistics(action);
705 static std::atomic<size_t> obsoleteCount = 0;
706 static std::atomic<size_t> mult = 1;
707 if ((obsoleteCount++ % (1 * mult)) == 0) {
708 LOGP(warning,
"Over {} incoming messages are already obsolete, not relaying.", obsoleteCount.load());
709 if (obsoleteCount > mult * 10) {
715 LOG(warning) <<
"Incoming data is invalid, not relaying.";
718 for (
size_t pi = 0; pi < nMessages; ++pi) {
719 messages[pi].reset(
nullptr);
727 mPruneOps.erase(std::remove_if(mPruneOps.begin(), mPruneOps.end(), [slot](
const auto&
x) { return x.slot == slot; }), mPruneOps.end());
728 size_t saved = saveInSlot(timeslice, input, slot, info);
732 index.publishSlot(slot);
733 index.markAsDirty(slot,
true);
741 LOGP(
debug,
"DataRelayer::getReadyToProcess");
742 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
745 const auto& cache = mCache;
746 const auto numInputTypes = mDistinctRoutesIndex.size();
752 auto getPartialRecord = [&cache, &numInputTypes](
int li) -> std::span<std::vector<fair::mq::MessagePtr>
const> {
753 auto offset = li * numInputTypes;
754 assert(cache.size() >=
offset + numInputTypes);
756 auto const end = cache.data() +
offset + numInputTypes;
765 LOGP(
debug,
"Doing action {} for slot {} (timeslice: {})", (
int)
op, li.
index, *timeslice);
768 LOGP(
debug,
"No timeslice associated with slot ", li.
index);
783 if (numInputTypes == 0) {
784 LOGP(
debug,
"numInputTypes == 0, returning.");
787 size_t cacheLines = cache.size() / numInputTypes;
788 assert(cacheLines * numInputTypes == cache.size());
789 int countConsume = 0;
790 int countConsumeExisting = 0;
791 int countProcess = 0;
792 int countDiscard = 0;
796 for (
int li = cacheLines - 1; li >= 0; --li) {
800 if (mTimesliceIndex.
isDirty(slot) ==
false) {
805 throw runtime_error_f(
"Completion police %s has no callback set", mCompletionPolicy.
name.c_str());
807 auto partial = getPartialRecord(li);
808 auto nPartsGetter = [&partial](
size_t idx) {
811 auto refCountGetter = [&partial](
size_t idx) ->
int {
812 auto& header =
static_cast<const fair::mq::shmem::Message&
>(*(partial[idx] |
get_header{0}));
813 return header.GetRefCount();
816 if (!partial[idx].
empty()) {
817 auto const& headerMsg = partial[idx][
indices.headerIdx];
818 auto const& payloadMsg = partial[idx][
indices.payloadIdx];
821 reinterpret_cast<const char*
>(headerMsg->GetData()),
822 payloadMsg ?
reinterpret_cast<char const*
>(payloadMsg->GetData()) :
nullptr,
823 payloadMsg ? payloadMsg->GetSize() : 0};
830 return next.headerIdx < partial[idx].size() ? next :
DataRefIndices{size_t(-1), size_t(-1)};
832 auto payloadGetter = [&partial](
size_t idx,
DataRefIndices current) -> fair::mq::Message* {
833 auto const& msgs = partial[idx];
834 if (msgs.size() <= current.payloadIdx || !msgs[current.payloadIdx]) {
837 return msgs[current.payloadIdx].get();
839 InputSpan span{nPartsGetter, refCountGetter, indicesGetter, nextIndicesGetter, payloadGetter,
static_cast<size_t>(partial.size())};
843 auto timeslice = std::get_if<uint64_t>(&variables.get(0));
847 updateCompletionResults(slot, timeslice, action);
854 updateCompletionResults(slot, timeslice, action);
858 countConsumeExisting++;
859 updateCompletionResults(slot, timeslice, action);
864 updateCompletionResults(slot, timeslice, action);
869 updateCompletionResults(slot, timeslice, action);
884 LOGP(
debug,
"DataRelayer::getReadyToProcess results notDirty:{}, consume:{}, consumeExisting:{}, process:{}, discard:{}, wait:{}",
885 notDirty, countConsume, countConsumeExisting, countProcess,
886 countDiscard, countWait);
891 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
892 const auto numInputTypes = mDistinctRoutesIndex.size();
894 auto markInputDone = [&cachedStateMetrics = mCachedStateMetrics,
896 auto cacheId = s.index * numInputTypes + arg;
897 if (cachedStateMetrics[cacheId] == oldStatus) {
898 cachedStateMetrics[cacheId] = newStatus;
902 for (
size_t ai = 0, ae = numInputTypes; ai != ae; ++ai) {
903 markInputDone(slot, ai, oldStatus, newStatus);
909 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
911 const auto numInputTypes = mDistinctRoutesIndex.size();
913 std::vector<std::vector<fair::mq::MessagePtr>> messages(numInputTypes);
914 auto& cache = mCache;
915 auto&
index = mTimesliceIndex;
926 auto moveHeaderPayloadToOutput = [&messages,
927 &cachedStateMetrics = mCachedStateMetrics,
929 auto cacheId = s.index * numInputTypes + arg;
933 if (!cache[cacheId].
empty()) {
934 messages[arg] = std::move(cache[cacheId]);
936 index.markAsInvalid(s);
944 for (
size_t ai = s.index * numInputTypes, ae = ai + numInputTypes; ai != ae; ++ai) {
945 assert(std::accumulate(cache[ai].begin(), cache[ai].
end(),
true, [](
bool result,
auto const&
element) {
return result &&
element.get() ==
nullptr; }));
948 index.markAsInvalid(s);
952 jumpToCacheEntryAssociatedWith(slot);
953 for (
size_t ai = 0, ae = numInputTypes; ai != ae; ++ai) {
954 moveHeaderPayloadToOutput(slot, ai);
956 invalidateCacheFor(slot);
963 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
965 const auto numInputTypes = mDistinctRoutesIndex.size();
967 std::vector<std::vector<fair::mq::MessagePtr>> messages(numInputTypes);
968 auto& cache = mCache;
969 auto&
index = mTimesliceIndex;
980 auto copyHeaderPayloadToOutput = [&messages,
981 &cachedStateMetrics = mCachedStateMetrics,
983 auto cacheId = s.index * numInputTypes + arg;
987 for (
size_t pi = 0; pi < (cache[cacheId] |
count_parts{}); pi++) {
988 auto& header = cache[cacheId] |
get_header{pi};
989 auto&& newHeader = header->GetTransport()->CreateMessage();
990 newHeader->Copy(*header);
991 messages[arg].emplace_back(std::move(newHeader));
992 messages[arg].emplace_back(std::move(cache[cacheId] |
get_payload{pi, 0}));
997 jumpToCacheEntryAssociatedWith(slot);
998 for (
size_t ai = 0, ae = numInputTypes; ai != ae; ++ai) {
999 copyHeaderPayloadToOutput(slot, ai);
1002 return std::move(messages);
1007 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
1009 for (
auto& cache : mCache) {
1012 for (
size_t s = 0; s < mTimesliceIndex.
size(); ++s) {
1020 return mCache.size() / mDistinctRoutesIndex.size();
1029 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
1031 mTimesliceIndex.
resize(s);
1032 mVariableContextes.resize(s);
1038 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
1040 auto numInputTypes = mDistinctRoutesIndex.size();
1044 mCache.resize(numInputTypes * mTimesliceIndex.
size());
1047 mCachedStateMetrics.resize(mCache.size());
1052 for (
size_t i = 0;
i < mVariableContextes.size(); ++
i) {
1054 .
name = fmt::format(
"matcher_variables/{}",
i),
1056 .minPublishInterval = 500,
1057 .sendInitialValue =
true,
1062 for (
int ci = 0; ci < mTimesliceIndex.
size(); ci++) {
1064 .
name = fmt::format(
"data_relayer/{}", ci),
1066 .minPublishInterval = 800,
1067 .sendInitialValue =
true,
1075 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
1081 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
1087 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
1093 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
1102 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
1104 for (
size_t ci = 0; ci < mTimesliceIndex.
size(); ++ci) {
1109 char relayerSlotState[1024];
1112 int written = snprintf(relayerSlotState, 1024,
"%d ", (
int)mTimesliceIndex.
size());
1113 char*
buffer = relayerSlotState + written;
1114 for (
size_t ci = 0; ci < mTimesliceIndex.
size(); ++ci) {
1115 for (
size_t si = 0; si < mDistinctRoutesIndex.size(); ++si) {
1116 int index = ci * mDistinctRoutesIndex.size() + si;
1117 int value =
static_cast<int>(mCachedStateMetrics[
index]);
1125 buffer[mDistinctRoutesIndex.size()] =
'\0';
1126 auto size = (
int)(
buffer - relayerSlotState + mDistinctRoutesIndex.size());
std::vector< framework::ConcreteDataMatcher > matchers
std::vector< OutputRoute > routes
#define O2_BUILTIN_UNREACHABLE
o2::monitoring::Verbosity Verbosity
#define O2_DECLARE_DYNAMIC_LOG(name)
#define O2_SIGNPOST_ID_FROM_POINTER(name, log, pointer)
#define O2_SIGNPOST_EVENT_EMIT_INFO(log, id, name, format,...)
#define O2_SIGNPOST_ID_GENERATE(name, log)
#define O2_SIGNPOST_EVENT_EMIT(log, id, name, format,...)
uint32_t getFirstTFOrbitForSlot(TimesliceSlot slot)
Get the firstTForbit associate to a given slot.
void updateCacheStatus(TimesliceSlot slot, CacheEntryStatus oldStatus, CacheEntryStatus newStatus)
uint32_t getRunNumberForSlot(TimesliceSlot slot)
Get the runNumber associated to a given slot.
void prunePending(OnDropCallback)
Prune all the pending entries in the cache.
std::function< void(TimesliceSlot, std::vector< std::span< fair::mq::MessagePtr > > &, TimesliceIndex::OldestOutputInfo info)> OnDropCallback
void getReadyToProcess(std::vector< RecordAction > &completed)
void setPipelineLength(size_t s)
Tune the maximum number of in flight timeslices this can handle.
std::vector< std::vector< fair::mq::MessagePtr > > consumeAllInputsForTimeslice(TimesliceSlot id)
size_t getParallelTimeslices() const
Returns how many timeslices we can handle in parallel.
RelayChoice relay(void const *rawHeader, std::unique_ptr< fair::mq::Message > *messages, InputInfo const &info, size_t nMessages, size_t nPayloads=1, OnInsertionCallback onInsertion=nullptr, OnDropCallback onDrop=nullptr)
void pruneCache(TimesliceSlot slot, OnDropCallback onDrop=nullptr)
Prune the cache for a given slot.
DataRelayer(CompletionPolicy const &, std::vector< InputRoute > const &routes, TimesliceIndex &, ServiceRegistryRef, int)
std::function< void(ServiceRegistryRef &, std::span< fair::mq::MessagePtr > &)> OnInsertionCallback
std::vector< std::vector< fair::mq::MessagePtr > > consumeExistingInputsForTimeslice(TimesliceSlot id)
void setOldestPossibleInput(TimesliceId timeslice, ChannelIndex channel)
uint64_t getCreationTimeForSlot(TimesliceSlot slot)
Get the creation time associated to a given slot.
void sendContextState()
Send metrics with the VariableContext information.
TimesliceId getTimesliceForSlot(TimesliceSlot slot)
ActivityStats processDanglingInputs(std::vector< ExpirationHandler > const &, ServiceRegistryRef context, bool createNew)
TimesliceIndex::OldestOutputInfo getOldestPossibleOutput() const
uint32_t getFirstTFCounterForSlot(TimesliceSlot slot)
Get the firstTFCounter associate to a given slot.
void clear()
Remove all pending messages.
virtual fair::mq::Device * device()=0
void markAsDirty(TimesliceSlot slot, bool value)
data_matcher::VariableContext & getPublishedVariablesForSlot(TimesliceSlot slot)
OldestInputInfo setOldestPossibleInput(TimesliceId timeslice, ChannelIndex channel)
OldestOutputInfo getOldestPossibleOutput() const
bool isDirty(TimesliceSlot const &slot) const
InputChannelInfo const & getChannelInfo(ChannelIndex channel) const
ActionTaken
The outcome for the processing of a given timeslot.
@ Wait
An obsolete slot is used to hold the new context and the old one is dropped.
@ DropObsolete
An invalid context is not inserted in the index and dropped.
@ DropInvalid
We wait for the oldest slot to complete.
@ ReplaceObsolete
An unused / invalid slot is used to hold the new context.
void rescan()
Mark all the cachelines as invalid, e.g. due to an out of band event.
bool validateSlot(TimesliceSlot slot, TimesliceId currentOldest)
bool isValid(TimesliceSlot const &slot) const
void markAsInvalid(TimesliceSlot slot)
OldestOutputInfo updateOldestPossibleOutput(bool rewinded)
data_matcher::VariableContext & getVariablesForSlot(TimesliceSlot slot)
void publish(void(*callback)(VariableContext const &, TimesliceSlot slot, void *context), void *context, TimesliceSlot slot)
ContextElement::Value const & get(size_t pos) const
GLsizei const GLfloat * value
GLsizei GLenum const void * indices
constexpr int MAX_MATCHING_VARIABLE
Defining ITS Vertex explicitly as messageable.
size_t matchToContext(void const *data, std::vector< DataDescriptorMatcher > const &matchers, std::vector< size_t > const &index, VariableContext &context)
constexpr int INVALID_INPUT
bool isCalibrationData(std::unique_ptr< fair::mq::Message > &first)
void sendVariableContextMetrics(VariableContext &context, TimesliceSlot slot, DataProcessingStates &states)
@ NoTransition
No pending transitions.
@ DROPPED_INCOMING_MESSAGES
RuntimeErrorRef runtime_error_f(const char *,...)
std::string to_string(gsl::span< T, Size > span)
static constexpr int INVALID
std::string name
Name of the policy itself.
CompletionOp
Action to take with the InputRecord:
@ Retry
Like Wait but mark the cacheline as dirty.
CallbackConfigureRelayer configureRelayer
CallbackFull callbackFull
Actual policy which decides what to do with a partial InputRecord, extended version.
Helper struct to hold statistics about the data processing happening.
@ Add
Update the rate of the metric given the amount since the last time.
Type type
What was the outcome of the relay operation.
@ Invalid
Ownership of the data has been taken.
@ Backpressured
The incoming data was not valid and has been dropped.
@ Dropped
The incoming data was not relayed, because we are backpressured.
static std::string describe(InputSpec const &spec)
static unsigned int pipelineLength(unsigned int minLength)
get max number of timeslices in the queue
static bool onlineDeploymentMode()
@true if running online
Running state information of a given device.
ProcessingType allowedProcessing
Reference to an inflight part.
std::unique_ptr< fair::mq::Message > header
std::unique_ptr< fair::mq::Message > payload
static bool isValid(TimesliceId const ×lice)
static constexpr uint64_t INVALID
static bool isValid(TimesliceSlot const &slot)
static constexpr uint64_t INVALID
static uint32_t getRunNumber(data_matcher::VariableContext const &variables)
static uint32_t getFirstTFCounter(data_matcher::VariableContext const &variables)
static uint64_t getCreationTime(data_matcher::VariableContext const &variables)
static uint32_t getFirstTFOrbit(data_matcher::VariableContext const &variables)
static TimesliceId getTimeslice(data_matcher::VariableContext const &variables)
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"