|
@@ -21,6 +21,29 @@
|
|
|
#include <lz4.h>
|
|
#include <lz4.h>
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
|
|
+#if defined(__GLIBC__) && !defined(__APPLE__)
|
|
|
|
|
+#include <malloc.h>
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
|
|
+namespace {
|
|
|
|
|
+// v1.9.1 — `malloc_trim(0)` after a big-buffer phase finishes. The
|
|
|
|
|
+// snapshot write and load paths both build multi-GB transient buffers
|
|
|
|
|
+// (uncompressed body + LZ4 chunks; per-collection JSON parses). When
|
|
|
|
|
+// those buffers free, glibc parks the pages on its arena freelist and
|
|
|
|
|
+// only reclaims them lazily during subsequent small allocations. For a
|
|
|
|
|
+// quiescent post-boot or post-snapshot process those allocations may
|
|
|
|
|
+// not arrive for hours, so the operator-visible VmRSS stays inflated
|
|
|
|
|
+// even though `estimatedMemoryBytes_` is correct. Trimming explicitly
|
|
|
|
|
+// here turns a 2.8 GB no-op back into a 2.8 GB drop (measured on Zoe
|
|
|
|
|
+// 2026-05-13 12:07 via the SIGUSR2 handler from v1.8.3 — same call,
|
|
|
|
|
+// just automated). No-op on non-glibc platforms.
|
|
|
|
|
+void releaseFreelistPages() {
|
|
|
|
|
+#if defined(__GLIBC__) && !defined(__APPLE__)
|
|
|
|
|
+ ::malloc_trim(0);
|
|
|
|
|
+#endif
|
|
|
|
|
+}
|
|
|
|
|
+} // namespace
|
|
|
|
|
+
|
|
|
namespace smartbotic::database {
|
|
namespace smartbotic::database {
|
|
|
|
|
|
|
|
namespace {
|
|
namespace {
|
|
@@ -274,6 +297,14 @@ std::filesystem::path SnapshotManager::createSnapshot(const MemoryStore& store,
|
|
|
cleanupOldSnapshots();
|
|
cleanupOldSnapshots();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // v1.9.1 — release freelist pages after the snapshot write. We just
|
|
|
|
|
+ // built and threw away the uncompressed serialized buffer
|
|
|
|
|
+ // (potentially several GB) plus LZ4 chunk staging buffers; same
|
|
|
|
|
+ // post-burst freelist-retention story as the boot-time load above.
|
|
|
|
|
+ // Trimming here keeps the periodic snapshot path from drifting RSS
|
|
|
|
|
+ // up over the day. Cheap relative to the snapshot write itself.
|
|
|
|
|
+ releaseFreelistPages();
|
|
|
|
|
+
|
|
|
return snapshotPath;
|
|
return snapshotPath;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -388,6 +419,14 @@ uint64_t SnapshotManager::loadSnapshot(const std::filesystem::path& path, Memory
|
|
|
store.clear();
|
|
store.clear();
|
|
|
deserializeStore(uncompressedData, store, header.version);
|
|
deserializeStore(uncompressedData, store, header.version);
|
|
|
|
|
|
|
|
|
|
+ // v1.9.1 — release allocator freelist pages now that the multi-GB
|
|
|
|
|
+ // transient buffers (decompressed body + per-doc JSON parses) are
|
|
|
|
|
+ // gone. Without this, glibc keeps those pages parked on the per-
|
|
|
|
|
+ // thread freelist; VmRSS stays high until subsequent small allocs
|
|
|
|
|
+ // happen to land in the right bin. On Zoe (5 GB snapshot) this
|
|
|
|
|
+ // drops post-boot RSS by ~2.8 GB.
|
|
|
|
|
+ releaseFreelistPages();
|
|
|
|
|
+
|
|
|
return header.walSequence;
|
|
return header.walSequence;
|
|
|
}
|
|
}
|
|
|
|
|
|