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 onDrop(slot, dropped, oldestPossibleTimeslice);
440 assert(cache.empty() ==
false);
441 assert(
index.size() * numInputTypes == cache.size());
445 assert(numInputTypes * slot.
index < cache.size());
446 for (
size_t ai = slot.
index * numInputTypes, ae = ai + numInputTypes; ai != ae; ++ai) {
457 auto* dph = o2::header::get<DataProcessingHeader*>(
first->GetData());
463 std::unique_ptr<fair::mq::Message>* messages,
470 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
476 return (slot.index % maxLanes) == (currentLane % maxLanes);
481 auto getInputTimeslice = [&
matchers = mInputMatchers,
482 &distinctRoutes = mDistinctRoutesIndex,
485 -> std::tuple<int, TimesliceId> {
498 if (
auto pval = std::get_if<uint64_t>(&context.get(0))) {
500 return {input, timeslice};
510 auto saveInSlot = [&cachedStateMetrics = mCachedStateMetrics,
516 &services = mContext,
519 O2_SIGNPOST_EVENT_EMIT(data_relayer, aid,
"saveInSlot",
"saving %{public}s@%zu in slot %zu from %{public}s",
520 fmt::format(
"{:x}", *o2::header::get<DataHeader*>(messages[0]->GetData())).c_str(),
521 timeslice.value, slot.index,
523 auto cacheIdx = numInputTypes * slot.index + input;
524 auto&
target = cache[cacheIdx];
528 assert(nPayloads > 0);
532 auto allMessages = std::span<fair::mq::MessagePtr>(messages, messages + nMessages);
534 onInsertion(services, allMessages);
536 for (
size_t mi = 0; mi < nMessages; ++mi) {
537 assert(mi + nPayloads < nMessages);
543 "Dropping incoming %zu messages because they are data processing.", nPayloads);
545 for (
size_t i = mi;
i < mi + nPayloads + 1;
i++) {
546 auto discard = std::move(messages[
i]);
551 auto span = std::span<fair::mq::MessagePtr>(messages + mi, messages + mi + nPayloads + 1);
554 for (
size_t i = 0;
i < nPayloads + 1; ++
i) {
555 target.emplace_back(std::move(span[
i]));
594 auto&
index = mTimesliceIndex;
596 bool needsCleaning =
false;
599 for (
size_t ci = 0; ci <
index.size(); ++ci) {
601 if (!isSlotInLane(slot)) {
604 if (
index.isValid(slot) ==
false) {
607 std::tie(input, timeslice) = getInputTimeslice(
index.getVariablesForSlot(slot));
616 for (
size_t ci = 0; ci <
index.size(); ++ci) {
618 if (
index.isValid(slot) ==
true) {
621 if (!isSlotInLane(slot)) {
624 std::tie(input, timeslice) = getInputTimeslice(
index.getVariablesForSlot(slot));
626 needsCleaning =
true;
637 mPruneOps.erase(std::remove_if(mPruneOps.begin(), mPruneOps.end(), [slot](
const auto&
x) { return x.slot == slot; }), mPruneOps.end());
639 size_t saved = saveInSlot(timeslice, input, slot, info);
643 index.publishSlot(slot);
644 index.markAsDirty(slot,
true);
652 std::tie(input, timeslice) = getInputTimeslice(pristineContext);
654 auto DataHeaderInfo = [&rawHeader]() {
657 const auto* dh = o2::header::get<o2::header::DataHeader*>(rawHeader);
659 error += fmt::format(
"{}/{}/{}", dh->dataOrigin, dh->dataDescription, dh->subSpecification);
661 error +=
"invalid header";
667 LOG(error) <<
"Could not match incoming data to any input route: " << DataHeaderInfo();
670 for (
size_t pi = 0; pi < nMessages; ++pi) {
671 messages[pi].reset(
nullptr);
677 LOG(error) <<
"Could not determine the timeslice for input: " << DataHeaderInfo();
680 for (
size_t pi = 0; pi < nMessages; ++pi) {
681 messages[pi].reset(
nullptr);
688 std::tie(action, slot) =
index.replaceLRUWith(pristineContext, timeslice);
689 uint64_t
const* debugTimestamp = std::get_if<uint64_t>(&pristineContext.
get(0));
692 "Slot %zu updated with %zu using action %d, %" PRIu64, slot.index, timeslice.value, (
int)action, *debugTimestamp);
695 updateStatistics(action);
701 static std::atomic<size_t> obsoleteCount = 0;
702 static std::atomic<size_t> mult = 1;
703 if ((obsoleteCount++ % (1 * mult)) == 0) {
704 LOGP(warning,
"Over {} incoming messages are already obsolete, not relaying.", obsoleteCount.load());
705 if (obsoleteCount > mult * 10) {
711 LOG(warning) <<
"Incoming data is invalid, not relaying.";
714 for (
size_t pi = 0; pi < nMessages; ++pi) {
715 messages[pi].reset(
nullptr);
723 mPruneOps.erase(std::remove_if(mPruneOps.begin(), mPruneOps.end(), [slot](
const auto&
x) { return x.slot == slot; }), mPruneOps.end());
724 size_t saved = saveInSlot(timeslice, input, slot, info);
728 index.publishSlot(slot);
729 index.markAsDirty(slot,
true);
737 LOGP(
debug,
"DataRelayer::getReadyToProcess");
738 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
741 const auto& cache = mCache;
742 const auto numInputTypes = mDistinctRoutesIndex.size();
748 auto getPartialRecord = [&cache, &numInputTypes](
int li) -> std::span<std::vector<fair::mq::MessagePtr>
const> {
749 auto offset = li * numInputTypes;
750 assert(cache.size() >=
offset + numInputTypes);
752 auto const end = cache.data() +
offset + numInputTypes;
761 LOGP(
debug,
"Doing action {} for slot {} (timeslice: {})", (
int)
op, li.
index, *timeslice);
764 LOGP(
debug,
"No timeslice associated with slot ", li.
index);
779 if (numInputTypes == 0) {
780 LOGP(
debug,
"numInputTypes == 0, returning.");
783 size_t cacheLines = cache.size() / numInputTypes;
784 assert(cacheLines * numInputTypes == cache.size());
785 int countConsume = 0;
786 int countConsumeExisting = 0;
787 int countProcess = 0;
788 int countDiscard = 0;
792 for (
int li = cacheLines - 1; li >= 0; --li) {
796 if (mTimesliceIndex.
isDirty(slot) ==
false) {
801 throw runtime_error_f(
"Completion police %s has no callback set", mCompletionPolicy.
name.c_str());
803 auto partial = getPartialRecord(li);
804 auto nPartsGetter = [&partial](
size_t idx) {
807 auto refCountGetter = [&partial](
size_t idx) ->
int {
808 auto& header =
static_cast<const fair::mq::shmem::Message&
>(*(partial[idx] |
get_header{0}));
809 return header.GetRefCount();
812 if (!partial[idx].
empty()) {
813 auto const& headerMsg = partial[idx][
indices.headerIdx];
814 auto const& payloadMsg = partial[idx][
indices.payloadIdx];
817 reinterpret_cast<const char*
>(headerMsg->GetData()),
818 payloadMsg ?
reinterpret_cast<char const*
>(payloadMsg->GetData()) :
nullptr,
819 payloadMsg ? payloadMsg->GetSize() : 0};
826 return next.headerIdx < partial[idx].size() ? next :
DataRefIndices{size_t(-1), size_t(-1)};
828 auto payloadGetter = [&partial](
size_t idx,
DataRefIndices current) -> fair::mq::Message* {
829 auto const& msgs = partial[idx];
830 if (msgs.size() <= current.payloadIdx || !msgs[current.payloadIdx]) {
833 return msgs[current.payloadIdx].get();
835 InputSpan span{nPartsGetter, refCountGetter, indicesGetter, nextIndicesGetter, payloadGetter,
static_cast<size_t>(partial.size())};
839 auto timeslice = std::get_if<uint64_t>(&variables.get(0));
843 updateCompletionResults(slot, timeslice, action);
850 updateCompletionResults(slot, timeslice, action);
854 countConsumeExisting++;
855 updateCompletionResults(slot, timeslice, action);
860 updateCompletionResults(slot, timeslice, action);
865 updateCompletionResults(slot, timeslice, action);
880 LOGP(
debug,
"DataRelayer::getReadyToProcess results notDirty:{}, consume:{}, consumeExisting:{}, process:{}, discard:{}, wait:{}",
881 notDirty, countConsume, countConsumeExisting, countProcess,
882 countDiscard, countWait);
887 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
888 const auto numInputTypes = mDistinctRoutesIndex.size();
890 auto markInputDone = [&cachedStateMetrics = mCachedStateMetrics,
892 auto cacheId = s.index * numInputTypes + arg;
893 if (cachedStateMetrics[cacheId] == oldStatus) {
894 cachedStateMetrics[cacheId] = newStatus;
898 for (
size_t ai = 0, ae = numInputTypes; ai != ae; ++ai) {
899 markInputDone(slot, ai, oldStatus, newStatus);
905 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
907 const auto numInputTypes = mDistinctRoutesIndex.size();
909 std::vector<std::vector<fair::mq::MessagePtr>> messages(numInputTypes);
910 auto& cache = mCache;
911 auto&
index = mTimesliceIndex;
922 auto moveHeaderPayloadToOutput = [&messages,
923 &cachedStateMetrics = mCachedStateMetrics,
925 auto cacheId = s.index * numInputTypes + arg;
929 if (!cache[cacheId].
empty()) {
930 messages[arg] = std::move(cache[cacheId]);
932 index.markAsInvalid(s);
940 for (
size_t ai = s.index * numInputTypes, ae = ai + numInputTypes; ai != ae; ++ai) {
941 assert(std::accumulate(cache[ai].begin(), cache[ai].
end(),
true, [](
bool result,
auto const&
element) {
return result &&
element.get() ==
nullptr; }));
944 index.markAsInvalid(s);
948 jumpToCacheEntryAssociatedWith(slot);
949 for (
size_t ai = 0, ae = numInputTypes; ai != ae; ++ai) {
950 moveHeaderPayloadToOutput(slot, ai);
952 invalidateCacheFor(slot);
959 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
961 const auto numInputTypes = mDistinctRoutesIndex.size();
963 std::vector<std::vector<fair::mq::MessagePtr>> messages(numInputTypes);
964 auto& cache = mCache;
965 auto&
index = mTimesliceIndex;
976 auto copyHeaderPayloadToOutput = [&messages,
977 &cachedStateMetrics = mCachedStateMetrics,
979 auto cacheId = s.index * numInputTypes + arg;
983 for (
size_t pi = 0; pi < (cache[cacheId] |
count_parts{}); pi++) {
984 auto& header = cache[cacheId] |
get_header{pi};
985 auto&& newHeader = header->GetTransport()->CreateMessage();
986 newHeader->Copy(*header);
987 messages[arg].emplace_back(std::move(newHeader));
988 messages[arg].emplace_back(std::move(cache[cacheId] |
get_payload{pi, 0}));
993 jumpToCacheEntryAssociatedWith(slot);
994 for (
size_t ai = 0, ae = numInputTypes; ai != ae; ++ai) {
995 copyHeaderPayloadToOutput(slot, ai);
998 return std::move(messages);
1003 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
1005 for (
auto& cache : mCache) {
1008 for (
size_t s = 0; s < mTimesliceIndex.
size(); ++s) {
1016 return mCache.size() / mDistinctRoutesIndex.size();
1025 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
1027 mTimesliceIndex.
resize(s);
1028 mVariableContextes.resize(s);
1034 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
1036 auto numInputTypes = mDistinctRoutesIndex.size();
1040 mCache.resize(numInputTypes * mTimesliceIndex.
size());
1043 mCachedStateMetrics.resize(mCache.size());
1048 for (
size_t i = 0;
i < mVariableContextes.size(); ++
i) {
1050 .
name = fmt::format(
"matcher_variables/{}",
i),
1052 .minPublishInterval = 500,
1053 .sendInitialValue =
true,
1058 for (
int ci = 0; ci < mTimesliceIndex.
size(); ci++) {
1060 .
name = fmt::format(
"data_relayer/{}", ci),
1062 .minPublishInterval = 800,
1063 .sendInitialValue =
true,
1071 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
1077 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
1083 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
1089 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
1098 std::scoped_lock<
O2_LOCKABLE(std::recursive_mutex)> lock(mMutex);
1100 for (
size_t ci = 0; ci < mTimesliceIndex.
size(); ++ci) {
1105 char relayerSlotState[1024];
1108 int written = snprintf(relayerSlotState, 1024,
"%d ", (
int)mTimesliceIndex.
size());
1109 char*
buffer = relayerSlotState + written;
1110 for (
size_t ci = 0; ci < mTimesliceIndex.
size(); ++ci) {
1111 for (
size_t si = 0; si < mDistinctRoutesIndex.size(); ++si) {
1112 int index = ci * mDistinctRoutesIndex.size() + si;
1113 int value =
static_cast<int>(mCachedStateMetrics[
index]);
1121 buffer[mDistinctRoutesIndex.size()] =
'\0';
1122 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.
void getReadyToProcess(std::vector< RecordAction > &completed)
std::function< void(TimesliceSlot, std::vector< std::vector< fair::mq::MessagePtr > > &, TimesliceIndex::OldestOutputInfo info)> OnDropCallback
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"