From f2e178bc29af4a1936136cb6bd2ebf4ac97cfb52 Mon Sep 17 00:00:00 2001 From: Barthelemy Date: Tue, 27 Oct 2026 11:03:05 +0100 Subject: [PATCH] [QC-1260] Consider handling better invalid JSON from `ccdbApi->list` The json error was already handled in the sense that it was not crashing and it was logging a human readable error. I added a test and added a bit more output in case we don't find the objects we are looking for in the listing. --- Framework/CMakeLists.txt | 2 + Framework/src/CcdbDatabase.cxx | 15 +- .../test/testCcdbDatabaseListingErrors.cxx | 142 ++++++++++++++++++ 3 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 Framework/test/testCcdbDatabaseListingErrors.cxx diff --git a/Framework/CMakeLists.txt b/Framework/CMakeLists.txt index 27513f0ef3..b52a705cac 100644 --- a/Framework/CMakeLists.txt +++ b/Framework/CMakeLists.txt @@ -329,6 +329,7 @@ set(TEST_SRCS test/testTaskRunner.cxx test/testObjectsManager.cxx test/testCcdbDatabase.cxx + test/testCcdbDatabaseListingErrors.cxx test/testCcdbDatabaseExtra.cxx test/testTriggers.cxx test/testPostProcessingInterface.cxx @@ -355,6 +356,7 @@ set(TEST_ARGS "" "" "" + "" "-b --run" "-b --run" "" diff --git a/Framework/src/CcdbDatabase.cxx b/Framework/src/CcdbDatabase.cxx index 3fd6a0fd5c..920353fad1 100644 --- a/Framework/src/CcdbDatabase.cxx +++ b/Framework/src/CcdbDatabase.cxx @@ -524,7 +524,12 @@ core::ValidityInterval CcdbDatabase::getLatestObjectValidity(const std::string& std::vector CcdbDatabase::getTimestampsForObject(const std::string& path) { - const auto& objects = getListingAsPtree(path).get_child("objects"); + const auto listing = getListingAsPtree(path); + if (listing.count("objects") == 0) { + ILOG(Warning, Support) << "Could not get a valid listing from db '" << mUrl << "' for timestamps of '" << path << "'" << ENDM; + return {}; + } + const auto& objects = listing.get_child("objects"); std::vector timestamps; timestamps.reserve(objects.size()); @@ -551,7 +556,13 @@ std::vector CcdbDatabase::getPublishedObjectNames(std::string taskN try { boost::property_tree::read_json(ss, pt); } catch (const boost::property_tree::json_parser::json_parser_error&) { - ILOG(Error, Support) << "Failed to parse json in CcdbDatabase::getTimestampsForObject from data: " << ss.str() << ENDM; + ILOG(Error, Support) << "Failed to parse json in CcdbDatabase::getPublishedObjectNames from data: " << listing << ENDM; + return result; + } + + if (pt.count("objects") == 0) { + ILOG(Warning, Support) << "Could not get a valid listing from db '" << mUrl << "' for published objects of '" << taskName << "'" << ENDM; + return result; } BOOST_FOREACH (boost::property_tree::ptree::value_type& v, pt.get_child("objects")) { diff --git a/Framework/test/testCcdbDatabaseListingErrors.cxx b/Framework/test/testCcdbDatabaseListingErrors.cxx new file mode 100644 index 0000000000..e3b23f752e --- /dev/null +++ b/Framework/test/testCcdbDatabaseListingErrors.cxx @@ -0,0 +1,142 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file testCcdbDatabaseListingErrors.cxx +/// \author Barthelemy von Haller +/// +/// Reproduces issues with CCDB + +#include "QualityControl/CcdbDatabase.h" + +#define BOOST_TEST_MODULE CcdbDatabase listing errors test +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace o2::quality_control::repository; + +namespace +{ + +constexpr const char* kServiceUnavailableHtml = + "HTTP Status 503 – Service Unavailable" + "

HTTP Status 503 – Service Unavailable

" + "

Message Too many concurrent requests to /browse/ or /latest/ , cannot serve more

" + "

Apache Tomcat/9.0.97

"; + +// Junie generated +class Html503Server +{ + public: + Html503Server() + { + mListenFd = ::socket(AF_INET, SOCK_STREAM, 0); + + int yes = 1; + ::setsockopt(mListenFd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); + + sockaddr_in addr{}; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.sin_port = htons(0); + + socklen_t len = sizeof(addr); + mPort = ntohs(addr.sin_port); + + mRunning = true; + mThread = std::thread([this] { run(); }); + } + + ~Html503Server() + { + mRunning = false; + if (mListenFd >= 0) { + ::shutdown(mListenFd, SHUT_RDWR); + ::close(mListenFd); + mListenFd = -1; + } + if (mThread.joinable()) { + mThread.join(); + } + } + + std::string url() const { return "http://127.0.0.1:" + std::to_string(mPort); } + + private: + void run() + { + while (mRunning) { + sockaddr_in client{}; + socklen_t clientLen = sizeof(client); + int clientFd = ::accept(mListenFd, reinterpret_cast(&client), &clientLen); + if (clientFd < 0) { + continue; + } + char buf[1024]; + ::recv(clientFd, buf, sizeof(buf), 0); + + const std::string body = kServiceUnavailableHtml; + std::string response = + "HTTP/1.1 503 Service Unavailable\r\n" + "Content-Type: text/html;charset=utf-8\r\n" + "Content-Length: " + + std::to_string(body.size()) + + "\r\n" + "Connection: close\r\n" + "\r\n" + + body; + ::send(clientFd, response.data(), response.size(), 0); + ::close(clientFd); + } + } + + int mListenFd = -1; + int mPort = 0; + std::atomic mRunning{ false }; + std::thread mThread; +}; + +} // namespace + +BOOST_AUTO_TEST_CASE(getPublishedObjectNames_handles_html_503) +{ + Html503Server server; + CcdbDatabase db; + db.connect(server.url(), "", "", ""); + + std::vector names; + BOOST_CHECK_NO_THROW(names = db.getPublishedObjectNames("qc/TST/MO/SomeTask")); + BOOST_TEST(names.empty()); +} + +BOOST_AUTO_TEST_CASE(getListingAsPtree_handles_html_503) +{ + Html503Server server; + CcdbDatabase db; + db.connect(server.url(), "", "", ""); + + boost::property_tree::ptree listing; + BOOST_CHECK_NO_THROW(listing = db.getListingAsPtree("qc/TST/MO/SomeTask")); + BOOST_TEST(listing.count("objects") == 0); +}