|
|
@@ -13,6 +13,10 @@
|
|
|
#include <string>
|
|
|
#include <vector>
|
|
|
|
|
|
+#if defined(__GLIBC__) && !defined(__APPLE__)
|
|
|
+#include <malloc.h>
|
|
|
+#endif
|
|
|
+
|
|
|
#ifdef HAVE_SYSTEMD
|
|
|
#include <systemd/sd-daemon.h>
|
|
|
#endif
|
|
|
@@ -142,7 +146,30 @@ std::filesystem::path findConfigFile(int argc, char* argv[]) {
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
+// v1.8.1: Cap glibc per-thread malloc arenas. With ~20+ threads
|
|
|
+// (eviction / WAL sync / snapshot / gRPC pool / replication apply +
|
|
|
+// connection loop), the default `8 × num_cpus` arenas fragment heap
|
|
|
+// pages so they're never returned to the OS, drifting RSS far above
|
|
|
+// the DB's tracked working set (Zoe: 6.5 GB gap, OOM every ~3h until
|
|
|
+// MALLOC_ARENA_MAX=2 was set). The systemd unit sets the env var as
|
|
|
+// the primary control; this programmatic call covers non-systemd
|
|
|
+// deployments (docker, ad-hoc launches). Skip if the env var is
|
|
|
+// already set, so operators can override.
|
|
|
+static void capGlibcArenas() {
|
|
|
+#if defined(__GLIBC__) && !defined(__APPLE__)
|
|
|
+ if (const char* v = std::getenv("MALLOC_ARENA_MAX"); v && *v) {
|
|
|
+ return; // Operator override in effect — respect it.
|
|
|
+ }
|
|
|
+ if (::mallopt(M_ARENA_MAX, 2) == 0) {
|
|
|
+ // mallopt returns nonzero on success; silent — the worst case is
|
|
|
+ // we run with default arenas and observe the historical drift.
|
|
|
+ }
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
int main(int argc, char* argv[]) {
|
|
|
+ capGlibcArenas();
|
|
|
+
|
|
|
// Parse command line arguments
|
|
|
for (int i = 1; i < argc; ++i) {
|
|
|
std::string arg = argv[i];
|