2018-04-20 20:58:49 +08:00
|
|
|
#include <algorithm>
|
|
|
|
#include <chrono>
|
2017-10-31 16:49:33 +08:00
|
|
|
#include <csignal>
|
2018-04-20 20:58:49 +08:00
|
|
|
#include <cstdint>
|
|
|
|
#include <exception>
|
|
|
|
#include <functional>
|
|
|
|
#include <limits>
|
|
|
|
#include <string>
|
|
|
|
#include <thread>
|
2016-08-10 16:39:02 +08:00
|
|
|
|
2017-07-06 19:53:39 +08:00
|
|
|
#include <gflags/gflags.h>
|
|
|
|
#include <glog/logging.h>
|
2017-05-22 18:31:04 +08:00
|
|
|
|
2017-03-06 20:37:51 +08:00
|
|
|
#include "communication/bolt/v1/session.hpp"
|
2017-10-25 21:28:10 +08:00
|
|
|
#include "config.hpp"
|
2018-01-12 22:17:04 +08:00
|
|
|
#include "database/graph_db.hpp"
|
2018-02-02 18:11:06 +08:00
|
|
|
#include "stats/stats.hpp"
|
2017-06-09 21:48:40 +08:00
|
|
|
#include "utils/flag_validation.hpp"
|
2018-03-30 17:07:37 +08:00
|
|
|
#include "utils/signals.hpp"
|
2017-09-22 20:24:53 +08:00
|
|
|
#include "utils/sysinfo/memory.hpp"
|
2017-01-13 17:47:17 +08:00
|
|
|
#include "utils/terminate_handler.hpp"
|
2017-09-26 15:43:43 +08:00
|
|
|
#include "version.hpp"
|
|
|
|
|
2018-04-20 20:58:49 +08:00
|
|
|
// Common stuff for enterprise and community editions
|
|
|
|
|
2018-01-12 22:17:04 +08:00
|
|
|
using communication::bolt::SessionData;
|
2018-03-23 23:32:17 +08:00
|
|
|
using SessionT = communication::bolt::Session<communication::InputStream,
|
|
|
|
communication::OutputStream>;
|
2017-10-17 20:05:08 +08:00
|
|
|
using ServerT = communication::Server<SessionT, SessionData>;
|
2017-03-06 20:37:51 +08:00
|
|
|
|
2017-12-19 19:40:30 +08:00
|
|
|
// General purpose flags.
|
2017-09-25 21:56:14 +08:00
|
|
|
DEFINE_string(interface, "0.0.0.0",
|
|
|
|
"Communication interface on which to listen.");
|
2018-01-15 21:03:07 +08:00
|
|
|
DEFINE_VALIDATED_int32(port, 7687, "Communication port on which to listen.",
|
|
|
|
FLAG_IN_RANGE(0, std::numeric_limits<uint16_t>::max()));
|
2017-06-09 21:48:40 +08:00
|
|
|
DEFINE_VALIDATED_int32(num_workers,
|
|
|
|
std::max(std::thread::hardware_concurrency(), 1U),
|
2018-02-23 17:56:56 +08:00
|
|
|
"Number of workers (Bolt)", FLAG_IN_RANGE(1, INT32_MAX));
|
2018-03-23 23:32:17 +08:00
|
|
|
DEFINE_VALIDATED_int32(session_inactivity_timeout, 1800,
|
|
|
|
"Time in seconds after which inactive sessions will be "
|
|
|
|
"closed.",
|
|
|
|
FLAG_IN_RANGE(1, INT32_MAX));
|
2017-10-17 20:05:08 +08:00
|
|
|
DEFINE_string(log_file, "", "Path to where the log should be stored.");
|
2017-11-22 23:40:39 +08:00
|
|
|
DEFINE_HIDDEN_string(
|
|
|
|
log_link_basename, "",
|
|
|
|
"Basename used for symlink creation to the last log file.");
|
2017-09-25 21:56:14 +08:00
|
|
|
DEFINE_uint64(memory_warning_threshold, 1024,
|
2018-02-02 18:11:06 +08:00
|
|
|
"Memory warning threshold, in MB. If Memgraph detects there is "
|
|
|
|
"less available RAM it will log a warning. Set to 0 to "
|
2017-09-25 21:56:14 +08:00
|
|
|
"disable.");
|
2016-08-01 01:58:12 +08:00
|
|
|
|
2017-10-31 16:49:33 +08:00
|
|
|
// Needed to correctly handle memgraph destruction from a signal handler.
|
|
|
|
// Without having some sort of a flag, it is possible that a signal is handled
|
2018-01-12 22:17:04 +08:00
|
|
|
// when we are exiting main, inside destructors of database::GraphDb and
|
|
|
|
// similar. The signal handler may then initiate another shutdown on memgraph
|
|
|
|
// which is in half destructed state, causing invalid memory access and crash.
|
2017-10-31 16:49:33 +08:00
|
|
|
volatile sig_atomic_t is_shutting_down = 0;
|
|
|
|
|
2018-04-20 20:58:49 +08:00
|
|
|
/// Set up signal handlers and register `shutdown` on SIGTERM and SIGINT.
|
|
|
|
/// In most cases you don't have to call this. If you are using a custom server
|
|
|
|
/// startup function for `WithInit`, then you probably need to use this to
|
|
|
|
/// shutdown your server.
|
|
|
|
void InitSignalHandlers(const std::function<void()> &shutdown_fun) {
|
2017-10-31 16:49:33 +08:00
|
|
|
// Prevent handling shutdown inside a shutdown. For example, SIGINT handler
|
|
|
|
// being interrupted by SIGTERM before is_shutting_down is set, thus causing
|
|
|
|
// double shutdown.
|
|
|
|
sigset_t block_shutdown_signals;
|
|
|
|
sigemptyset(&block_shutdown_signals);
|
|
|
|
sigaddset(&block_shutdown_signals, SIGTERM);
|
|
|
|
sigaddset(&block_shutdown_signals, SIGINT);
|
|
|
|
|
2018-04-20 20:58:49 +08:00
|
|
|
// Wrap the shutdown function in a safe way to prevent recursive shutdown.
|
|
|
|
auto shutdown = [shutdown_fun]() {
|
|
|
|
if (is_shutting_down) return;
|
|
|
|
is_shutting_down = 1;
|
|
|
|
shutdown_fun();
|
|
|
|
};
|
|
|
|
|
2018-03-30 17:07:37 +08:00
|
|
|
CHECK(utils::SignalHandler::RegisterHandler(utils::Signal::Terminate,
|
|
|
|
shutdown, block_shutdown_signals))
|
2017-10-31 16:49:33 +08:00
|
|
|
<< "Unable to register SIGTERM handler!";
|
2018-03-30 17:07:37 +08:00
|
|
|
CHECK(utils::SignalHandler::RegisterHandler(utils::Signal::Interupt, shutdown,
|
|
|
|
block_shutdown_signals))
|
2017-10-31 16:49:33 +08:00
|
|
|
<< "Unable to register SIGINT handler!";
|
2017-06-08 19:30:59 +08:00
|
|
|
|
2017-11-22 23:40:39 +08:00
|
|
|
// Setup SIGUSR1 to be used for reopening log files, when e.g. logrotate
|
|
|
|
// rotates our logs.
|
2018-03-30 17:07:37 +08:00
|
|
|
CHECK(utils::SignalHandler::RegisterHandler(utils::Signal::User1, []() {
|
2017-11-22 23:40:39 +08:00
|
|
|
google::CloseLogDestination(google::INFO);
|
|
|
|
})) << "Unable to register SIGUSR1 handler!";
|
2017-12-19 19:40:30 +08:00
|
|
|
}
|
2017-11-22 23:40:39 +08:00
|
|
|
|
2018-04-20 20:58:49 +08:00
|
|
|
/// Run the Memgraph server.
|
|
|
|
///
|
|
|
|
/// Sets up all the required state before running `memgraph_main` and does any
|
|
|
|
/// required cleanup afterwards. `get_stats_prefix` is used to obtain the
|
|
|
|
/// prefix when logging Memgraph's statistics.
|
|
|
|
///
|
|
|
|
/// Command line arguments and configuration files are read before calling any
|
|
|
|
/// of the supplied functions. Therefore, you should use flags only from those
|
|
|
|
/// functions, and *not before* invoking `WithInit`.
|
|
|
|
///
|
|
|
|
/// This should be the first and last thing a OS specific main function does.
|
|
|
|
///
|
|
|
|
/// A common example of usage is:
|
|
|
|
///
|
|
|
|
/// @code
|
|
|
|
/// int main(int argc, char *argv[]) {
|
|
|
|
/// auto get_stats_prefix = []() -> std::string { return "memgraph"; };
|
|
|
|
/// return WithInit(argc, argv, get_stats_prefix, SingleNodeMain);
|
|
|
|
/// }
|
|
|
|
/// @endcode
|
|
|
|
///
|
|
|
|
/// If you wish to start Memgraph server in another way, you can pass a
|
|
|
|
/// `memgraph_main` functions which does that. You should take care to call
|
|
|
|
/// `InitSignalHandlers` with appropriate function to shutdown the server you
|
|
|
|
/// started.
|
|
|
|
int WithInit(int argc, char **argv,
|
|
|
|
const std::function<std::string()> &get_stats_prefix,
|
|
|
|
const std::function<void()> &memgraph_main) {
|
|
|
|
gflags::SetVersionString(version_string);
|
2017-12-19 19:40:30 +08:00
|
|
|
|
2018-04-20 20:58:49 +08:00
|
|
|
// Load config before parsing arguments, so that flags from the command line
|
|
|
|
// overwrite the config.
|
|
|
|
LoadConfig();
|
|
|
|
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
|
|
|
|
|
|
|
google::InitGoogleLogging(argv[0]);
|
|
|
|
google::SetLogDestination(google::INFO, FLAGS_log_file.c_str());
|
|
|
|
google::SetLogSymlink(google::INFO, FLAGS_log_link_basename.c_str());
|
|
|
|
|
|
|
|
// Unhandled exception handler init.
|
|
|
|
std::set_terminate(&utils::TerminateHandler);
|
|
|
|
|
|
|
|
stats::InitStatsLogging(get_stats_prefix());
|
|
|
|
utils::OnScopeExit stop_stats([] { stats::StopStatsLogging(); });
|
|
|
|
|
|
|
|
// Start memory warning logger.
|
|
|
|
utils::Scheduler mem_log_scheduler;
|
|
|
|
if (FLAGS_memory_warning_threshold > 0) {
|
|
|
|
mem_log_scheduler.Run("Memory warning", std::chrono::seconds(3), [] {
|
|
|
|
auto free_ram_mb = utils::sysinfo::AvailableMem() / 1024;
|
|
|
|
if (free_ram_mb < FLAGS_memory_warning_threshold)
|
|
|
|
LOG(WARNING) << "Running out of available RAM, only " << free_ram_mb
|
|
|
|
<< " MB left.";
|
|
|
|
});
|
|
|
|
}
|
|
|
|
memgraph_main();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SingleNodeMain() {
|
|
|
|
google::SetUsageMessage("Memgraph single-node database server");
|
|
|
|
database::SingleNode db;
|
2018-01-12 22:17:04 +08:00
|
|
|
SessionData session_data{db};
|
2018-01-15 21:03:07 +08:00
|
|
|
ServerT server({FLAGS_interface, static_cast<uint16_t>(FLAGS_port)},
|
2018-03-23 23:32:17 +08:00
|
|
|
session_data, FLAGS_session_inactivity_timeout, "Bolt",
|
|
|
|
FLAGS_num_workers);
|
2017-12-19 19:40:30 +08:00
|
|
|
|
|
|
|
// Handler for regular termination signals
|
2018-01-12 22:17:04 +08:00
|
|
|
auto shutdown = [&server] {
|
2017-12-19 19:40:30 +08:00
|
|
|
// Server needs to be shutdown first and then the database. This prevents a
|
|
|
|
// race condition when a transaction is accepted during server shutdown.
|
|
|
|
server.Shutdown();
|
|
|
|
};
|
|
|
|
InitSignalHandlers(shutdown);
|
2018-04-20 20:58:49 +08:00
|
|
|
|
2018-01-10 20:56:12 +08:00
|
|
|
server.AwaitShutdown();
|
2017-12-19 19:40:30 +08:00
|
|
|
}
|
|
|
|
|
2018-04-20 20:58:49 +08:00
|
|
|
// End common stuff for enterprise and community editions
|
|
|
|
|
|
|
|
#ifdef MG_COMMUNITY
|
|
|
|
|
|
|
|
int main(int argc, char **argv) {
|
|
|
|
return WithInit(argc, argv, []() { return "memgraph"; }, SingleNodeMain);
|
2017-12-19 19:40:30 +08:00
|
|
|
}
|
|
|
|
|
2018-04-20 20:58:49 +08:00
|
|
|
#else // enterprise edition
|
|
|
|
|
|
|
|
// Distributed flags.
|
|
|
|
DEFINE_HIDDEN_bool(
|
|
|
|
master, false,
|
|
|
|
"If this Memgraph server is the master in a distributed deployment.");
|
|
|
|
DEFINE_HIDDEN_bool(
|
|
|
|
worker, false,
|
|
|
|
"If this Memgraph server is a worker in a distributed deployment.");
|
|
|
|
DECLARE_int32(worker_id);
|
|
|
|
|
|
|
|
void MasterMain() {
|
|
|
|
google::SetUsageMessage("Memgraph distributed master");
|
|
|
|
|
|
|
|
database::Master db;
|
2018-01-12 22:17:04 +08:00
|
|
|
SessionData session_data{db};
|
2018-01-15 21:03:07 +08:00
|
|
|
ServerT server({FLAGS_interface, static_cast<uint16_t>(FLAGS_port)},
|
2018-03-23 23:32:17 +08:00
|
|
|
session_data, FLAGS_session_inactivity_timeout, "Bolt",
|
|
|
|
FLAGS_num_workers);
|
2017-12-19 19:40:30 +08:00
|
|
|
|
|
|
|
// Handler for regular termination signals
|
2018-01-12 22:17:04 +08:00
|
|
|
auto shutdown = [&server] {
|
2017-12-19 19:40:30 +08:00
|
|
|
// Server needs to be shutdown first and then the database. This prevents a
|
|
|
|
// race condition when a transaction is accepted during server shutdown.
|
|
|
|
server.Shutdown();
|
|
|
|
};
|
|
|
|
|
2018-04-20 20:58:49 +08:00
|
|
|
InitSignalHandlers(shutdown);
|
2018-01-10 20:56:12 +08:00
|
|
|
server.AwaitShutdown();
|
2017-12-19 19:40:30 +08:00
|
|
|
}
|
|
|
|
|
2018-04-20 20:58:49 +08:00
|
|
|
void WorkerMain() {
|
|
|
|
google::SetUsageMessage("Memgraph distributed worker");
|
|
|
|
database::Worker db;
|
|
|
|
db.WaitForShutdown();
|
|
|
|
}
|
2017-01-23 19:02:11 +08:00
|
|
|
|
2018-04-20 20:58:49 +08:00
|
|
|
int main(int argc, char **argv) {
|
|
|
|
auto get_stats_prefix = [&]() -> std::string {
|
|
|
|
if (FLAGS_master) {
|
|
|
|
return "master";
|
|
|
|
} else if (FLAGS_worker) {
|
|
|
|
return fmt::format("worker-{}", FLAGS_worker_id);
|
|
|
|
}
|
|
|
|
return "memgraph";
|
|
|
|
};
|
2018-02-02 18:11:06 +08:00
|
|
|
|
2018-04-20 20:58:49 +08:00
|
|
|
auto memgraph_main = [&]() {
|
|
|
|
CHECK(!(FLAGS_master && FLAGS_worker))
|
|
|
|
<< "Can't run Memgraph as worker and master at the same time";
|
|
|
|
if (FLAGS_master)
|
|
|
|
MasterMain();
|
|
|
|
else if (FLAGS_worker)
|
|
|
|
WorkerMain();
|
|
|
|
else
|
|
|
|
SingleNodeMain();
|
|
|
|
};
|
2018-02-23 21:35:16 +08:00
|
|
|
|
2018-04-20 20:58:49 +08:00
|
|
|
return WithInit(argc, argv, get_stats_prefix, memgraph_main);
|
2016-08-01 01:58:12 +08:00
|
|
|
}
|
2018-04-20 20:58:49 +08:00
|
|
|
|
|
|
|
#endif // enterprise edition
|