@@ -1105,6 +1105,91 @@ TEST_CASE("DataRelayer")
11051105 checkContents ();
11061106 }
11071107
1108+ // The analysis CCDB fetcher sends a CCDB table followed by the objects it
1109+ // refers to, all for the same route. The consumer must get them in the same
1110+ // slot, with the table as the first part, which holds when they are relayed
1111+ // from one multipart, i.e. without checking for completion in between.
1112+ SECTION (" CCDBTableAndObjectsCompleteTogether" )
1113+ {
1114+ InputSpec spec0{" bcs" , " AOD" , " BC" };
1115+ InputSpec spec1{" ccdb" , " AOD" , " GLOCCDBOBJ" , 1 };
1116+
1117+ std::vector<InputRoute> inputs = {
1118+ InputRoute{spec0, 0 , " Fake0" , 0 },
1119+ InputRoute{spec1, 1 , " Fake1" , 0 },
1120+ };
1121+
1122+ std::vector<InputChannelInfo> infos{1 };
1123+ TimesliceIndex index{1 , infos};
1124+ ref.registerService (ServiceRegistryHelpers::handleForService<TimesliceIndex>(&index));
1125+
1126+ auto policy = CompletionPolicyHelpers::consumeWhenAll ();
1127+ DataRelayer relayer (policy, inputs, index, {registry}, -1 );
1128+ relayer.setPipelineLength (4 );
1129+
1130+ auto transport = fair::mq::TransportFactory::CreateTransportFactory (" zeromq" );
1131+ auto channelAlloc = o2::pmr::getTransportAllocator (transport.get ());
1132+
1133+ auto relayOne = [&](o2::header::DataDescription description, uint32_t subSpec, o2::header::SerializationMethod method, size_t timeslice) {
1134+ DataHeader dh;
1135+ dh.dataOrigin = " AOD" ;
1136+ dh.dataDescription = description;
1137+ dh.subSpecification = subSpec;
1138+ dh.splitPayloadIndex = 0 ;
1139+ dh.splitPayloadParts = 1 ;
1140+ dh.payloadSerializationMethod = method;
1141+ std::array<fair::mq::MessagePtr, 2 > msgs;
1142+ msgs[0 ] = o2::pmr::getMessage (Stack{channelAlloc, dh, DataProcessingHeader{timeslice, 1 }});
1143+ msgs[1 ] = transport->CreateMessage (8 );
1144+ DataRelayer::InputInfo info{0 , 2 , DataRelayer::InputType::Data, {ChannelIndex::INVALID }};
1145+ relayer.relay (msgs[0 ]->GetData (), msgs.data (), info, 2 );
1146+ };
1147+ auto relayCCDB = [&](size_t timeslice) {
1148+ relayOne (" GLOCCDBOBJ" , 1 , o2::header::gSerializationMethodArrow , timeslice);
1149+ relayOne (" GLOCCDBOBJ" , 1 , o2::header::gSerializationMethodCCDB , timeslice);
1150+ relayOne (" GLOCCDBOBJ" , 1 , o2::header::gSerializationMethodCCDB , timeslice);
1151+ };
1152+
1153+ // Table and objects in one multipart: one slot, with the table first.
1154+ relayOne (" BC" , 0 , o2::header::gSerializationMethodArrow , 0 );
1155+ relayCCDB (0 );
1156+ std::vector<RecordAction> ready;
1157+ relayer.getReadyToProcess (ready);
1158+ REQUIRE (ready.size () == 1 );
1159+ auto result = relayer.consumeAllInputsForTimeslice (ready[0 ].slot );
1160+ REQUIRE ((result[1 ] | count_parts{}) == 3 );
1161+ for (size_t p = 0 ; p < 3 ; ++p) {
1162+ auto * dh = o2::header::get<DataHeader*>((result[1 ] | get_header{p})->GetData ());
1163+ REQUIRE (dh->payloadSerializationMethod == (p == 0 ? o2::header::gSerializationMethodArrow : o2::header::gSerializationMethodCCDB ));
1164+ }
1165+
1166+ // The same, with the CCDB parts arriving before the other input.
1167+ relayCCDB (1 );
1168+ ready.clear ();
1169+ relayer.getReadyToProcess (ready);
1170+ REQUIRE (ready.empty ());
1171+ relayOne (" BC" , 0 , o2::header::gSerializationMethodArrow , 1 );
1172+ relayer.getReadyToProcess (ready);
1173+ REQUIRE (ready.size () == 1 );
1174+ result = relayer.consumeAllInputsForTimeslice (ready[0 ].slot );
1175+ REQUIRE ((result[1 ] | count_parts{}) == 3 );
1176+
1177+ // What happened when the table and the objects were sent separately: the
1178+ // slot completes on the table alone, and the objects open a new slot which
1179+ // can never complete.
1180+ relayOne (" BC" , 0 , o2::header::gSerializationMethodArrow , 2 );
1181+ relayOne (" GLOCCDBOBJ" , 1 , o2::header::gSerializationMethodArrow , 2 );
1182+ ready.clear ();
1183+ relayer.getReadyToProcess (ready);
1184+ REQUIRE (ready.size () == 1 );
1185+ result = relayer.consumeAllInputsForTimeslice (ready[0 ].slot );
1186+ REQUIRE ((result[1 ] | count_parts{}) == 1 );
1187+ relayOne (" GLOCCDBOBJ" , 1 , o2::header::gSerializationMethodCCDB , 2 );
1188+ ready.clear ();
1189+ relayer.getReadyToProcess (ready);
1190+ REQUIRE (ready.empty ());
1191+ }
1192+
11081193 // An expiring input is materialised straight into the slot, so with one
11091194 // shared buffer per slot it lands *after* whatever the other inputs already
11101195 // hold -- the cells are then no longer in input order. Check that the data
0 commit comments