summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Goodliffe <dan.goodliffe@octal.co.uk>2026-03-19 12:33:48 +0000
committerDan Goodliffe <dan.goodliffe@octal.co.uk>2026-03-19 12:33:48 +0000
commit8c6fecd356003309f8eebec30374344272ca6072 (patch)
tree023f79e07a3abd0829c91bfffd5d2a5657d8a225
parentee01cb7017e3895419544a0385f90f8eb0498680 (diff)
downloadwebstat-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.cpp31
-rw-r--r--src/ingestor.hpp8
-rw-r--r--test/test-ingest.cpp13
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);