2017-05-30 22:26:16 +08:00
|
|
|
#include <experimental/filesystem>
|
2017-01-23 19:02:11 +08:00
|
|
|
#include <iostream>
|
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"
|
|
|
|
#include "communication/server.hpp"
|
|
|
|
|
|
|
|
#include "io/network/network_endpoint.hpp"
|
|
|
|
#include "io/network/network_error.hpp"
|
2016-08-10 16:39:02 +08:00
|
|
|
#include "io/network/socket.hpp"
|
|
|
|
|
2017-06-09 21:48:40 +08:00
|
|
|
#include "utils/flag_validation.hpp"
|
2017-09-22 20:24:53 +08:00
|
|
|
#include "utils/scheduler.hpp"
|
2016-12-16 20:56:36 +08:00
|
|
|
#include "utils/signals/handler.hpp"
|
2017-06-13 21:43:00 +08:00
|
|
|
#include "utils/stacktrace.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"
|
2016-08-01 01:58:12 +08:00
|
|
|
|
2017-05-30 22:26:16 +08:00
|
|
|
namespace fs = std::experimental::filesystem;
|
2017-03-06 20:37:51 +08:00
|
|
|
using endpoint_t = io::network::NetworkEndpoint;
|
|
|
|
using socket_t = io::network::Socket;
|
2017-03-22 23:36:48 +08:00
|
|
|
using session_t = communication::bolt::Session<socket_t>;
|
2017-05-22 18:31:04 +08:00
|
|
|
using result_stream_t =
|
|
|
|
communication::bolt::ResultStream<communication::bolt::Encoder<
|
|
|
|
communication::bolt::ChunkedEncoderBuffer<socket_t>>>;
|
2017-08-03 21:26:48 +08:00
|
|
|
using session_data_t = communication::bolt::SessionData<result_stream_t>;
|
2017-03-28 18:42:04 +08:00
|
|
|
using bolt_server_t =
|
2017-08-04 16:49:00 +08:00
|
|
|
communication::Server<session_t, socket_t, session_data_t>;
|
2017-03-06 20:37:51 +08:00
|
|
|
|
2017-05-22 18:31:04 +08:00
|
|
|
DEFINE_string(interface, "0.0.0.0", "Default interface on which to listen.");
|
|
|
|
DEFINE_string(port, "7687", "Default port on which to listen.");
|
2017-06-09 21:48:40 +08:00
|
|
|
DEFINE_VALIDATED_int32(num_workers,
|
|
|
|
std::max(std::thread::hardware_concurrency(), 1U),
|
|
|
|
"Number of workers", FLAG_IN_RANGE(1, INT32_MAX));
|
2017-06-16 15:07:33 +08:00
|
|
|
DEFINE_string(log_file, "memgraph.log",
|
|
|
|
"Path to where the log should be stored.");
|
2017-09-22 20:24:53 +08:00
|
|
|
DEFINE_uint64(
|
|
|
|
memory_warning_threshold, 1024,
|
|
|
|
"Memory warning treshold, in MB. If Memgraph detects there is less available "
|
|
|
|
"RAM available it will log a warning. Set to 0 to disable.");
|
2016-08-01 01:58:12 +08:00
|
|
|
|
2017-06-07 21:23:08 +08:00
|
|
|
// Load flags in this order, the last one has the highest priority:
|
|
|
|
// 1) /etc/memgraph/config
|
|
|
|
// 2) ~/.memgraph/config
|
|
|
|
// 3) env - MEMGRAPH_CONFIG
|
|
|
|
// 4) command line flags
|
|
|
|
|
|
|
|
void load_config(int &argc, char **&argv) {
|
|
|
|
std::vector<fs::path> configs = {fs::path("/etc/memgraph/config")};
|
|
|
|
if (getenv("HOME") != nullptr)
|
|
|
|
configs.emplace_back(fs::path(getenv("HOME")) /
|
|
|
|
fs::path(".memgraph/config"));
|
2017-09-11 21:02:16 +08:00
|
|
|
{
|
|
|
|
auto memgraph_config = getenv("MEMGRAPH_CONFIG");
|
|
|
|
if (memgraph_config != nullptr) {
|
|
|
|
auto path = fs::path(memgraph_config);
|
|
|
|
CHECK(fs::exists(path))
|
|
|
|
<< "MEMGRAPH_CONFIG environment variable set to nonexisting path: "
|
|
|
|
<< path.generic_string();
|
|
|
|
configs.emplace_back(path);
|
|
|
|
}
|
|
|
|
}
|
2017-06-07 21:23:08 +08:00
|
|
|
|
|
|
|
std::vector<std::string> flagfile_arguments;
|
|
|
|
for (const auto &config : configs)
|
|
|
|
if (fs::exists(config)) {
|
|
|
|
flagfile_arguments.emplace_back(
|
|
|
|
std::string("--flagfile=" + config.generic_string()));
|
|
|
|
}
|
|
|
|
|
|
|
|
int custom_argc = static_cast<int>(flagfile_arguments.size()) + 1;
|
|
|
|
char **custom_argv = new char *[custom_argc];
|
|
|
|
|
|
|
|
custom_argv[0] = strdup(std::string("memgraph").c_str());
|
2017-07-12 19:16:17 +08:00
|
|
|
for (int i = 0; i < static_cast<int>(flagfile_arguments.size()); ++i) {
|
2017-06-07 21:23:08 +08:00
|
|
|
custom_argv[i + 1] = strdup(flagfile_arguments[i].c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
// setup flags from config flags
|
|
|
|
gflags::ParseCommandLineFlags(&custom_argc, &custom_argv, false);
|
|
|
|
|
|
|
|
// unconsumed arguments have to be freed to avoid memory leak since they are
|
|
|
|
// strdup-ed.
|
|
|
|
for (int i = 0; i < custom_argc; ++i) free(custom_argv[i]);
|
|
|
|
delete[] custom_argv;
|
|
|
|
|
|
|
|
// setup flags from command line
|
|
|
|
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
|
|
|
}
|
|
|
|
|
2017-01-23 19:02:11 +08:00
|
|
|
int main(int argc, char **argv) {
|
2017-05-30 22:26:16 +08:00
|
|
|
fs::current_path(fs::path(argv[0]).parent_path());
|
2017-06-07 21:23:08 +08:00
|
|
|
load_config(argc, argv);
|
2017-06-08 19:30:59 +08:00
|
|
|
|
2017-06-21 17:29:13 +08:00
|
|
|
google::InitGoogleLogging(argv[0]);
|
|
|
|
google::SetLogDestination(google::INFO, FLAGS_log_file.c_str());
|
2017-01-23 19:02:11 +08:00
|
|
|
|
2017-06-06 21:48:04 +08:00
|
|
|
// Unhandled exception handler init.
|
2017-01-23 19:02:11 +08:00
|
|
|
std::set_terminate(&terminate_handler);
|
|
|
|
|
2017-06-06 21:48:04 +08:00
|
|
|
// Signal handling init.
|
2017-01-23 19:02:11 +08:00
|
|
|
SignalHandler::register_handler(Signal::SegmentationFault, []() {
|
Fix errors with handling SIGSEGV and SIGABRT
Summary:
There were a couple of issues with handling the above 2 signals.
1) Calling `std::exit` from a signal handler is undefined behaviour.
The only defined way for a signal handler to stop the program is calling
one of: `std::_Exit`, `std::abort` or `std::quick_exit`. Neither of them
will completely clean the resources, so a clean exit is not possible.
Since SIGSEGV and SIGABRT happen in extraordinary circumstances that we
wish to debug 99% of the time, it makes sense to generate a core dump
which can be inspected by a debugger. Of the 3 termination functions,
only `std::abort` will generate a core dump, so it makes sense to use
that to stop the program.
Also, since we are now aborting as is the default behaviour on SIGSEGV
and SIGABRT, it becomes questionable why have a custom handler at all.
2) Raising an exception inside a signal handler is undefined behaviour
Although the handler by itself does not raise an exception, it is
possible for the logging facility to raise one. This is a real case when
logging a stack trace in particular. Stack trace is generated by
creating a string "<function name> <line location>". It is possible that
a function name will contain '{}' somewhere inside. This is usually the
case with anonymous functions. The generated string is then passed to
logging, which uses the `fmt` library to fill '{}' with remaining
arguments. Since only a single argument (the stack trace string) is
passed for formatting, naturally the `fmt::format` throws an exception,
that it is missing a format argument.
We could provide an overload which takes a single string, but that
defeats the purpose of `fmt::format` raising an exception in regular
code if we forget to pass an argument. Another solution is to escape the
whole stack trace string, so it is valid for formatting, but this only
complicates the handler even further. The simplest solution is to send
the stack trace to `stderr` and avoid logging altogether.
Simplify Shutdown, so it can be used in a signal handler
Reviewers: florijan, mferencevic, buda, mislav.bradac
Reviewed By: mferencevic, buda
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D474
2017-06-14 22:37:23 +08:00
|
|
|
// Log that we got SIGSEGV and abort the program, because returning from
|
|
|
|
// SIGSEGV handler is undefined behaviour.
|
|
|
|
std::cerr << "SegmentationFault signal raised" << std::endl;
|
|
|
|
std::abort(); // This will continue into our SIGABRT handler.
|
2017-01-23 19:02:11 +08:00
|
|
|
});
|
|
|
|
SignalHandler::register_handler(Signal::Abort, []() {
|
Fix errors with handling SIGSEGV and SIGABRT
Summary:
There were a couple of issues with handling the above 2 signals.
1) Calling `std::exit` from a signal handler is undefined behaviour.
The only defined way for a signal handler to stop the program is calling
one of: `std::_Exit`, `std::abort` or `std::quick_exit`. Neither of them
will completely clean the resources, so a clean exit is not possible.
Since SIGSEGV and SIGABRT happen in extraordinary circumstances that we
wish to debug 99% of the time, it makes sense to generate a core dump
which can be inspected by a debugger. Of the 3 termination functions,
only `std::abort` will generate a core dump, so it makes sense to use
that to stop the program.
Also, since we are now aborting as is the default behaviour on SIGSEGV
and SIGABRT, it becomes questionable why have a custom handler at all.
2) Raising an exception inside a signal handler is undefined behaviour
Although the handler by itself does not raise an exception, it is
possible for the logging facility to raise one. This is a real case when
logging a stack trace in particular. Stack trace is generated by
creating a string "<function name> <line location>". It is possible that
a function name will contain '{}' somewhere inside. This is usually the
case with anonymous functions. The generated string is then passed to
logging, which uses the `fmt` library to fill '{}' with remaining
arguments. Since only a single argument (the stack trace string) is
passed for formatting, naturally the `fmt::format` throws an exception,
that it is missing a format argument.
We could provide an overload which takes a single string, but that
defeats the purpose of `fmt::format` raising an exception in regular
code if we forget to pass an argument. Another solution is to escape the
whole stack trace string, so it is valid for formatting, but this only
complicates the handler even further. The simplest solution is to send
the stack trace to `stderr` and avoid logging altogether.
Simplify Shutdown, so it can be used in a signal handler
Reviewers: florijan, mferencevic, buda, mislav.bradac
Reviewed By: mferencevic, buda
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D474
2017-06-14 22:37:23 +08:00
|
|
|
// Log the stacktrace and let the abort continue.
|
|
|
|
Stacktrace stacktrace;
|
|
|
|
std::cerr << "Abort signal raised" << std::endl
|
|
|
|
<< stacktrace.dump() << std::endl;
|
2017-01-23 19:02:11 +08:00
|
|
|
});
|
|
|
|
|
2017-09-18 20:58:30 +08:00
|
|
|
// Initialize bolt session data (Dbms and QueryEngine).
|
|
|
|
session_data_t session_data;
|
|
|
|
|
2017-06-06 21:48:04 +08:00
|
|
|
// Initialize endpoint.
|
2017-03-06 20:37:51 +08:00
|
|
|
endpoint_t endpoint;
|
2017-01-23 19:02:11 +08:00
|
|
|
try {
|
2017-05-22 18:31:04 +08:00
|
|
|
endpoint = endpoint_t(FLAGS_interface, FLAGS_port);
|
2017-03-06 20:37:51 +08:00
|
|
|
} catch (io::network::NetworkEndpointException &e) {
|
2017-06-21 17:29:13 +08:00
|
|
|
LOG(FATAL) << e.what();
|
2017-01-23 19:02:11 +08:00
|
|
|
}
|
2017-03-06 20:37:51 +08:00
|
|
|
|
2017-06-06 21:48:04 +08:00
|
|
|
// Initialize socket.
|
2017-03-06 20:37:51 +08:00
|
|
|
socket_t socket;
|
|
|
|
if (!socket.Bind(endpoint)) {
|
2017-07-12 18:44:21 +08:00
|
|
|
LOG(FATAL) << "Cannot bind to socket on " << FLAGS_interface << " at "
|
2017-06-21 17:29:13 +08:00
|
|
|
<< FLAGS_port;
|
2017-03-06 20:37:51 +08:00
|
|
|
}
|
|
|
|
if (!socket.SetNonBlocking()) {
|
2017-07-12 18:44:21 +08:00
|
|
|
LOG(FATAL) << "Cannot set socket to non blocking!";
|
2017-03-06 20:37:51 +08:00
|
|
|
}
|
|
|
|
if (!socket.Listen(1024)) {
|
2017-07-12 18:44:21 +08:00
|
|
|
LOG(FATAL) << "Cannot listen on socket!";
|
2017-03-06 20:37:51 +08:00
|
|
|
}
|
|
|
|
|
2017-06-06 21:48:04 +08:00
|
|
|
// Initialize server.
|
2017-08-03 21:26:48 +08:00
|
|
|
bolt_server_t server(std::move(socket), session_data);
|
2017-06-07 21:56:26 +08:00
|
|
|
|
|
|
|
// register SIGTERM handler
|
2017-06-09 18:25:54 +08:00
|
|
|
SignalHandler::register_handler(Signal::Terminate,
|
|
|
|
[&server]() { server.Shutdown(); });
|
2017-01-23 19:02:11 +08:00
|
|
|
|
2017-06-08 19:30:59 +08:00
|
|
|
// register SIGINT handler
|
2017-06-09 18:25:54 +08:00
|
|
|
SignalHandler::register_handler(Signal::Interupt,
|
|
|
|
[&server]() { server.Shutdown(); });
|
2017-06-08 19:30:59 +08:00
|
|
|
|
2017-09-22 20:24:53 +08:00
|
|
|
// Start memory warning logger.
|
|
|
|
Scheduler mem_log_scheduler;
|
|
|
|
if (FLAGS_memory_warning_threshold > 0) {
|
|
|
|
mem_log_scheduler.Run(std::chrono::seconds(3), [] {
|
|
|
|
auto free_ram_mb = utils::AvailableMem() / 1024;
|
|
|
|
if (free_ram_mb < FLAGS_memory_warning_threshold)
|
|
|
|
LOG(WARNING) << "Running out of available RAM, only " << free_ram_mb
|
|
|
|
<< " MB left.";
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-06-06 21:48:04 +08:00
|
|
|
// Start worker threads.
|
|
|
|
server.Start(FLAGS_num_workers);
|
2017-01-23 19:02:11 +08:00
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
2016-08-01 01:58:12 +08:00
|
|
|
}
|