diff options
| author | Dan Goodliffe <dan.goodliffe@octal.co.uk> | 2026-03-19 12:33:48 +0000 |
|---|---|---|
| committer | Dan Goodliffe <dan.goodliffe@octal.co.uk> | 2026-03-19 12:33:48 +0000 |
| commit | 8c6fecd356003309f8eebec30374344272ca6072 (patch) | |
| tree | 023f79e07a3abd0829c91bfffd5d2a5657d8a225 | |
| parent | ee01cb7017e3895419544a0385f90f8eb0498680 (diff) | |
| download | webstat-8c6fecd356003309f8eebec30374344272ca6072.tar.bz2 webstat-8c6fecd356003309f8eebec30374344272ca6072.tar.xz webstat-8c6fecd356003309f8eebec30374344272ca6072.zip | |
Gracefully handle SIGTERM
Apache sends SIGTERM to the logger process to it shutdown. Honestly I
thought it would just close stdin and I should have checked.
| -rw-r--r-- | src/ingestor.cpp | 31 | ||||
| -rw-r--r-- | src/ingestor.hpp | 8 | ||||
| -rw-r--r-- | test/test-ingest.cpp | 13 |
3 files changed, 50 insertions, 2 deletions
diff --git a/src/ingestor.cpp b/src/ingestor.cpp index def3ce1..e2018ad 100644 --- a/src/ingestor.cpp +++ b/src/ingestor.cpp @@ -3,6 +3,7 @@ #include "uaLookup.hpp" #include "util.hpp" #include <connection.h> +#include <csignal> #include <dbTypes.h> #include <fstream> #include <modifycommand.h> @@ -64,8 +65,11 @@ namespace WebStat { return std::make_tuple(std::get<N>(ENTITY_TYPE_MAP)(std::get<N>(values))...); }(std::make_index_sequence<VALUE_COUNT>()); } + } + Ingestor * Ingestor::currentIngestor = nullptr; + Ingestor::Ingestor(const utsname & host, IngestorSettings settings) : Ingestor {host, std::make_shared<DB::ConnectionPool>( @@ -84,6 +88,31 @@ namespace WebStat { bindMany(ins, 0, hostnameId, host.nodename, host.sysname, host.release, host.version, host.machine, host.domainname); ins->execute(); + + assert(!currentIngestor); + currentIngestor = this; + signal(SIGTERM, &sigtermHandler); + } + + Ingestor::~Ingestor() + { + assert(currentIngestor); + signal(SIGTERM, SIG_DFL); + currentIngestor = nullptr; + } + + void + Ingestor::sigtermHandler(int sigNo) + { + assert(currentIngestor); + currentIngestor->terminate(sigNo); + } + + void + Ingestor::terminate(int) + { + terminated = true; + curl_multi_wakeup(curl.get()); } Ingestor::ScanResult @@ -138,7 +167,7 @@ namespace WebStat { const auto curlTimeOut = static_cast<int>( std::chrono::duration_cast<std::chrono::milliseconds>(settings.checkJobsAfter).count()); - while (curl_multi_poll(curl.get(), &logIn, 1, curlTimeOut, nullptr) == CURLM_OK) { + while (!terminated && curl_multi_poll(curl.get(), &logIn, 1, curlTimeOut, nullptr) == CURLM_OK) { if (logIn.revents) { if (auto line = scn::scan<std::string>(input, "{:[^\n]}\n")) { linesRead++; diff --git a/src/ingestor.hpp b/src/ingestor.hpp index 0bf2297..2ae2936 100644 --- a/src/ingestor.hpp +++ b/src/ingestor.hpp @@ -39,7 +39,7 @@ namespace WebStat { Ingestor(const utsname &, IngestorSettings); Ingestor(const utsname &, DB::ConnectionPoolPtr, IngestorSettings); - virtual ~Ingestor() = default; + virtual ~Ingestor(); SPECIAL_MEMBERS_DELETE(Ingestor); using ScanResult = decltype(scn::scan<std::string_view, std::string_view, uint64_t, std::string_view, @@ -63,6 +63,7 @@ namespace WebStat { IngestorSettings settings; protected: + static Ingestor * currentIngestor; DB::ConnectionPoolPtr dbpool; size_t linesRead = 0; @@ -71,6 +72,8 @@ namespace WebStat { size_t linesParked = 0; mutable std::flat_set<Crc32Value> existingEntities; + bool terminated = false; + struct Job { using LastRunTime = std::chrono::system_clock::time_point; using Impl = unsigned int (Ingestor::*)(); @@ -98,6 +101,9 @@ namespace WebStat { void jobIngestParkedLine(const std::filesystem::directory_iterator &); void jobIngestParkedLine(const std::filesystem::path &, uintmax_t size); + static void sigtermHandler(int); + void terminate(int); + using CurlOperations = std::map<CURL *, std::unique_ptr<CurlOperation>>; uint32_t hostnameId; CurlMultiPtr curl; diff --git a/test/test-ingest.cpp b/test/test-ingest.cpp index 41b3690..be3be56 100644 --- a/test/test-ingest.cpp +++ b/test/test-ingest.cpp @@ -249,6 +249,19 @@ BOOST_AUTO_TEST_CASE(StoreLog, *boost::unit_test::depends_on("I/StoreLogLine")) BOOST_CHECK_EQUAL(linesDiscarded, 0); } +BOOST_AUTO_TEST_CASE(TerminateHandler, *boost::unit_test::timeout(5)) +{ + WebStat::FilePtr input {fopen("/dev/null", "r")}; + BOOST_REQUIRE(input); + BOOST_REQUIRE(!terminated); + raise(SIGTERM); + BOOST_REQUIRE(terminated); + ingestLog(input.get()); + BOOST_CHECK_EQUAL(linesRead, 0); + BOOST_CHECK_EQUAL(linesParsed, 0); + BOOST_CHECK_EQUAL(linesDiscarded, 0); +} + BOOST_AUTO_TEST_CASE(ParkLogLine) { parkLogLine(LOGLINE1); |
