From 8bee913ce3e1edd0179a3541559a990490579130 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Mon, 2 Dec 2024 09:07:05 +0200 Subject: [PATCH] MDEV-33763 : Buffered error logging for Galera This is adaptation of feature by Zsolt Parragi for Percona XtraDB Cluster to MariaDB. Original feature is documented at https://www.percona.com/blog/introducing-buffered-error-logging-in-percona-server-for-mysql/. In this adaptation variable names are changed and integration to server code is different. Commit adds new variables wsrep_buffered_error_log_buffer_size ulong default 0 dynamic Size of buffer used in buffered error logging. Because wsrep debug setting produces large number of messages it is recomended to set this variable around 1-100Mb. wsrep_buffered_error_log_filename string default NULL static Name of the file where buffered error logging is done. wsrep_buffered_error_log_file_size ulong default 0 dynamic Maximum size of one buffered error log file. This variable should be configured to be 10-100 times bigger than buffer size. wsrep_buffered_error_log_rotations uint default 9 dynamic. Number of buffered error log files kept before deleting them. When wsrep_buffered_error_log_buffer_size is specified and non zero, a buffer is allocated with the specified size, and log messages are written there. This includes also wsrep debug messages. When this buffer gets full, the server shuts down, or crashes, the buffer is written to wsrep_buffered_error_log_filename. When log files reaches configured size limit log is rotated and only configured amount of log files are kept. This is useful in situations where debug logs create lots of output and slow down normal server operation, but are helpful for figuring out issues, as writing to a memory buffer has less overhead. Warning: When this feature is used server will write big number of messages to error log. Therefore, it is recomended to store these logs on separate location to avoid filling up your database disk. Furthermore, it is recomended to set up logrotate to delete old log files. Detailed changes: wsrep.h Log printing is now controlled by wsrep_debug_mode variable and controlled by wsrep_debug and wsrep_buffered_error_log_buffer_size variables. sql/log.cc Added sql_print_debug to log wsrep debug messages (WSREP_DEBUG()). print_buffer_to_file Actual buffered error logging if enabled will happen in this function. sql/mysqld.cc Buffered error logging is initialized and shutdown here if it is configured. sql/signal_handler.cc If buffered error logging is enabled remaining buffer is written before continuing signal handling. sql/sys_vars.cc New variables are defined here. sql/wsrep_buffered_error_log.cc sql/wsrep_buffered_error_log.h Buffered error log implementation using logger API (mysys/file_logger.cc). sql/wsrep_var.cc New variables update and check functions. mysys/file_logger.cc Added support for buffered error logging, file name changing and buffer resizing. --- include/m_ctype.h | 3 +- include/mysql/plugin_audit.h.pp | 14 +- include/mysql/plugin_auth.h.pp | 14 +- include/mysql/plugin_data_type.h.pp | 14 +- include/mysql/plugin_encryption.h.pp | 14 +- include/mysql/plugin_ftparser.h.pp | 14 +- include/mysql/plugin_function.h.pp | 14 +- include/mysql/plugin_password_validation.h.pp | 14 +- include/mysql/service_logger.h | 45 +- include/wsrep.h | 14 +- mysql-test/include/galera_variables_ok.inc | 2 +- .../include/galera_variables_ok_debug.inc | 2 +- mysql-test/include/send_kill_to_mysqld.inc | 38 ++ .../suite/galera/r/galera_defaults.result | 4 + ..._var_buffered_error_log_buffer_size.result | 86 ++++ ...ra_var_buffered_error_log_file_size.result | 56 +++ ...era_var_buffered_error_log_filename.result | 33 ++ ...ra_var_buffered_error_log_rotations.result | 69 +++ ...era_var_buffered_error_log_buffer_size.cnf | 9 + ...ra_var_buffered_error_log_buffer_size.test | 69 +++ ...alera_var_buffered_error_log_file_size.cnf | 13 + ...lera_var_buffered_error_log_file_size.test | 69 +++ ...galera_var_buffered_error_log_filename.cnf | 9 + ...alera_var_buffered_error_log_filename.test | 41 ++ ...alera_var_buffered_error_log_rotations.cnf | 13 + ...lera_var_buffered_error_log_rotations.test | 86 ++++ .../suite/sys_vars/r/sysvars_wsrep.result | 60 +++ ...uffered_error_log_buffer_size_basic.result | 39 ++ ..._buffered_error_log_file_size_basic.result | 39 ++ ...p_buffered_error_log_filename_basic.result | 30 ++ ..._buffered_error_log_rotations_basic.result | 39 ++ ..._buffered_error_log_buffer_size_basic.test | 29 ++ ...ep_buffered_error_log_file_size_basic.test | 29 ++ ...rep_buffered_error_log_filename_basic.test | 24 + ...ep_buffered_error_log_rotations_basic.test | 29 ++ mysql-test/suite/wsrep/r/variables.result | 97 ++-- .../suite/wsrep/r/variables_debug.result | 5 + mysql-test/suite/wsrep/t/variables.test | 1 + mysys/CMakeLists.txt | 2 +- mysys/file_logger.cc | 454 ++++++++++++++++++ {mysys => plugin/server_audit}/file_logger.c | 0 plugin/server_audit/server_audit.c | 10 +- plugin/server_audit/service_logger.h | 105 ++++ plugin/sql_errlog/sql_errlog.c | 4 +- sql/CMakeLists.txt | 1 + sql/handler.cc | 13 +- sql/log.cc | 76 ++- sql/log.h | 2 + sql/mysqld.cc | 14 +- sql/signal_handler.cc | 8 + sql/sql_parse.cc | 3 +- sql/sql_plugin_services.inl | 6 +- sql/sys_vars.cc | 36 +- sql/wsrep_buffered_error_log.cc | 160 ++++++ sql/wsrep_buffered_error_log.h | 56 +++ sql/wsrep_mysqld.cc | 17 +- sql/wsrep_mysqld.h | 18 +- sql/wsrep_trans_observer.h | 6 - sql/wsrep_var.cc | 177 ++++++- sql/wsrep_var.h | 11 +- 60 files changed, 2246 insertions(+), 113 deletions(-) create mode 100644 mysql-test/include/send_kill_to_mysqld.inc create mode 100644 mysql-test/suite/galera/r/galera_var_buffered_error_log_buffer_size.result create mode 100644 mysql-test/suite/galera/r/galera_var_buffered_error_log_file_size.result create mode 100644 mysql-test/suite/galera/r/galera_var_buffered_error_log_filename.result create mode 100644 mysql-test/suite/galera/r/galera_var_buffered_error_log_rotations.result create mode 100644 mysql-test/suite/galera/t/galera_var_buffered_error_log_buffer_size.cnf create mode 100644 mysql-test/suite/galera/t/galera_var_buffered_error_log_buffer_size.test create mode 100644 mysql-test/suite/galera/t/galera_var_buffered_error_log_file_size.cnf create mode 100644 mysql-test/suite/galera/t/galera_var_buffered_error_log_file_size.test create mode 100644 mysql-test/suite/galera/t/galera_var_buffered_error_log_filename.cnf create mode 100644 mysql-test/suite/galera/t/galera_var_buffered_error_log_filename.test create mode 100644 mysql-test/suite/galera/t/galera_var_buffered_error_log_rotations.cnf create mode 100644 mysql-test/suite/galera/t/galera_var_buffered_error_log_rotations.test create mode 100644 mysql-test/suite/sys_vars/r/wsrep_buffered_error_log_buffer_size_basic.result create mode 100644 mysql-test/suite/sys_vars/r/wsrep_buffered_error_log_file_size_basic.result create mode 100644 mysql-test/suite/sys_vars/r/wsrep_buffered_error_log_filename_basic.result create mode 100644 mysql-test/suite/sys_vars/r/wsrep_buffered_error_log_rotations_basic.result create mode 100644 mysql-test/suite/sys_vars/t/wsrep_buffered_error_log_buffer_size_basic.test create mode 100644 mysql-test/suite/sys_vars/t/wsrep_buffered_error_log_file_size_basic.test create mode 100644 mysql-test/suite/sys_vars/t/wsrep_buffered_error_log_filename_basic.test create mode 100644 mysql-test/suite/sys_vars/t/wsrep_buffered_error_log_rotations_basic.test create mode 100644 mysys/file_logger.cc rename {mysys => plugin/server_audit}/file_logger.c (100%) create mode 100644 plugin/server_audit/service_logger.h create mode 100644 sql/wsrep_buffered_error_log.cc create mode 100644 sql/wsrep_buffered_error_log.h diff --git a/include/m_ctype.h b/include/m_ctype.h index 8673794356a06..215837972eb06 100644 --- a/include/m_ctype.h +++ b/include/m_ctype.h @@ -27,7 +27,8 @@ enum loglevel { ERROR_LEVEL= 0, WARNING_LEVEL= 1, - INFORMATION_LEVEL= 2 + INFORMATION_LEVEL= 2, + DEBUG_LEVEL= 3 }; #ifdef __cplusplus diff --git a/include/mysql/plugin_audit.h.pp b/include/mysql/plugin_audit.h.pp index 3d57b383f10cc..d589948f526bc 100644 --- a/include/mysql/plugin_audit.h.pp +++ b/include/mysql/plugin_audit.h.pp @@ -128,22 +128,32 @@ void (*logger_init_mutexes)(); LOGGER_HANDLE* (*open)(const char *path, unsigned long long size_limit, + unsigned long long buffer_limit, unsigned int rotations); int (*close)(LOGGER_HANDLE *log); int (*vprintf)(LOGGER_HANDLE *log, const char *fmt, va_list argptr); int (*printf)(LOGGER_HANDLE *log, const char *fmt, ...); int (*write)(LOGGER_HANDLE *log, const char *buffer, size_t size); - int (*rotate)(LOGGER_HANDLE *log); + int (*rotate)(LOGGER_HANDLE *log, const unsigned int n_rotations); + int (*rename_file)(LOGGER_HANDLE *log, const char *path); + int (*resize_buffer)(LOGGER_HANDLE *log, unsigned long long buffer_limit); + int (*resize_size)(LOGGER_HANDLE *log, unsigned long long size_limit); + int (*flush)(LOGGER_HANDLE *log); } *logger_service; void logger_init_mutexes(); LOGGER_HANDLE *logger_open(const char *path, unsigned long long size_limit, + unsigned long long buffer_limit, unsigned int rotations); int logger_close(LOGGER_HANDLE *log); int logger_vprintf(LOGGER_HANDLE *log, const char *fmt, va_list argptr); int logger_printf(LOGGER_HANDLE *log, const char *fmt, ...); int logger_write(LOGGER_HANDLE *log, const char *buffer, size_t size); - int logger_rotate(LOGGER_HANDLE *log); + int logger_rotate(LOGGER_HANDLE *log, const unsigned int n_rotations); + int logger_rename_file(LOGGER_HANDLE *log, const char* path); + int logger_resize_buffer(LOGGER_HANDLE *log, unsigned long long buffer_limit); + int logger_resize_size(LOGGER_HANDLE *log, unsigned long long size_limit); + int logger_flush(LOGGER_HANDLE *log); } extern "C" { extern struct my_md5_service_st { diff --git a/include/mysql/plugin_auth.h.pp b/include/mysql/plugin_auth.h.pp index fbb4f5910ecf9..65a2f9e4a3cb4 100644 --- a/include/mysql/plugin_auth.h.pp +++ b/include/mysql/plugin_auth.h.pp @@ -128,22 +128,32 @@ void (*logger_init_mutexes)(); LOGGER_HANDLE* (*open)(const char *path, unsigned long long size_limit, + unsigned long long buffer_limit, unsigned int rotations); int (*close)(LOGGER_HANDLE *log); int (*vprintf)(LOGGER_HANDLE *log, const char *fmt, va_list argptr); int (*printf)(LOGGER_HANDLE *log, const char *fmt, ...); int (*write)(LOGGER_HANDLE *log, const char *buffer, size_t size); - int (*rotate)(LOGGER_HANDLE *log); + int (*rotate)(LOGGER_HANDLE *log, const unsigned int n_rotations); + int (*rename_file)(LOGGER_HANDLE *log, const char *path); + int (*resize_buffer)(LOGGER_HANDLE *log, unsigned long long buffer_limit); + int (*resize_size)(LOGGER_HANDLE *log, unsigned long long size_limit); + int (*flush)(LOGGER_HANDLE *log); } *logger_service; void logger_init_mutexes(); LOGGER_HANDLE *logger_open(const char *path, unsigned long long size_limit, + unsigned long long buffer_limit, unsigned int rotations); int logger_close(LOGGER_HANDLE *log); int logger_vprintf(LOGGER_HANDLE *log, const char *fmt, va_list argptr); int logger_printf(LOGGER_HANDLE *log, const char *fmt, ...); int logger_write(LOGGER_HANDLE *log, const char *buffer, size_t size); - int logger_rotate(LOGGER_HANDLE *log); + int logger_rotate(LOGGER_HANDLE *log, const unsigned int n_rotations); + int logger_rename_file(LOGGER_HANDLE *log, const char* path); + int logger_resize_buffer(LOGGER_HANDLE *log, unsigned long long buffer_limit); + int logger_resize_size(LOGGER_HANDLE *log, unsigned long long size_limit); + int logger_flush(LOGGER_HANDLE *log); } extern "C" { extern struct my_md5_service_st { diff --git a/include/mysql/plugin_data_type.h.pp b/include/mysql/plugin_data_type.h.pp index be20dcccd557a..c21e367404bb8 100644 --- a/include/mysql/plugin_data_type.h.pp +++ b/include/mysql/plugin_data_type.h.pp @@ -128,22 +128,32 @@ void (*logger_init_mutexes)(); LOGGER_HANDLE* (*open)(const char *path, unsigned long long size_limit, + unsigned long long buffer_limit, unsigned int rotations); int (*close)(LOGGER_HANDLE *log); int (*vprintf)(LOGGER_HANDLE *log, const char *fmt, va_list argptr); int (*printf)(LOGGER_HANDLE *log, const char *fmt, ...); int (*write)(LOGGER_HANDLE *log, const char *buffer, size_t size); - int (*rotate)(LOGGER_HANDLE *log); + int (*rotate)(LOGGER_HANDLE *log, const unsigned int n_rotations); + int (*rename_file)(LOGGER_HANDLE *log, const char *path); + int (*resize_buffer)(LOGGER_HANDLE *log, unsigned long long buffer_limit); + int (*resize_size)(LOGGER_HANDLE *log, unsigned long long size_limit); + int (*flush)(LOGGER_HANDLE *log); } *logger_service; void logger_init_mutexes(); LOGGER_HANDLE *logger_open(const char *path, unsigned long long size_limit, + unsigned long long buffer_limit, unsigned int rotations); int logger_close(LOGGER_HANDLE *log); int logger_vprintf(LOGGER_HANDLE *log, const char *fmt, va_list argptr); int logger_printf(LOGGER_HANDLE *log, const char *fmt, ...); int logger_write(LOGGER_HANDLE *log, const char *buffer, size_t size); - int logger_rotate(LOGGER_HANDLE *log); + int logger_rotate(LOGGER_HANDLE *log, const unsigned int n_rotations); + int logger_rename_file(LOGGER_HANDLE *log, const char* path); + int logger_resize_buffer(LOGGER_HANDLE *log, unsigned long long buffer_limit); + int logger_resize_size(LOGGER_HANDLE *log, unsigned long long size_limit); + int logger_flush(LOGGER_HANDLE *log); } extern "C" { extern struct my_md5_service_st { diff --git a/include/mysql/plugin_encryption.h.pp b/include/mysql/plugin_encryption.h.pp index 21edc27e4e350..b235dae1814ab 100644 --- a/include/mysql/plugin_encryption.h.pp +++ b/include/mysql/plugin_encryption.h.pp @@ -128,22 +128,32 @@ void (*logger_init_mutexes)(); LOGGER_HANDLE* (*open)(const char *path, unsigned long long size_limit, + unsigned long long buffer_limit, unsigned int rotations); int (*close)(LOGGER_HANDLE *log); int (*vprintf)(LOGGER_HANDLE *log, const char *fmt, va_list argptr); int (*printf)(LOGGER_HANDLE *log, const char *fmt, ...); int (*write)(LOGGER_HANDLE *log, const char *buffer, size_t size); - int (*rotate)(LOGGER_HANDLE *log); + int (*rotate)(LOGGER_HANDLE *log, const unsigned int n_rotations); + int (*rename_file)(LOGGER_HANDLE *log, const char *path); + int (*resize_buffer)(LOGGER_HANDLE *log, unsigned long long buffer_limit); + int (*resize_size)(LOGGER_HANDLE *log, unsigned long long size_limit); + int (*flush)(LOGGER_HANDLE *log); } *logger_service; void logger_init_mutexes(); LOGGER_HANDLE *logger_open(const char *path, unsigned long long size_limit, + unsigned long long buffer_limit, unsigned int rotations); int logger_close(LOGGER_HANDLE *log); int logger_vprintf(LOGGER_HANDLE *log, const char *fmt, va_list argptr); int logger_printf(LOGGER_HANDLE *log, const char *fmt, ...); int logger_write(LOGGER_HANDLE *log, const char *buffer, size_t size); - int logger_rotate(LOGGER_HANDLE *log); + int logger_rotate(LOGGER_HANDLE *log, const unsigned int n_rotations); + int logger_rename_file(LOGGER_HANDLE *log, const char* path); + int logger_resize_buffer(LOGGER_HANDLE *log, unsigned long long buffer_limit); + int logger_resize_size(LOGGER_HANDLE *log, unsigned long long size_limit); + int logger_flush(LOGGER_HANDLE *log); } extern "C" { extern struct my_md5_service_st { diff --git a/include/mysql/plugin_ftparser.h.pp b/include/mysql/plugin_ftparser.h.pp index 58b8274fcb0cc..5b09e059f279e 100644 --- a/include/mysql/plugin_ftparser.h.pp +++ b/include/mysql/plugin_ftparser.h.pp @@ -128,22 +128,32 @@ void (*logger_init_mutexes)(); LOGGER_HANDLE* (*open)(const char *path, unsigned long long size_limit, + unsigned long long buffer_limit, unsigned int rotations); int (*close)(LOGGER_HANDLE *log); int (*vprintf)(LOGGER_HANDLE *log, const char *fmt, va_list argptr); int (*printf)(LOGGER_HANDLE *log, const char *fmt, ...); int (*write)(LOGGER_HANDLE *log, const char *buffer, size_t size); - int (*rotate)(LOGGER_HANDLE *log); + int (*rotate)(LOGGER_HANDLE *log, const unsigned int n_rotations); + int (*rename_file)(LOGGER_HANDLE *log, const char *path); + int (*resize_buffer)(LOGGER_HANDLE *log, unsigned long long buffer_limit); + int (*resize_size)(LOGGER_HANDLE *log, unsigned long long size_limit); + int (*flush)(LOGGER_HANDLE *log); } *logger_service; void logger_init_mutexes(); LOGGER_HANDLE *logger_open(const char *path, unsigned long long size_limit, + unsigned long long buffer_limit, unsigned int rotations); int logger_close(LOGGER_HANDLE *log); int logger_vprintf(LOGGER_HANDLE *log, const char *fmt, va_list argptr); int logger_printf(LOGGER_HANDLE *log, const char *fmt, ...); int logger_write(LOGGER_HANDLE *log, const char *buffer, size_t size); - int logger_rotate(LOGGER_HANDLE *log); + int logger_rotate(LOGGER_HANDLE *log, const unsigned int n_rotations); + int logger_rename_file(LOGGER_HANDLE *log, const char* path); + int logger_resize_buffer(LOGGER_HANDLE *log, unsigned long long buffer_limit); + int logger_resize_size(LOGGER_HANDLE *log, unsigned long long size_limit); + int logger_flush(LOGGER_HANDLE *log); } extern "C" { extern struct my_md5_service_st { diff --git a/include/mysql/plugin_function.h.pp b/include/mysql/plugin_function.h.pp index f1c4ad93b33ac..d4f6396440880 100644 --- a/include/mysql/plugin_function.h.pp +++ b/include/mysql/plugin_function.h.pp @@ -128,22 +128,32 @@ void (*logger_init_mutexes)(); LOGGER_HANDLE* (*open)(const char *path, unsigned long long size_limit, + unsigned long long buffer_limit, unsigned int rotations); int (*close)(LOGGER_HANDLE *log); int (*vprintf)(LOGGER_HANDLE *log, const char *fmt, va_list argptr); int (*printf)(LOGGER_HANDLE *log, const char *fmt, ...); int (*write)(LOGGER_HANDLE *log, const char *buffer, size_t size); - int (*rotate)(LOGGER_HANDLE *log); + int (*rotate)(LOGGER_HANDLE *log, const unsigned int n_rotations); + int (*rename_file)(LOGGER_HANDLE *log, const char *path); + int (*resize_buffer)(LOGGER_HANDLE *log, unsigned long long buffer_limit); + int (*resize_size)(LOGGER_HANDLE *log, unsigned long long size_limit); + int (*flush)(LOGGER_HANDLE *log); } *logger_service; void logger_init_mutexes(); LOGGER_HANDLE *logger_open(const char *path, unsigned long long size_limit, + unsigned long long buffer_limit, unsigned int rotations); int logger_close(LOGGER_HANDLE *log); int logger_vprintf(LOGGER_HANDLE *log, const char *fmt, va_list argptr); int logger_printf(LOGGER_HANDLE *log, const char *fmt, ...); int logger_write(LOGGER_HANDLE *log, const char *buffer, size_t size); - int logger_rotate(LOGGER_HANDLE *log); + int logger_rotate(LOGGER_HANDLE *log, const unsigned int n_rotations); + int logger_rename_file(LOGGER_HANDLE *log, const char* path); + int logger_resize_buffer(LOGGER_HANDLE *log, unsigned long long buffer_limit); + int logger_resize_size(LOGGER_HANDLE *log, unsigned long long size_limit); + int logger_flush(LOGGER_HANDLE *log); } extern "C" { extern struct my_md5_service_st { diff --git a/include/mysql/plugin_password_validation.h.pp b/include/mysql/plugin_password_validation.h.pp index 5f9bd1b398933..2149783dd6c52 100644 --- a/include/mysql/plugin_password_validation.h.pp +++ b/include/mysql/plugin_password_validation.h.pp @@ -128,22 +128,32 @@ void (*logger_init_mutexes)(); LOGGER_HANDLE* (*open)(const char *path, unsigned long long size_limit, + unsigned long long buffer_limit, unsigned int rotations); int (*close)(LOGGER_HANDLE *log); int (*vprintf)(LOGGER_HANDLE *log, const char *fmt, va_list argptr); int (*printf)(LOGGER_HANDLE *log, const char *fmt, ...); int (*write)(LOGGER_HANDLE *log, const char *buffer, size_t size); - int (*rotate)(LOGGER_HANDLE *log); + int (*rotate)(LOGGER_HANDLE *log, const unsigned int n_rotations); + int (*rename_file)(LOGGER_HANDLE *log, const char *path); + int (*resize_buffer)(LOGGER_HANDLE *log, unsigned long long buffer_limit); + int (*resize_size)(LOGGER_HANDLE *log, unsigned long long size_limit); + int (*flush)(LOGGER_HANDLE *log); } *logger_service; void logger_init_mutexes(); LOGGER_HANDLE *logger_open(const char *path, unsigned long long size_limit, + unsigned long long buffer_limit, unsigned int rotations); int logger_close(LOGGER_HANDLE *log); int logger_vprintf(LOGGER_HANDLE *log, const char *fmt, va_list argptr); int logger_printf(LOGGER_HANDLE *log, const char *fmt, ...); int logger_write(LOGGER_HANDLE *log, const char *buffer, size_t size); - int logger_rotate(LOGGER_HANDLE *log); + int logger_rotate(LOGGER_HANDLE *log, const unsigned int n_rotations); + int logger_rename_file(LOGGER_HANDLE *log, const char* path); + int logger_resize_buffer(LOGGER_HANDLE *log, unsigned long long buffer_limit); + int logger_resize_size(LOGGER_HANDLE *log, unsigned long long size_limit); + int logger_flush(LOGGER_HANDLE *log); } extern "C" { extern struct my_md5_service_st { diff --git a/include/mysql/service_logger.h b/include/mysql/service_logger.h index 5979901bdd0c3..8b965c19601cd 100644 --- a/include/mysql/service_logger.h +++ b/include/mysql/service_logger.h @@ -24,17 +24,22 @@ @file logger service - Log file with rotation implementation. + Log file with buffered writing and rotation implementation. - This service implements logging with possible rotation - of the log files. Interface intentionally tries to be similar to FILE* - related functions. + This service implements logging with possible buffered writing + and rotation of the log files. Interface intentionally tries to + be similar to FILE* related functions. So that one can open the log with logger_open(), specifying - the limit on the logfile size and the rotations number. + the limit on the logfile size, possible buffer size and the + rotations number. + + If buffer size is given, messages are written to buffer. + If size of messages in buffer grow over the specified + limit, they are written to logfile. Then it's possible to write messages to the log with - logger_printf or logger_vprintf functions. + logger_printf, logger_vprintf or logger_write functions. As the size of the logfile grows over the specified limit, it is renamed to 'logfile.1'. The former 'logfile.1' becomes @@ -63,37 +68,55 @@ extern struct logger_service_st { void (*logger_init_mutexes)(); LOGGER_HANDLE* (*open)(const char *path, unsigned long long size_limit, + unsigned long long buffer_limit, unsigned int rotations); int (*close)(LOGGER_HANDLE *log); int (*vprintf)(LOGGER_HANDLE *log, const char *fmt, va_list argptr); int (*printf)(LOGGER_HANDLE *log, const char *fmt, ...); int (*write)(LOGGER_HANDLE *log, const char *buffer, size_t size); - int (*rotate)(LOGGER_HANDLE *log); + int (*rotate)(LOGGER_HANDLE *log, const unsigned int n_rotations); + int (*rename_file)(LOGGER_HANDLE *log, const char *path); + int (*resize_buffer)(LOGGER_HANDLE *log, unsigned long long buffer_limit); + int (*resize_size)(LOGGER_HANDLE *log, unsigned long long size_limit); + int (*flush)(LOGGER_HANDLE *log); } *logger_service; #ifdef MYSQL_DYNAMIC_PLUGIN #define logger_init_mutexes logger_service->logger_init_mutexes -#define logger_open(path, size_limit, rotations) \ - (logger_service->open(path, size_limit, rotations)) +#define logger_open(path, size_limit, buffer_limit, rotations) \ + (logger_service->open(path, size_limit, buffer_limit, rotations)) #define logger_close(log) (logger_service->close(log)) -#define logger_rotate(log) (logger_service->rotate(log)) +#define logger_rotate(log, rotations) (logger_service->rotate(log, rotations)) #define logger_vprintf(log, fmt, argptr) (logger_service->\ vprintf(log, fmt, argptr)) #define logger_printf (*logger_service->printf) #define logger_write(log, buffer, size) \ (logger_service->write(log, buffer, size)) +#define logger_rename_file(log, path) \ + (logger_service->rename_file(log, path)) +#define logger_resize_buffer(log, buffer_limit) \ + (logger_service->resize_buffer(log, path)) +#define logger_resize_size(log, size_limit) \ + (logger_service->resize_size(log, size_limit)) +#define logger_flush(log) \ + (logger_service->flush(log)) #else void logger_init_mutexes(); LOGGER_HANDLE *logger_open(const char *path, unsigned long long size_limit, + unsigned long long buffer_limit, unsigned int rotations); int logger_close(LOGGER_HANDLE *log); int logger_vprintf(LOGGER_HANDLE *log, const char *fmt, va_list argptr); int logger_printf(LOGGER_HANDLE *log, const char *fmt, ...); int logger_write(LOGGER_HANDLE *log, const char *buffer, size_t size); - int logger_rotate(LOGGER_HANDLE *log); + int logger_rotate(LOGGER_HANDLE *log, const unsigned int n_rotations); + int logger_rename_file(LOGGER_HANDLE *log, const char* path); + int logger_resize_buffer(LOGGER_HANDLE *log, unsigned long long buffer_limit); + int logger_resize_size(LOGGER_HANDLE *log, unsigned long long size_limit); + int logger_flush(LOGGER_HANDLE *log); #endif diff --git a/include/wsrep.h b/include/wsrep.h index e4535deabfe5c..e5ab917399353 100644 --- a/include/wsrep.h +++ b/include/wsrep.h @@ -23,11 +23,17 @@ #define IF_WSREP(A,B) A #define DBUG_ASSERT_IF_WSREP(A) DBUG_ASSERT(A) -extern ulong wsrep_debug; // wsrep_mysqld.cc +/** wsrep_debug_mode is controlled by wsrep_debug and +wsrep_buffered_error_log_buffer_size variables. +If any of these are set we output also debug messages. */ +extern ulong wsrep_debug_mode; // wsrep_var.cc + extern void WSREP_LOG(void (*fun)(const char* fmt, ...), const char* fmt, ...); -#define WSREP_DEBUG(...) \ - if (wsrep_debug) WSREP_LOG(sql_print_information, ##__VA_ARGS__) +#define WSREP_DEBUG(...) \ + if (wsrep_debug_mode) \ + WSREP_LOG(sql_print_debug, ##__VA_ARGS__) + #define WSREP_INFO(...) WSREP_LOG(sql_print_information, ##__VA_ARGS__) #define WSREP_WARN(...) WSREP_LOG(sql_print_warning, ##__VA_ARGS__) #define WSREP_ERROR(...) WSREP_LOG(sql_print_error, ##__VA_ARGS__) @@ -47,7 +53,7 @@ extern void WSREP_LOG(void (*fun)(const char* fmt, ...), const char* fmt, ...); ); #define WSREP_LOG_CONFLICT(bf_thd, victim_thd, bf_abort) \ - if (wsrep_debug || wsrep_log_conflicts) \ + if (wsrep_debug_mode || wsrep_log_conflicts) \ { \ WSREP_INFO("cluster conflict due to %s for threads:", \ (bf_abort) ? "high priority abort" : "certification failure" \ diff --git a/mysql-test/include/galera_variables_ok.inc b/mysql-test/include/galera_variables_ok.inc index e420b3af6c30b..3dc9c65566954 100644 --- a/mysql-test/include/galera_variables_ok.inc +++ b/mysql-test/include/galera_variables_ok.inc @@ -1,6 +1,6 @@ --disable_query_log ---let $galera_variables_ok = `SELECT COUNT(*) = 51 FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'wsrep%'` +--let $galera_variables_ok = `SELECT COUNT(*) = 53 FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'wsrep%'` --if (!$galera_variables_ok) { --skip Galera number of variables has changed! diff --git a/mysql-test/include/galera_variables_ok_debug.inc b/mysql-test/include/galera_variables_ok_debug.inc index e420b3af6c30b..3dc9c65566954 100644 --- a/mysql-test/include/galera_variables_ok_debug.inc +++ b/mysql-test/include/galera_variables_ok_debug.inc @@ -1,6 +1,6 @@ --disable_query_log ---let $galera_variables_ok = `SELECT COUNT(*) = 51 FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'wsrep%'` +--let $galera_variables_ok = `SELECT COUNT(*) = 53 FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'wsrep%'` --if (!$galera_variables_ok) { --skip Galera number of variables has changed! diff --git a/mysql-test/include/send_kill_to_mysqld.inc b/mysql-test/include/send_kill_to_mysqld.inc new file mode 100644 index 0000000000000..857426daece90 --- /dev/null +++ b/mysql-test/include/send_kill_to_mysqld.inc @@ -0,0 +1,38 @@ +# ==== Purpose ==== +# +# send kill signal to mysqld process. +# +# ==== Usage ==== +# +# [--let $_kill_signal= 9] +# --source include/send_kill_to_mysqld.inc +# +# Parameters: +# +# $_kill_signal +# Which signal should be sent to mysqld. (default: 9 / SIGKILL) + +if ($_kill_signal == '') +{ + --let $_kill_signal = 9 +} +--echo Sending kill signal $_kill_signal to mysqld ... + +# Write file to make mysql-test-run.pl expect the crash, but don't start it +--source include/expect_crash.inc + +# Kill the connected server +--disable_reconnect +--let KILL_NODE_PIDFILE = `SELECT @@pid_file` +--let KILL_SIGNAL = $_kill_signal +--perl + my $pid_filename = $ENV{'KILL_NODE_PIDFILE'}; + my $mysqld_pid = `cat $pid_filename`; + my $kill_signal = $ENV{'KILL_SIGNAL'}; + chomp($mysqld_pid); + chomp($kill_signal); + system("kill -$kill_signal $mysqld_pid"); + exit(0); +EOF + +--source include/wait_until_disconnected.inc diff --git a/mysql-test/suite/galera/r/galera_defaults.result b/mysql-test/suite/galera/r/galera_defaults.result index 56c45b71632ca..14a5cd41f089f 100644 --- a/mysql-test/suite/galera/r/galera_defaults.result +++ b/mysql-test/suite/galera/r/galera_defaults.result @@ -19,6 +19,10 @@ ORDER BY VARIABLE_NAME; VARIABLE_NAME VARIABLE_VALUE WSREP_ALLOWLIST WSREP_AUTO_INCREMENT_CONTROL ON +WSREP_BUFFERED_ERROR_LOG_BUFFER_SIZE 0 +WSREP_BUFFERED_ERROR_LOG_FILENAME +WSREP_BUFFERED_ERROR_LOG_FILE_SIZE 0 +WSREP_BUFFERED_ERROR_LOG_ROTATIONS 10 WSREP_CERTIFICATION_RULES strict WSREP_CERTIFY_NONPK ON WSREP_CLUSTER_ADDRESS gcomm:// diff --git a/mysql-test/suite/galera/r/galera_var_buffered_error_log_buffer_size.result b/mysql-test/suite/galera/r/galera_var_buffered_error_log_buffer_size.result new file mode 100644 index 0000000000000..6ebcf93580533 --- /dev/null +++ b/mysql-test/suite/galera/r/galera_var_buffered_error_log_buffer_size.result @@ -0,0 +1,86 @@ +connection node_2; +connection node_1; +connection node_2; +SELECT @@global.wsrep_buffered_error_log_filename; +@@global.wsrep_buffered_error_log_filename +galera_buffered_error_log.err +SELECT @@global.wsrep_buffered_error_log_buffer_size; +@@global.wsrep_buffered_error_log_buffer_size +102400 +SELECT @@global.wsrep_buffered_error_log_file_size; +@@global.wsrep_buffered_error_log_file_size +4194304 +CREATE TABLE t1(a int not null primary key) engine=innodb; +INSERT INTO t1 SELECT * FROM seq_1_to_1000; +# Change buffer size larger +SET GLOBAL wsrep_buffered_error_log_buffer_size = 202400; +SHOW WARNINGS; +Level Code Message +Testing that old log contains something logged +FOUND 1 /WSREP: Loading provider/ in galera_buffered_error_log.err +SELECT @@global.wsrep_buffered_error_log_filename; +@@global.wsrep_buffered_error_log_filename +galera_buffered_error_log.err +SELECT @@global.wsrep_buffered_error_log_buffer_size; +@@global.wsrep_buffered_error_log_buffer_size +202400 +SELECT @@global.wsrep_buffered_error_log_file_size; +@@global.wsrep_buffered_error_log_file_size +4194304 +SELECT COUNT(*) AS EXPECT_1000 FROM t1; +EXPECT_1000 +1000 +DROP TABLE t1; +# Change buffer size smaller +SET GLOBAL wsrep_buffered_error_log_buffer_size = 102400; +SHOW WARNINGS; +Level Code Message +Testing that log contains something logged +FOUND 4 /DROP TABLE/ in galera_buffered_error_log.err +SELECT @@global.wsrep_buffered_error_log_filename; +@@global.wsrep_buffered_error_log_filename +galera_buffered_error_log.err +SELECT @@global.wsrep_buffered_error_log_buffer_size; +@@global.wsrep_buffered_error_log_buffer_size +102400 +SELECT @@global.wsrep_buffered_error_log_file_size; +@@global.wsrep_buffered_error_log_file_size +4194304 +# Try to change buffer size to unsupported size i.e too large +SET GLOBAL wsrep_buffered_error_log_buffer_size = 4194400; +ERROR 42000: Variable 'wsrep_buffered_error_log_buffer_size' can't be set to the value of '4194400' +SHOW WARNINGS; +Level Code Message +Warning 1231 Parameter 'wsrep_buffered_error_log_size' should be at range 102400-419430 because wsrep_buffered_error_log_file_size=4194304 +Error 1231 Variable 'wsrep_buffered_error_log_buffer_size' can't be set to the value of '4194400' +SELECT @@global.wsrep_buffered_error_log_filename; +@@global.wsrep_buffered_error_log_filename +galera_buffered_error_log.err +SELECT @@global.wsrep_buffered_error_log_buffer_size; +@@global.wsrep_buffered_error_log_buffer_size +102400 +SELECT @@global.wsrep_buffered_error_log_file_size; +@@global.wsrep_buffered_error_log_file_size +4194304 +# Try to change buffer size to unsupported size i.e too small +SET GLOBAL wsrep_buffered_error_log_buffer_size = 8196; +ERROR 42000: Variable 'wsrep_buffered_error_log_buffer_size' can't be set to the value of '8196' +SHOW WARNINGS; +Level Code Message +Warning 1231 Parameter 'wsrep_buffered_error_log_size' should be at range 102400-419430 because wsrep_buffered_error_log_file_size=4194304 +Error 1231 Variable 'wsrep_buffered_error_log_buffer_size' can't be set to the value of '8196' +SELECT @@global.wsrep_buffered_error_log_filename; +@@global.wsrep_buffered_error_log_filename +galera_buffered_error_log.err +SELECT @@global.wsrep_buffered_error_log_buffer_size; +@@global.wsrep_buffered_error_log_buffer_size +102400 +SELECT @@global.wsrep_buffered_error_log_file_size; +@@global.wsrep_buffered_error_log_file_size +4194304 +# Reset buffer size to original size +SET GLOBAL wsrep_buffered_error_log_buffer_size = 102400; +SHOW WARNINGS; +Level Code Message +Warning 1231 Parameter 'wsrep_buffered_error_log_size' should be at range 102400-419430 because wsrep_buffered_error_log_file_size=4194304 +Error 1231 Variable 'wsrep_buffered_error_log_buffer_size' can't be set to the value of '8196' diff --git a/mysql-test/suite/galera/r/galera_var_buffered_error_log_file_size.result b/mysql-test/suite/galera/r/galera_var_buffered_error_log_file_size.result new file mode 100644 index 0000000000000..cacac77cad304 --- /dev/null +++ b/mysql-test/suite/galera/r/galera_var_buffered_error_log_file_size.result @@ -0,0 +1,56 @@ +connection node_2; +connection node_1; +connection node_2; +SELECT @@global.wsrep_buffered_error_log_filename; +@@global.wsrep_buffered_error_log_filename +galera_buffered_error_log.err +SELECT @@global.wsrep_buffered_error_log_buffer_size; +@@global.wsrep_buffered_error_log_buffer_size +10240 +SELECT @@global.wsrep_buffered_error_log_file_size; +@@global.wsrep_buffered_error_log_file_size +204800 +CREATE TABLE t1(a int not null primary key auto_increment, count int) engine=innodb; +# Change file size larger +SET GLOBAL wsrep_buffered_error_log_file_size=262144; +# List error log files - should have 3 files +galera_buffered_error_log.err +galera_buffered_error_log.err.1 +galera_buffered_error_log.err.2 +SELECT @@global.wsrep_buffered_error_log_filename; +@@global.wsrep_buffered_error_log_filename +galera_buffered_error_log.err +SELECT @@global.wsrep_buffered_error_log_buffer_size; +@@global.wsrep_buffered_error_log_buffer_size +10240 +SELECT @@global.wsrep_buffered_error_log_file_size; +@@global.wsrep_buffered_error_log_file_size +262144 +SELECT COUNT(*) AS EXPECT_2000 FROM t1; +EXPECT_2000 +2000 +DROP TABLE t1; +# Change file size smaller +SET GLOBAL wsrep_buffered_error_log_file_size=131072; +# List error log files - should be 3 files +galera_buffered_error_log.err +galera_buffered_error_log.err.1 +galera_buffered_error_log.err.2 +SELECT @@global.wsrep_buffered_error_log_filename; +@@global.wsrep_buffered_error_log_filename +galera_buffered_error_log.err +SELECT @@global.wsrep_buffered_error_log_buffer_size; +@@global.wsrep_buffered_error_log_buffer_size +10240 +SELECT @@global.wsrep_buffered_error_log_file_size; +@@global.wsrep_buffered_error_log_file_size +131072 +# Try to change buffer size to unsupported size i.e too small +SET GLOBAL wsrep_buffered_error_log_file_size=1024; +ERROR 42000: Variable 'wsrep_buffered_error_log_file_size' can't be set to the value of '1024' +# Reset file size to original size +SET GLOBAL wsrep_buffered_error_log_file_size=204800; +# List error log files - should be 3 files +galera_buffered_error_log.err +galera_buffered_error_log.err.1 +galera_buffered_error_log.err.2 diff --git a/mysql-test/suite/galera/r/galera_var_buffered_error_log_filename.result b/mysql-test/suite/galera/r/galera_var_buffered_error_log_filename.result new file mode 100644 index 0000000000000..913465fdf6c12 --- /dev/null +++ b/mysql-test/suite/galera/r/galera_var_buffered_error_log_filename.result @@ -0,0 +1,33 @@ +connection node_2; +connection node_1; +connection node_2; +SELECT @@global.wsrep_buffered_error_log_filename; +@@global.wsrep_buffered_error_log_filename +galera_buffered_error_log.err +SELECT @@global.wsrep_buffered_error_log_buffer_size; +@@global.wsrep_buffered_error_log_buffer_size +102400 +SELECT @@global.wsrep_buffered_error_log_file_size; +@@global.wsrep_buffered_error_log_file_size +1048576 +CREATE TABLE t1(a int not null primary key) engine=innodb; +INSERT INTO t1 SELECT * FROM seq_1_to_1000; +# Change file name +Testing that old log contains something logged +FOUND 1 /WSREP: Loading provider/ in galera_buffered_error_log.err +SELECT @@global.wsrep_buffered_error_log_filename; +@@global.wsrep_buffered_error_log_filename +galera_buffered_error_log2.err +SELECT @@global.wsrep_buffered_error_log_buffer_size; +@@global.wsrep_buffered_error_log_buffer_size +102400 +SELECT @@global.wsrep_buffered_error_log_file_size; +@@global.wsrep_buffered_error_log_file_size +1048576 +SELECT COUNT(*) AS EXPECT_1000 FROM t1; +EXPECT_1000 +1000 +DROP TABLE t1; +# Change file name back to original +Testing that second log contains something logged +FOUND 4 /DROP TABLE/ in galera_buffered_error_log2.err diff --git a/mysql-test/suite/galera/r/galera_var_buffered_error_log_rotations.result b/mysql-test/suite/galera/r/galera_var_buffered_error_log_rotations.result new file mode 100644 index 0000000000000..edf8167149c9c --- /dev/null +++ b/mysql-test/suite/galera/r/galera_var_buffered_error_log_rotations.result @@ -0,0 +1,69 @@ +connection node_2; +connection node_1; +connection node_2; +SELECT @@global.wsrep_buffered_error_log_filename; +@@global.wsrep_buffered_error_log_filename +galera_buffered_error_log.err +SELECT @@global.wsrep_buffered_error_log_buffer_size; +@@global.wsrep_buffered_error_log_buffer_size +10240 +SELECT @@global.wsrep_buffered_error_log_file_size; +@@global.wsrep_buffered_error_log_file_size +102400 +SELECT @@global.wsrep_buffered_error_log_rotations; +@@global.wsrep_buffered_error_log_rotations +5 +CREATE TABLE t1(a int not null primary key auto_increment, count int) engine=innodb; +# List error log files - max file number .5 +galera_buffered_error_log.err +galera_buffered_error_log.err.1 +galera_buffered_error_log.err.2 +galera_buffered_error_log.err.3 +galera_buffered_error_log.err.4 +galera_buffered_error_log.err.5 +# Change rotations larger +SET GLOBAL wsrep_buffered_error_log_rotations=6; +# List error log files - max file number .6 +galera_buffered_error_log.err +galera_buffered_error_log.err.1 +galera_buffered_error_log.err.2 +galera_buffered_error_log.err.3 +galera_buffered_error_log.err.4 +galera_buffered_error_log.err.5 +galera_buffered_error_log.err.6 +SELECT @@global.wsrep_buffered_error_log_filename; +@@global.wsrep_buffered_error_log_filename +galera_buffered_error_log.err +SELECT @@global.wsrep_buffered_error_log_buffer_size; +@@global.wsrep_buffered_error_log_buffer_size +10240 +SELECT @@global.wsrep_buffered_error_log_file_size; +@@global.wsrep_buffered_error_log_file_size +102400 +SELECT @@global.wsrep_buffered_error_log_rotations; +@@global.wsrep_buffered_error_log_rotations +6 +# Change rotations smaller and use different name +SET GLOBAL wsrep_buffered_error_log_rotations=2; +SELECT COUNT(*) AS EXPECT_2000 FROM t1; +EXPECT_2000 +3000 +DROP TABLE t1; +# List error log files - max file number .2 +galera_buffered_error_log2.err +galera_buffered_error_log2.err.1 +galera_buffered_error_log2.err.2 +SELECT @@global.wsrep_buffered_error_log_filename; +@@global.wsrep_buffered_error_log_filename +galera_buffered_error_log2.err +SELECT @@global.wsrep_buffered_error_log_buffer_size; +@@global.wsrep_buffered_error_log_buffer_size +10240 +SELECT @@global.wsrep_buffered_error_log_file_size; +@@global.wsrep_buffered_error_log_file_size +102400 +SELECT @@global.wsrep_buffered_error_log_rotations; +@@global.wsrep_buffered_error_log_rotations +2 +# Reset +SET @@global.wsrep_buffered_error_log_rotations=5; diff --git a/mysql-test/suite/galera/t/galera_var_buffered_error_log_buffer_size.cnf b/mysql-test/suite/galera/t/galera_var_buffered_error_log_buffer_size.cnf new file mode 100644 index 0000000000000..edb6815695cd5 --- /dev/null +++ b/mysql-test/suite/galera/t/galera_var_buffered_error_log_buffer_size.cnf @@ -0,0 +1,9 @@ +!include ../galera_2nodes.cnf + +[mysqld] +wsrep-debug=1 + +[mysqld.2] +wsrep_buffered_error_log_buffer_size=100K +wsrep_buffered_error_log_file_size=4M +wsrep_buffered_error_log_filename="galera_buffered_error_log.err" diff --git a/mysql-test/suite/galera/t/galera_var_buffered_error_log_buffer_size.test b/mysql-test/suite/galera/t/galera_var_buffered_error_log_buffer_size.test new file mode 100644 index 0000000000000..409b2588189f6 --- /dev/null +++ b/mysql-test/suite/galera/t/galera_var_buffered_error_log_buffer_size.test @@ -0,0 +1,69 @@ +--source include/galera_cluster.inc +--source include/have_sequence.inc + +--connection node_2 +--replace_regex /.*galera_buffered_error_log.err.*/galera_buffered_error_log.err/ +SELECT @@global.wsrep_buffered_error_log_filename; +SELECT @@global.wsrep_buffered_error_log_buffer_size; +SELECT @@global.wsrep_buffered_error_log_file_size; + +CREATE TABLE t1(a int not null primary key) engine=innodb; +INSERT INTO t1 SELECT * FROM seq_1_to_1000; + +--echo # Change buffer size larger +SET GLOBAL wsrep_buffered_error_log_buffer_size = 202400; +SHOW WARNINGS; + +--echo Testing that old log contains something logged +--let SEARCH_FILE= $MYSQLTEST_VARDIR/mysqld.2/data/galera_buffered_error_log.err +--let ABORT_ON = NOT_FOUND +--let SEARCH_PATTERN=WSREP: Loading provider +--source include/search_pattern_in_file.inc + +--replace_regex /.*galera_buffered_error_log.err.*/galera_buffered_error_log.err/ +SELECT @@global.wsrep_buffered_error_log_filename; +SELECT @@global.wsrep_buffered_error_log_buffer_size; +SELECT @@global.wsrep_buffered_error_log_file_size; + +SELECT COUNT(*) AS EXPECT_1000 FROM t1; +DROP TABLE t1; + +--echo # Change buffer size smaller +SET GLOBAL wsrep_buffered_error_log_buffer_size = 102400; +SHOW WARNINGS; + +--echo Testing that log contains something logged +--let SEARCH_FILE= $MYSQLTEST_VARDIR/mysqld.2/data/galera_buffered_error_log.err +--let ABORT_ON = NOT_FOUND +--let SEARCH_PATTERN=DROP TABLE +--source include/search_pattern_in_file.inc + +--replace_regex /.*galera_buffered_error_log.err.*/galera_buffered_error_log.err/ +SELECT @@global.wsrep_buffered_error_log_filename; +SELECT @@global.wsrep_buffered_error_log_buffer_size; +SELECT @@global.wsrep_buffered_error_log_file_size; + +--echo # Try to change buffer size to unsupported size i.e too large +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL wsrep_buffered_error_log_buffer_size = 4194400; +SHOW WARNINGS; + +--replace_regex /.*galera_buffered_error_log.err.*/galera_buffered_error_log.err/ +SELECT @@global.wsrep_buffered_error_log_filename; +SELECT @@global.wsrep_buffered_error_log_buffer_size; +SELECT @@global.wsrep_buffered_error_log_file_size; + +--echo # Try to change buffer size to unsupported size i.e too small +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL wsrep_buffered_error_log_buffer_size = 8196; +SHOW WARNINGS; + +--replace_regex /.*galera_buffered_error_log.err.*/galera_buffered_error_log.err/ +SELECT @@global.wsrep_buffered_error_log_filename; +SELECT @@global.wsrep_buffered_error_log_buffer_size; +SELECT @@global.wsrep_buffered_error_log_file_size; + +--echo # Reset buffer size to original size +SET GLOBAL wsrep_buffered_error_log_buffer_size = 102400; +SHOW WARNINGS; + diff --git a/mysql-test/suite/galera/t/galera_var_buffered_error_log_file_size.cnf b/mysql-test/suite/galera/t/galera_var_buffered_error_log_file_size.cnf new file mode 100644 index 0000000000000..93d029970fb78 --- /dev/null +++ b/mysql-test/suite/galera/t/galera_var_buffered_error_log_file_size.cnf @@ -0,0 +1,13 @@ +!include ../galera_2nodes.cnf + +[mysqld] +wsrep-debug=1 + +[mysqld.2] +wsrep_buffered_error_log_buffer_size=10K +wsrep_buffered_error_log_file_size=200K +wsrep_buffered_error_log_filename="galera_buffered_error_log.err" +wsrep_buffered_error_log_rotations=2 +loose-galera-log_file-size + + diff --git a/mysql-test/suite/galera/t/galera_var_buffered_error_log_file_size.test b/mysql-test/suite/galera/t/galera_var_buffered_error_log_file_size.test new file mode 100644 index 0000000000000..4a7ae5e3a1601 --- /dev/null +++ b/mysql-test/suite/galera/t/galera_var_buffered_error_log_file_size.test @@ -0,0 +1,69 @@ +--source include/galera_cluster.inc +--source include/have_sequence.inc +--source include/force_restart.inc + +--connection node_2 +--replace_regex /.*galera_buffered_error_log.err.*/galera_buffered_error_log.err/ +SELECT @@global.wsrep_buffered_error_log_filename; +SELECT @@global.wsrep_buffered_error_log_buffer_size; +SELECT @@global.wsrep_buffered_error_log_file_size; + +CREATE TABLE t1(a int not null primary key auto_increment, count int) engine=innodb; + +--let $inserts=1000 +--let $count=0 +--disable_query_log +while($count < $inserts) +{ + --eval insert into t1 values (NULL,$count) + --eval update t1 set count = $count+100 where count = $count + --inc $count +} +--enable_query_log + +--echo # Change file size larger +SET GLOBAL wsrep_buffered_error_log_file_size=262144; + +--echo # List error log files - should have 3 files +--list_files $MYSQLTEST_VARDIR/mysqld.2/data galera_buffered_error_log* + +--replace_regex /.*galera_buffered_error_log.err.*/galera_buffered_error_log.err/ +SELECT @@global.wsrep_buffered_error_log_filename; +SELECT @@global.wsrep_buffered_error_log_buffer_size; +SELECT @@global.wsrep_buffered_error_log_file_size; + +--let $inserts=1000 +--let $count=0 +--disable_query_log +while($count < $inserts) +{ + --eval insert into t1 values (NULL,$count) + --eval update t1 set count = $count+100 where count = $count + --inc $count +} +--enable_query_log + +SELECT COUNT(*) AS EXPECT_2000 FROM t1; +DROP TABLE t1; + +--echo # Change file size smaller +SET GLOBAL wsrep_buffered_error_log_file_size=131072; + +--echo # List error log files - should be 3 files +--list_files $MYSQLTEST_VARDIR/mysqld.2/data galera_buffered_error_log* + +--replace_regex /.*galera_buffered_error_log.err.*/galera_buffered_error_log.err/ +SELECT @@global.wsrep_buffered_error_log_filename; +SELECT @@global.wsrep_buffered_error_log_buffer_size; +SELECT @@global.wsrep_buffered_error_log_file_size; + +--echo # Try to change buffer size to unsupported size i.e too small +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL wsrep_buffered_error_log_file_size=1024; + +--echo # Reset file size to original size +SET GLOBAL wsrep_buffered_error_log_file_size=204800; + +--echo # List error log files - should be 3 files +--list_files $MYSQLTEST_VARDIR/mysqld.2/data galera_buffered_error_log* + diff --git a/mysql-test/suite/galera/t/galera_var_buffered_error_log_filename.cnf b/mysql-test/suite/galera/t/galera_var_buffered_error_log_filename.cnf new file mode 100644 index 0000000000000..8171a3fc4f258 --- /dev/null +++ b/mysql-test/suite/galera/t/galera_var_buffered_error_log_filename.cnf @@ -0,0 +1,9 @@ +!include ../galera_2nodes.cnf + +[mysqld] +wsrep-debug=1 + +[mysqld.2] +wsrep_buffered_error_log_buffer_size=100Kb +wsrep_buffered_error_log_file_size=1M +wsrep_buffered_error_log_filename="galera_buffered_error_log.err" diff --git a/mysql-test/suite/galera/t/galera_var_buffered_error_log_filename.test b/mysql-test/suite/galera/t/galera_var_buffered_error_log_filename.test new file mode 100644 index 0000000000000..b9884d2394424 --- /dev/null +++ b/mysql-test/suite/galera/t/galera_var_buffered_error_log_filename.test @@ -0,0 +1,41 @@ +--source include/galera_cluster.inc +--source include/have_sequence.inc + +--connection node_2 +--replace_regex /.*galera_buffered_error_log.err.*/galera_buffered_error_log.err/ +SELECT @@global.wsrep_buffered_error_log_filename; +SELECT @@global.wsrep_buffered_error_log_buffer_size; +SELECT @@global.wsrep_buffered_error_log_file_size; + +CREATE TABLE t1(a int not null primary key) engine=innodb; +INSERT INTO t1 SELECT * FROM seq_1_to_1000; + +--echo # Change file name +--disable_query_log +--eval SET GLOBAL wsrep_buffered_error_log_filename="galera_buffered_error_log2.err" +--enable_query_log + +--echo Testing that old log contains something logged +--let SEARCH_FILE= $MYSQLTEST_VARDIR/mysqld.2/data/galera_buffered_error_log.err +--let ABORT_ON = NOT_FOUND +--let SEARCH_PATTERN=WSREP: Loading provider +--source include/search_pattern_in_file.inc + +--replace_regex /.*galera_buffered_error_log2.err.*/galera_buffered_error_log2.err/ +SELECT @@global.wsrep_buffered_error_log_filename; +SELECT @@global.wsrep_buffered_error_log_buffer_size; +SELECT @@global.wsrep_buffered_error_log_file_size; + +SELECT COUNT(*) AS EXPECT_1000 FROM t1; +DROP TABLE t1; + +--echo # Change file name back to original +--disable_query_log +--eval SET GLOBAL wsrep_buffered_error_log_filename="galera_buffered_error_log.err" +--enable_query_log + +--echo Testing that second log contains something logged +--let SEARCH_FILE= $MYSQLTEST_VARDIR/mysqld.2/data/galera_buffered_error_log2.err +--let ABORT_ON = NOT_FOUND +--let SEARCH_PATTERN=DROP TABLE +--source include/search_pattern_in_file.inc diff --git a/mysql-test/suite/galera/t/galera_var_buffered_error_log_rotations.cnf b/mysql-test/suite/galera/t/galera_var_buffered_error_log_rotations.cnf new file mode 100644 index 0000000000000..9e22940f2789f --- /dev/null +++ b/mysql-test/suite/galera/t/galera_var_buffered_error_log_rotations.cnf @@ -0,0 +1,13 @@ +!include ../galera_2nodes.cnf + +[mysqld] +wsrep-debug=1 + +[mysqld.2] +wsrep_buffered_error_log_buffer_size=10K +wsrep_buffered_error_log_file_size=100K +wsrep_buffered_error_log_filename="galera_buffered_error_log.err" +wsrep_buffered_error_log_rotations=5 +loose-galera-log_rotations + + diff --git a/mysql-test/suite/galera/t/galera_var_buffered_error_log_rotations.test b/mysql-test/suite/galera/t/galera_var_buffered_error_log_rotations.test new file mode 100644 index 0000000000000..c50773f009a02 --- /dev/null +++ b/mysql-test/suite/galera/t/galera_var_buffered_error_log_rotations.test @@ -0,0 +1,86 @@ +--source include/galera_cluster.inc +--source include/have_sequence.inc +--source include/force_restart.inc + +--connection node_2 +--replace_regex /.*galera_buffered_error_log.err.*/galera_buffered_error_log.err/ +SELECT @@global.wsrep_buffered_error_log_filename; +SELECT @@global.wsrep_buffered_error_log_buffer_size; +SELECT @@global.wsrep_buffered_error_log_file_size; +SELECT @@global.wsrep_buffered_error_log_rotations; + +CREATE TABLE t1(a int not null primary key auto_increment, count int) engine=innodb; + +--let $inserts=1000 +--let $count=0 +--disable_query_log +while($count < $inserts) +{ + --eval insert into t1 values (NULL,$count) + --eval update t1 set count = $count+100 where count = $count + --inc $count +} +--enable_query_log + +--echo # List error log files - max file number .5 +--list_files $MYSQLTEST_VARDIR/mysqld.2/data galera_buffered_error_log* + +--echo # Change rotations larger +SET GLOBAL wsrep_buffered_error_log_rotations=6; + +--let $inserts=1000 +--let $count=0 +--disable_query_log +while($count < $inserts) +{ + --eval insert into t1 values (NULL,$count) + --eval update t1 set count = $count+100 where count = $count + --inc $count +} +--enable_query_log + +--echo # List error log files - max file number .6 +--list_files $MYSQLTEST_VARDIR/mysqld.2/data galera_buffered_error_log* + +--replace_regex /.*galera_buffered_error_log.err.*/galera_buffered_error_log.err/ +SELECT @@global.wsrep_buffered_error_log_filename; +SELECT @@global.wsrep_buffered_error_log_buffer_size; +SELECT @@global.wsrep_buffered_error_log_file_size; +SELECT @@global.wsrep_buffered_error_log_rotations; + +--echo # Change rotations smaller and use different name +--disable_query_log +--eval SET GLOBAL wsrep_buffered_error_log_filename="galera_buffered_error_log2.err" +--enable_query_log +SET GLOBAL wsrep_buffered_error_log_rotations=2; + +--let $inserts=1000 +--let $count=0 +--disable_query_log +while($count < $inserts) +{ + --eval insert into t1 values (NULL,$count) + --eval update t1 set count = $count+100 where count = $count + --inc $count +} +--enable_query_log + +SELECT COUNT(*) AS EXPECT_2000 FROM t1; +DROP TABLE t1; + +--echo # List error log files - max file number .2 +--list_files $MYSQLTEST_VARDIR/mysqld.2/data galera_buffered_error_log2* + +--replace_regex /.*galera_buffered_error_log2.err.*/galera_buffered_error_log2.err/ +SELECT @@global.wsrep_buffered_error_log_filename; +SELECT @@global.wsrep_buffered_error_log_buffer_size; +SELECT @@global.wsrep_buffered_error_log_file_size; +SELECT @@global.wsrep_buffered_error_log_rotations; + +--echo # Reset +--disable_query_log +--eval SET GLOBAL wsrep_buffered_error_log_filename="galera_buffered_error_log.err" +--enable_query_log +SET @@global.wsrep_buffered_error_log_rotations=5; + + diff --git a/mysql-test/suite/sys_vars/r/sysvars_wsrep.result b/mysql-test/suite/sys_vars/r/sysvars_wsrep.result index 9310119071c9a..4e49ccc56d01e 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_wsrep.result +++ b/mysql-test/suite/sys_vars/r/sysvars_wsrep.result @@ -31,6 +31,66 @@ ENUM_VALUE_LIST OFF,ON READ_ONLY NO COMMAND_LINE_ARGUMENT OPTIONAL GLOBAL_VALUE_PATH NULL +VARIABLE_NAME WSREP_BUFFERED_ERROR_LOG_BUFFER_SIZE +SESSION_VALUE NULL +GLOBAL_VALUE 0 +GLOBAL_VALUE_ORIGIN COMPILE-TIME +DEFAULT_VALUE 0 +VARIABLE_SCOPE GLOBAL +VARIABLE_TYPE BIGINT UNSIGNED +VARIABLE_COMMENT Size of the buffered error log buffer +NUMERIC_MIN_VALUE 0 +NUMERIC_MAX_VALUE 18446744073709551615 +NUMERIC_BLOCK_SIZE 1 +ENUM_VALUE_LIST NULL +READ_ONLY NO +COMMAND_LINE_ARGUMENT REQUIRED +GLOBAL_VALUE_PATH NULL +VARIABLE_NAME WSREP_BUFFERED_ERROR_LOG_FILENAME +SESSION_VALUE NULL +GLOBAL_VALUE +GLOBAL_VALUE_ORIGIN COMPILE-TIME +DEFAULT_VALUE +VARIABLE_SCOPE GLOBAL +VARIABLE_TYPE VARCHAR +VARIABLE_COMMENT Filename of the buffered error log +NUMERIC_MIN_VALUE NULL +NUMERIC_MAX_VALUE NULL +NUMERIC_BLOCK_SIZE NULL +ENUM_VALUE_LIST NULL +READ_ONLY NO +COMMAND_LINE_ARGUMENT REQUIRED +GLOBAL_VALUE_PATH NULL +VARIABLE_NAME WSREP_BUFFERED_ERROR_LOG_FILE_SIZE +SESSION_VALUE NULL +GLOBAL_VALUE 0 +GLOBAL_VALUE_ORIGIN COMPILE-TIME +DEFAULT_VALUE 0 +VARIABLE_SCOPE GLOBAL +VARIABLE_TYPE BIGINT UNSIGNED +VARIABLE_COMMENT Max size of the buffered error log file +NUMERIC_MIN_VALUE 0 +NUMERIC_MAX_VALUE 18446744073709551615 +NUMERIC_BLOCK_SIZE 1 +ENUM_VALUE_LIST NULL +READ_ONLY NO +COMMAND_LINE_ARGUMENT REQUIRED +GLOBAL_VALUE_PATH NULL +VARIABLE_NAME WSREP_BUFFERED_ERROR_LOG_ROTATIONS +SESSION_VALUE NULL +GLOBAL_VALUE 10 +GLOBAL_VALUE_ORIGIN COMPILE-TIME +DEFAULT_VALUE 10 +VARIABLE_SCOPE GLOBAL +VARIABLE_TYPE INT UNSIGNED +VARIABLE_COMMENT Number of log rotations before log is removed +NUMERIC_MIN_VALUE 0 +NUMERIC_MAX_VALUE 999999 +NUMERIC_BLOCK_SIZE 1 +ENUM_VALUE_LIST NULL +READ_ONLY NO +COMMAND_LINE_ARGUMENT REQUIRED +GLOBAL_VALUE_PATH NULL VARIABLE_NAME WSREP_CERTIFICATION_RULES SESSION_VALUE NULL GLOBAL_VALUE strict diff --git a/mysql-test/suite/sys_vars/r/wsrep_buffered_error_log_buffer_size_basic.result b/mysql-test/suite/sys_vars/r/wsrep_buffered_error_log_buffer_size_basic.result new file mode 100644 index 0000000000000..5b11b3c869e06 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/wsrep_buffered_error_log_buffer_size_basic.result @@ -0,0 +1,39 @@ +SET @start_value = @@global.wsrep_buffered_error_log_buffer_size; +SELECT @start_value; +@start_value +0 +SET @@global.wsrep_buffered_error_log_buffer_size = 102400; +ERROR 42000: Variable 'wsrep_buffered_error_log_buffer_size' can't be set to the value of '102400' +SHOW WARNINGS; +Level Code Message +Warning 1231 Cannot set 'wsrep_buffered_error_log_size' to a value other than 0 because wsrep is switched off. +Error 1231 Variable 'wsrep_buffered_error_log_buffer_size' can't be set to the value of '102400' +SELECT @@global.wsrep_buffered_error_log_buffer_size; +@@global.wsrep_buffered_error_log_buffer_size +0 +SET @@global.wsrep_buffered_error_log_buffer_size = 0; +SELECT @@global.wsrep_buffered_error_log_buffer_size; +@@global.wsrep_buffered_error_log_buffer_size +0 +SET @@session.wsrep_buffered_error_log_buffer_size = 0; +ERROR HY000: Variable 'wsrep_buffered_error_log_buffer_size' is a GLOBAL variable and should be set with SET GLOBAL +SELECT @@session.wsrep_buffered_error_log_buffer_size; +ERROR HY000: Variable 'wsrep_buffered_error_log_buffer_size' is a GLOBAL variable +SET @@global.wsrep_buffered_error_log_buffer_size = -10; +Warnings: +Warning 1292 Truncated incorrect wsrep_buffered_error_log_buff... value: '-10' +SELECT @@global.wsrep_buffered_error_log_buffer_size; +@@global.wsrep_buffered_error_log_buffer_size +0 +SET @@global.wsrep_buffered_error_log_buffer_size = "/foo/bar.txt"; +ERROR 42000: Incorrect argument type to variable 'wsrep_buffered_error_log_buffer_size' +SHOW WARNINGS; +Level Code Message +Error 1232 Incorrect argument type to variable 'wsrep_buffered_error_log_buffer_size' +SELECT @@global.wsrep_buffered_error_log_buffer_size; +@@global.wsrep_buffered_error_log_buffer_size +0 +SET @@global.wsrep_buffered_error_log_buffer_size = @start_value; +SELECT @@global.wsrep_buffered_error_log_buffer_size; +@@global.wsrep_buffered_error_log_buffer_size +0 diff --git a/mysql-test/suite/sys_vars/r/wsrep_buffered_error_log_file_size_basic.result b/mysql-test/suite/sys_vars/r/wsrep_buffered_error_log_file_size_basic.result new file mode 100644 index 0000000000000..db5b6132c1d83 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/wsrep_buffered_error_log_file_size_basic.result @@ -0,0 +1,39 @@ +SET @start_value = @@global.wsrep_buffered_error_log_file_size; +SELECT @start_value; +@start_value +0 +SET @@global.wsrep_buffered_error_log_file_size = 420; +ERROR 42000: Variable 'wsrep_buffered_error_log_file_size' can't be set to the value of '420' +SHOW WARNINGS; +Level Code Message +Warning 1231 Cannot set 'wsrep_buffered_error_log_file_size' to a value other than 0 because wsrep is switched off. +Error 1231 Variable 'wsrep_buffered_error_log_file_size' can't be set to the value of '420' +SELECT @@global.wsrep_buffered_error_log_file_size; +@@global.wsrep_buffered_error_log_file_size +0 +SET @@global.wsrep_buffered_error_log_file_size = 0; +SELECT @@global.wsrep_buffered_error_log_file_size; +@@global.wsrep_buffered_error_log_file_size +0 +SET @@session.wsrep_buffered_error_log_file_size = 0; +ERROR HY000: Variable 'wsrep_buffered_error_log_file_size' is a GLOBAL variable and should be set with SET GLOBAL +SELECT @@session.wsrep_buffered_error_log_file_size; +ERROR HY000: Variable 'wsrep_buffered_error_log_file_size' is a GLOBAL variable +SET @@global.wsrep_buffered_error_log_file_size = -10; +Warnings: +Warning 1292 Truncated incorrect wsrep_buffered_error_log_file... value: '-10' +SELECT @@global.wsrep_buffered_error_log_file_size; +@@global.wsrep_buffered_error_log_file_size +0 +SET @@global.wsrep_buffered_error_log_file_size = "/foo/bar.txt"; +ERROR 42000: Incorrect argument type to variable 'wsrep_buffered_error_log_file_size' +SHOW WARNINGS; +Level Code Message +Error 1232 Incorrect argument type to variable 'wsrep_buffered_error_log_file_size' +SELECT @@global.wsrep_buffered_error_log_file_size; +@@global.wsrep_buffered_error_log_file_size +0 +SET @@global.wsrep_buffered_error_log_file_size = @start_value; +SELECT @@global.wsrep_buffered_error_log_file_size; +@@global.wsrep_buffered_error_log_file_size +0 diff --git a/mysql-test/suite/sys_vars/r/wsrep_buffered_error_log_filename_basic.result b/mysql-test/suite/sys_vars/r/wsrep_buffered_error_log_filename_basic.result new file mode 100644 index 0000000000000..2eca2753434e8 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/wsrep_buffered_error_log_filename_basic.result @@ -0,0 +1,30 @@ +SET @start_value = @@global.wsrep_buffered_error_log_filename; +SELECT @start_value; +@start_value +NULL +SET @size_start_value = @@global.wsrep_buffered_error_log_buffer_size; +SELECT @size_start_value; +@size_start_value +0 +SET @@global.wsrep_buffered_error_log_filename = "/tmp/bar.txt"; +ERROR 42000: Variable 'wsrep_buffered_error_log_filename' can't be set to the value of '/tmp/bar.txt' +SHOW WARNINGS; +Level Code Message +Warning 1231 Cannot set 'wsrep_buffered_error_log_filename' because wsrep is switched off. +Error 1231 Variable 'wsrep_buffered_error_log_filename' can't be set to the value of '/tmp/bar.txt' +SELECT @@global.wsrep_buffered_error_log_filename; +@@global.wsrep_buffered_error_log_filename +NULL +SET @@global.wsrep_buffered_error_log_filename = ''; +SELECT @@global.wsrep_buffered_error_log_filename; +@@global.wsrep_buffered_error_log_filename + +SET @@session.wsrep_buffered_error_log_filename = OFF; +ERROR HY000: Variable 'wsrep_buffered_error_log_filename' is a GLOBAL variable and should be set with SET GLOBAL +SELECT @@session.wsrep_buffered_error_log_filename; +ERROR HY000: Variable 'wsrep_buffered_error_log_filename' is a GLOBAL variable +SET @@global.wsrep_buffered_error_log_filename = @start_value; +SET @@global.wsrep_buffered_error_log_buffer_size = @size_start_value; +SELECT @@global.wsrep_buffered_error_log_filename; +@@global.wsrep_buffered_error_log_filename +NULL diff --git a/mysql-test/suite/sys_vars/r/wsrep_buffered_error_log_rotations_basic.result b/mysql-test/suite/sys_vars/r/wsrep_buffered_error_log_rotations_basic.result new file mode 100644 index 0000000000000..5b11b3c869e06 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/wsrep_buffered_error_log_rotations_basic.result @@ -0,0 +1,39 @@ +SET @start_value = @@global.wsrep_buffered_error_log_buffer_size; +SELECT @start_value; +@start_value +0 +SET @@global.wsrep_buffered_error_log_buffer_size = 102400; +ERROR 42000: Variable 'wsrep_buffered_error_log_buffer_size' can't be set to the value of '102400' +SHOW WARNINGS; +Level Code Message +Warning 1231 Cannot set 'wsrep_buffered_error_log_size' to a value other than 0 because wsrep is switched off. +Error 1231 Variable 'wsrep_buffered_error_log_buffer_size' can't be set to the value of '102400' +SELECT @@global.wsrep_buffered_error_log_buffer_size; +@@global.wsrep_buffered_error_log_buffer_size +0 +SET @@global.wsrep_buffered_error_log_buffer_size = 0; +SELECT @@global.wsrep_buffered_error_log_buffer_size; +@@global.wsrep_buffered_error_log_buffer_size +0 +SET @@session.wsrep_buffered_error_log_buffer_size = 0; +ERROR HY000: Variable 'wsrep_buffered_error_log_buffer_size' is a GLOBAL variable and should be set with SET GLOBAL +SELECT @@session.wsrep_buffered_error_log_buffer_size; +ERROR HY000: Variable 'wsrep_buffered_error_log_buffer_size' is a GLOBAL variable +SET @@global.wsrep_buffered_error_log_buffer_size = -10; +Warnings: +Warning 1292 Truncated incorrect wsrep_buffered_error_log_buff... value: '-10' +SELECT @@global.wsrep_buffered_error_log_buffer_size; +@@global.wsrep_buffered_error_log_buffer_size +0 +SET @@global.wsrep_buffered_error_log_buffer_size = "/foo/bar.txt"; +ERROR 42000: Incorrect argument type to variable 'wsrep_buffered_error_log_buffer_size' +SHOW WARNINGS; +Level Code Message +Error 1232 Incorrect argument type to variable 'wsrep_buffered_error_log_buffer_size' +SELECT @@global.wsrep_buffered_error_log_buffer_size; +@@global.wsrep_buffered_error_log_buffer_size +0 +SET @@global.wsrep_buffered_error_log_buffer_size = @start_value; +SELECT @@global.wsrep_buffered_error_log_buffer_size; +@@global.wsrep_buffered_error_log_buffer_size +0 diff --git a/mysql-test/suite/sys_vars/t/wsrep_buffered_error_log_buffer_size_basic.test b/mysql-test/suite/sys_vars/t/wsrep_buffered_error_log_buffer_size_basic.test new file mode 100644 index 0000000000000..a2631b340c3aa --- /dev/null +++ b/mysql-test/suite/sys_vars/t/wsrep_buffered_error_log_buffer_size_basic.test @@ -0,0 +1,29 @@ +--source include/have_wsrep.inc + +SET @start_value = @@global.wsrep_buffered_error_log_buffer_size; +SELECT @start_value; + +--error ER_WRONG_VALUE_FOR_VAR +SET @@global.wsrep_buffered_error_log_buffer_size = 102400; +SHOW WARNINGS; +SELECT @@global.wsrep_buffered_error_log_buffer_size; +SET @@global.wsrep_buffered_error_log_buffer_size = 0; +SELECT @@global.wsrep_buffered_error_log_buffer_size; + +--error ER_GLOBAL_VARIABLE +SET @@session.wsrep_buffered_error_log_buffer_size = 0; +--Error ER_INCORRECT_GLOBAL_LOCAL_VAR +SELECT @@session.wsrep_buffered_error_log_buffer_size; + +# No error, truncation +SET @@global.wsrep_buffered_error_log_buffer_size = -10; +SELECT @@global.wsrep_buffered_error_log_buffer_size; + +--error ER_WRONG_TYPE_FOR_VAR +SET @@global.wsrep_buffered_error_log_buffer_size = "/foo/bar.txt"; +SHOW WARNINGS; +SELECT @@global.wsrep_buffered_error_log_buffer_size; + +SET @@global.wsrep_buffered_error_log_buffer_size = @start_value; +SELECT @@global.wsrep_buffered_error_log_buffer_size; + diff --git a/mysql-test/suite/sys_vars/t/wsrep_buffered_error_log_file_size_basic.test b/mysql-test/suite/sys_vars/t/wsrep_buffered_error_log_file_size_basic.test new file mode 100644 index 0000000000000..8f0d129506d95 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/wsrep_buffered_error_log_file_size_basic.test @@ -0,0 +1,29 @@ +--source include/have_wsrep.inc + +SET @start_value = @@global.wsrep_buffered_error_log_file_size; +SELECT @start_value; + +--error ER_WRONG_VALUE_FOR_VAR +SET @@global.wsrep_buffered_error_log_file_size = 420; +SHOW WARNINGS; +SELECT @@global.wsrep_buffered_error_log_file_size; +SET @@global.wsrep_buffered_error_log_file_size = 0; +SELECT @@global.wsrep_buffered_error_log_file_size; + +--error ER_GLOBAL_VARIABLE +SET @@session.wsrep_buffered_error_log_file_size = 0; +--Error ER_INCORRECT_GLOBAL_LOCAL_VAR +SELECT @@session.wsrep_buffered_error_log_file_size; + +# No error, truncation +SET @@global.wsrep_buffered_error_log_file_size = -10; +SELECT @@global.wsrep_buffered_error_log_file_size; + +--error ER_WRONG_TYPE_FOR_VAR +SET @@global.wsrep_buffered_error_log_file_size = "/foo/bar.txt"; +SHOW WARNINGS; +SELECT @@global.wsrep_buffered_error_log_file_size; + +SET @@global.wsrep_buffered_error_log_file_size = @start_value; +SELECT @@global.wsrep_buffered_error_log_file_size; + diff --git a/mysql-test/suite/sys_vars/t/wsrep_buffered_error_log_filename_basic.test b/mysql-test/suite/sys_vars/t/wsrep_buffered_error_log_filename_basic.test new file mode 100644 index 0000000000000..f15ce05156f39 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/wsrep_buffered_error_log_filename_basic.test @@ -0,0 +1,24 @@ +--source include/have_wsrep.inc + +SET @start_value = @@global.wsrep_buffered_error_log_filename; +SELECT @start_value; +SET @size_start_value = @@global.wsrep_buffered_error_log_buffer_size; +SELECT @size_start_value; + +--error ER_WRONG_VALUE_FOR_VAR +SET @@global.wsrep_buffered_error_log_filename = "/tmp/bar.txt"; +SHOW WARNINGS; +SELECT @@global.wsrep_buffered_error_log_filename; +SET @@global.wsrep_buffered_error_log_filename = ''; +SELECT @@global.wsrep_buffered_error_log_filename; + + +--Error ER_GLOBAL_VARIABLE +SET @@session.wsrep_buffered_error_log_filename = OFF; +--Error ER_INCORRECT_GLOBAL_LOCAL_VAR +SELECT @@session.wsrep_buffered_error_log_filename; + + +SET @@global.wsrep_buffered_error_log_filename = @start_value; +SET @@global.wsrep_buffered_error_log_buffer_size = @size_start_value; +SELECT @@global.wsrep_buffered_error_log_filename; diff --git a/mysql-test/suite/sys_vars/t/wsrep_buffered_error_log_rotations_basic.test b/mysql-test/suite/sys_vars/t/wsrep_buffered_error_log_rotations_basic.test new file mode 100644 index 0000000000000..a2631b340c3aa --- /dev/null +++ b/mysql-test/suite/sys_vars/t/wsrep_buffered_error_log_rotations_basic.test @@ -0,0 +1,29 @@ +--source include/have_wsrep.inc + +SET @start_value = @@global.wsrep_buffered_error_log_buffer_size; +SELECT @start_value; + +--error ER_WRONG_VALUE_FOR_VAR +SET @@global.wsrep_buffered_error_log_buffer_size = 102400; +SHOW WARNINGS; +SELECT @@global.wsrep_buffered_error_log_buffer_size; +SET @@global.wsrep_buffered_error_log_buffer_size = 0; +SELECT @@global.wsrep_buffered_error_log_buffer_size; + +--error ER_GLOBAL_VARIABLE +SET @@session.wsrep_buffered_error_log_buffer_size = 0; +--Error ER_INCORRECT_GLOBAL_LOCAL_VAR +SELECT @@session.wsrep_buffered_error_log_buffer_size; + +# No error, truncation +SET @@global.wsrep_buffered_error_log_buffer_size = -10; +SELECT @@global.wsrep_buffered_error_log_buffer_size; + +--error ER_WRONG_TYPE_FOR_VAR +SET @@global.wsrep_buffered_error_log_buffer_size = "/foo/bar.txt"; +SHOW WARNINGS; +SELECT @@global.wsrep_buffered_error_log_buffer_size; + +SET @@global.wsrep_buffered_error_log_buffer_size = @start_value; +SELECT @@global.wsrep_buffered_error_log_buffer_size; + diff --git a/mysql-test/suite/wsrep/r/variables.result b/mysql-test/suite/wsrep/r/variables.result index 9a3c4169ac83b..9ae934f5e9ade 100644 --- a/mysql-test/suite/wsrep/r/variables.result +++ b/mysql-test/suite/wsrep/r/variables.result @@ -1,72 +1,73 @@ # Correct Galera library found SHOW GLOBAL STATUS LIKE 'wsrep%'; Variable_name Value -wsrep_local_state_uuid # -wsrep_protocol_version # -wsrep_last_committed # -wsrep_replicated # -wsrep_replicated_bytes # -wsrep_repl_keys # -wsrep_repl_keys_bytes # -wsrep_repl_data_bytes # -wsrep_repl_other_bytes # -wsrep_received # -wsrep_received_bytes # -wsrep_local_commits # -wsrep_local_cert_failures # -wsrep_local_replays # -wsrep_local_send_queue # -wsrep_local_send_queue_max # -wsrep_local_send_queue_min # -wsrep_local_send_queue_avg # -wsrep_local_recv_queue # -wsrep_local_recv_queue_max # -wsrep_local_recv_queue_min # -wsrep_local_recv_queue_avg # -wsrep_local_cached_downto # -wsrep_flow_control_paused_ns # -wsrep_flow_control_paused # -wsrep_flow_control_sent # -wsrep_flow_control_recv # -wsrep_flow_control_active # -wsrep_flow_control_requested # -wsrep_cert_deps_distance # +wsrep_applier_thread_count # wsrep_apply_oooe # wsrep_apply_oool # -wsrep_apply_window # wsrep_apply_waits # -wsrep_commit_oooe # -wsrep_commit_oool # -wsrep_commit_window # -wsrep_local_state # -wsrep_local_state_comment # +wsrep_apply_window # +wsrep_causal_reads # +wsrep_cert_deps_distance # wsrep_cert_index_size # wsrep_cert_interval # -wsrep_open_transactions # -wsrep_open_connections # -wsrep_incoming_addresses # +wsrep_cluster_capabilities # +wsrep_cluster_conf_id # +wsrep_cluster_size # +wsrep_cluster_state_uuid # +wsrep_cluster_status # wsrep_cluster_weight # +wsrep_commit_oooe # +wsrep_commit_oool # +wsrep_commit_window # +wsrep_connected # wsrep_desync_count # wsrep_evs_delayed # wsrep_evs_evict_list # wsrep_evs_repl_latency # wsrep_evs_state # +wsrep_flow_control_active # +wsrep_flow_control_paused # +wsrep_flow_control_paused_ns # +wsrep_flow_control_recv # +wsrep_flow_control_requested # +wsrep_flow_control_sent # wsrep_gcomm_uuid # wsrep_gmcast_segment # -wsrep_applier_thread_count # -wsrep_cluster_capabilities # -wsrep_cluster_conf_id # -wsrep_cluster_size # -wsrep_cluster_state_uuid # -wsrep_cluster_status # -wsrep_connected # +wsrep_incoming_addresses # +wsrep_last_committed # wsrep_local_bf_aborts # +wsrep_local_cached_downto # +wsrep_local_cert_failures # +wsrep_local_commits # wsrep_local_index # +wsrep_local_recv_queue # +wsrep_local_recv_queue_avg # +wsrep_local_recv_queue_max # +wsrep_local_recv_queue_min # +wsrep_local_replays # +wsrep_local_send_queue # +wsrep_local_send_queue_avg # +wsrep_local_send_queue_max # +wsrep_local_send_queue_min # +wsrep_local_state # +wsrep_local_state_comment # +wsrep_local_state_uuid # +wsrep_open_connections # +wsrep_open_transactions # +wsrep_protocol_version # wsrep_provider_capabilities # wsrep_provider_name # wsrep_provider_vendor # wsrep_provider_version # wsrep_ready # +wsrep_received # +wsrep_received_bytes # +wsrep_repl_data_bytes # +wsrep_repl_keys # +wsrep_repl_keys_bytes # +wsrep_repl_other_bytes # +wsrep_replicated # +wsrep_replicated_bytes # wsrep_rollbacker_thread_count # wsrep_thread_count # # Should show nothing. @@ -89,6 +90,10 @@ SELECT VARIABLE_NAME FROM INFORMATION_SCHEMA.SESSION_VARIABLES WHERE VARIABLE_NA VARIABLE_NAME WSREP_ALLOWLIST WSREP_AUTO_INCREMENT_CONTROL +WSREP_BUFFERED_ERROR_LOG_BUFFER_SIZE +WSREP_BUFFERED_ERROR_LOG_FILENAME +WSREP_BUFFERED_ERROR_LOG_FILE_SIZE +WSREP_BUFFERED_ERROR_LOG_ROTATIONS WSREP_CERTIFICATION_RULES WSREP_CERTIFY_NONPK WSREP_CLUSTER_ADDRESS diff --git a/mysql-test/suite/wsrep/r/variables_debug.result b/mysql-test/suite/wsrep/r/variables_debug.result index 50d695fed4be2..bd04a9c791681 100644 --- a/mysql-test/suite/wsrep/r/variables_debug.result +++ b/mysql-test/suite/wsrep/r/variables_debug.result @@ -41,6 +41,7 @@ wsrep_commit_window # wsrep_local_state # wsrep_local_state_comment # wsrep_cert_index_size # +wsrep_causal_reads # wsrep_cert_interval # wsrep_open_transactions # wsrep_open_connections # @@ -90,6 +91,10 @@ SELECT VARIABLE_NAME FROM INFORMATION_SCHEMA.SESSION_VARIABLES WHERE VARIABLE_NA VARIABLE_NAME WSREP_ALLOWLIST WSREP_AUTO_INCREMENT_CONTROL +WSREP_BUFFERED_ERROR_LOG_BUFFER_SIZE +WSREP_BUFFERED_ERROR_LOG_FILENAME +WSREP_BUFFERED_ERROR_LOG_FILE_SIZE +WSREP_BUFFERED_ERROR_LOG_ROTATIONS WSREP_CERTIFICATION_RULES WSREP_CERTIFY_NONPK WSREP_CLUSTER_ADDRESS diff --git a/mysql-test/suite/wsrep/t/variables.test b/mysql-test/suite/wsrep/t/variables.test index c28638e78f1fb..e4f1658361230 100644 --- a/mysql-test/suite/wsrep/t/variables.test +++ b/mysql-test/suite/wsrep/t/variables.test @@ -8,6 +8,7 @@ source include/check_galera_version.inc; source include/galera_variables_ok.inc; +--sorted_result --replace_column 2 # SHOW GLOBAL STATUS LIKE 'wsrep%'; diff --git a/mysys/CMakeLists.txt b/mysys/CMakeLists.txt index 1a08bafda99e2..013080cf383f6 100644 --- a/mysys/CMakeLists.txt +++ b/mysys/CMakeLists.txt @@ -46,7 +46,7 @@ SET(MYSYS_SOURCES array.c charset-def.c charset.c my_default.c my_uuid.c wqueue.c waiting_threads.c ma_dyncol.c ../sql-common/my_time.c my_rdtsc.c psi_noop.c my_atomic_writes.c my_cpu.c my_likely.c my_largepage.c - file_logger.c my_dlerror.c crc32/crc32c.cc + file_logger.cc my_dlerror.c crc32/crc32c.cc my_timezone.cc my_thread_name.cc) IF (WIN32) diff --git a/mysys/file_logger.cc b/mysys/file_logger.cc new file mode 100644 index 0000000000000..9a92c50a16cff --- /dev/null +++ b/mysys/file_logger.cc @@ -0,0 +1,454 @@ +/* Copyright (C) 2012-2025 Monty Program Ab + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + + +#ifndef FLOGGER_SKIP_INCLUDES +#include "my_global.h" +#include +#include +#include +#include +#include +#include +#include +#include +#endif /*FLOGGER_SKIP_INCLUDES*/ + +#ifndef flogger_mutex_init +#define flogger_mutex_init(A,B,C) mysql_mutex_init(A,B,C) +#define flogger_mutex_destroy(A) mysql_mutex_destroy(A) +#define flogger_mutex_lock(A) mysql_mutex_lock(A) +#define flogger_mutex_unlock(A) mysql_mutex_unlock(A) +#endif /*flogger_mutex_init*/ + +#ifdef HAVE_PSI_INTERFACE +/* These belong to the service initialization */ +static PSI_mutex_key key_LOCK_logger_service; +static PSI_mutex_info mutex_list[]= +{{ &key_LOCK_logger_service, "logger_service_file_st::lock", PSI_FLAG_GLOBAL}}; +#endif + +struct logger_handle_st +{ + logger_handle_st() : data(nullptr), size_limit(0), buffer_limit(0), rotations(0) + {} + // We are using a pointer to string to guarantee that we can decrease the size + // of the buffer + // memory buffer storing the log messages + std::unique_ptr data; + unsigned long long size_limit; + unsigned long long buffer_limit; + unsigned int rotations; + File file; + char path[FN_REFLEN]; + size_t path_len; + mysql_mutex_t lock; + + void resize_buffer(void); +}; + +void logger_handle_st::resize_buffer(void) +{ + std::unique_ptr new_buffer(new std::string()); + new_buffer->reserve(buffer_limit); + data.swap(new_buffer); +} + +#define LOG_FLAGS (O_APPEND | O_CREAT | O_WRONLY) + +static unsigned int n_dig(unsigned int i) +{ + unsigned int n=1; + if (i < 10) + n= 1; + else if (i < 100) + n= 2; + else if (i < 1000) + n= 3; + else if (i < 10000) + n= 4; + else if (i < 100000) + n= 5; + else if (i < 1000000) + n= 6; + return n; +} + + +LOGGER_HANDLE *logger_open(const char *path, + unsigned long long size_limit, + unsigned long long buffer_limit, + unsigned int rotations) +{ + LOGGER_HANDLE new_log; + /* + I don't think we ever need more rotations, + but if it's so, the rotation procedure should be adapted to it. + */ + if (rotations > 9999999) + return 0; + + new_log.path_len= strlen(fn_format(new_log.path, path, + mysql_data_home, "", MY_UNPACK_FILENAME)); + + if (new_log.path_len+n_dig(rotations)+1 > FN_REFLEN) + { + errno= ENAMETOOLONG; + /* File path too long */ + return 0; + } + if ((new_log.file= my_open(new_log.path, LOG_FLAGS, MYF(0))) < 0) + { + errno= my_errno; + /* Check errno for the cause */ + return 0; + } + + struct logger_handle_st* l_handle(new logger_handle_st()); + + l_handle->rotations= rotations; + l_handle->size_limit= size_limit; + l_handle->buffer_limit= buffer_limit; + l_handle->path_len= new_log.path_len; + strcpy(l_handle->path, new_log.path); + l_handle->file= new_log.file; + + flogger_mutex_init(key_LOCK_logger_service, &l_handle->lock, + MY_MUTEX_INIT_FAST); + + if (!buffer_limit) + return (LOGGER_HANDLE*)l_handle; + + l_handle->resize_buffer(); + + return (LOGGER_HANDLE*)l_handle; +} + +int logger_close(LOGGER_HANDLE *log) +{ + int result; + File file= log->file; + flogger_mutex_destroy(&log->lock); + if ((result= my_close(file, MYF(0)))) + errno= my_errno; + delete log; + return result; +} + + +static char *logname(LOGGER_HANDLE *log, char *buf, unsigned int n_log) +{ + sprintf(buf+log->path_len, ".%0*u", n_dig(log->rotations), n_log); + return buf; +} + + +static int do_rotate(LOGGER_HANDLE *log) +{ + char namebuf[FN_REFLEN]; + int result; + unsigned int i; + char *buf_old, *buf_new, *tmp; + + if (log->rotations == 0) + return 0; + + memcpy(namebuf, log->path, log->path_len); + + buf_new= logname(log, namebuf, log->rotations); + buf_old= log->path; + for (i=log->rotations-1; i>0; i--) + { + logname(log, buf_old, i); + if (!access(buf_old, F_OK) && + (result= my_rename(buf_old, buf_new, MYF(0)))) + goto exit; + tmp= buf_old; + buf_old= buf_new; + buf_new= tmp; + } + if ((result= my_close(log->file, MYF(0)))) + goto exit; + namebuf[log->path_len]= 0; + result= my_rename(namebuf, logname(log, log->path, 1), MYF(0)); + log->file= my_open(namebuf, LOG_FLAGS, MYF(0)); +exit: + errno= my_errno; + return log->file < 0 || result; +} + + +/* + Return 1 if we should rotate the log +*/ + +my_bool logger_time_to_rotate(LOGGER_HANDLE *log) +{ + my_off_t filesize; + if (log->rotations > 0) + { + filesize= my_tell(log->file, MYF(0)); + if (filesize != (my_off_t) -1) + { + filesize+= log->data->size(); // Add buffer + if ((unsigned long long)filesize >= log->size_limit) + return 1; + } + } + + return 0; +} + + +int logger_vprintf(LOGGER_HANDLE *log, const char* fmt, va_list ap) +{ + int result=0; + char cvtbuf[1024]; + size_t n_bytes; + + flogger_mutex_lock(&log->lock); + if (logger_time_to_rotate(log) && do_rotate(log)) + { + result= -1; + errno= my_errno; + goto exit; /* Log rotation needed but failed */ + } + + n_bytes= my_vsnprintf(cvtbuf, sizeof(cvtbuf), fmt, ap); + if (n_bytes >= sizeof(cvtbuf)) + n_bytes= sizeof(cvtbuf) - 1; + + if (log->data.get() == nullptr || log->data->capacity() == 0) + { + result= (int)my_write(log->file, (uchar *) cvtbuf, n_bytes, MYF(0)); + } + else + { + // Buffered logging + const size_t msg_size = log->data->size() + n_bytes; + if (msg_size > log->data->capacity() - 1) + logger_flush(log); + *(log->data)+= cvtbuf; + } + +exit: + flogger_mutex_unlock(&log->lock); + return result; +} + + +static int logger_write_r(LOGGER_HANDLE *log, my_bool allow_rotations, + const char *buffer, size_t size) +{ + int result=0; + + flogger_mutex_lock(&log->lock); + if (allow_rotations && logger_time_to_rotate(log) && do_rotate(log)) + { + result= -1; + errno= my_errno; + goto exit; /* Log rotation needed but failed */ + } + + if (log->data.get() == nullptr || log->data->capacity() == 0) + { + result= (int)my_write(log->file, (uchar *) buffer, size, MYF(0)); + } + else + { + // Buffered logging + const size_t msg_size = log->data->size() + size; + if (msg_size > log->data->capacity() - 1) + result= logger_flush(log); + *(log->data)+= buffer; + } + +exit: + flogger_mutex_unlock(&log->lock); + return result; +} + + +int logger_write(LOGGER_HANDLE *log, const char *buffer, size_t size) +{ + return logger_write_r(log, TRUE, buffer, size); +} + +int logger_rotate(LOGGER_HANDLE *log, const unsigned int n_rotations) +{ + int result=0; + flogger_mutex_lock(&log->lock); + if (n_rotations) + log->rotations= n_rotations; + result= do_rotate(log); + flogger_mutex_unlock(&log->lock); + + return result; +} + + +int logger_printf(LOGGER_HANDLE *log, const char *fmt, ...) +{ + int result; + va_list args; + va_start(args,fmt); + result= logger_vprintf(log, fmt, args); + va_end(args); + return result; +} + +void logger_init_mutexes() +{ +#ifdef HAVE_PSI_INTERFACE + if (unlikely(PSI_server)) + PSI_server->register_mutex("sql_logger", mutex_list, 1); +#endif +} + +/** Resize buffer size. + +@param[in,out] log log handle + +@return 0 success, !0 for error */ +int logger_resize_buffer(LOGGER_HANDLE *log, unsigned long long buffer_limit) +{ + if (log->data.get() != nullptr && buffer_limit == log->data->capacity()) + return 0; + + flogger_mutex_lock(&log->lock); + // Write out what we have currently, that way we don't have + // to deal with old data in the buffer + logger_flush(log); + log->buffer_limit= buffer_limit; + + if (buffer_limit == 0) + { + log->data.reset(); + } + else + { + log->resize_buffer(); + } + + flogger_mutex_unlock(&log->lock); + return 0; +} + +/** Flush buffered log to the disk. + +Note that this function does not use mutexes, thus +it should be called from function that does or +in shutdown or from signal handler. + +@param[in,out] log log handle + +@return 0 success, !0 for error */ +int logger_flush(LOGGER_HANDLE *log) +{ + size_t result= 0; + + if (log->data.get() == nullptr || log->data->size() == 0) + return 0; + + const size_t curr_size= log->data->capacity(); + if (logger_time_to_rotate(log) && do_rotate(log)) + { + /* Check errno for the cause */ + errno= my_errno; + result= 1; + goto exit; + } + + result= my_write(log->file, (uchar *) log->data->data(), log->data->size(), MYF(0)); + + if (result == (size_t)-1) + { + /* Check errno for the cause */ + errno= my_errno; + goto exit; + } + + result= 0; + log->data->clear(); + log->data->reserve(curr_size); + +exit: + return result; +} + +/** Set a new file size limit. + +If file size limit is changed we rotate to new log file +if limit is reached. + +@param[in,out] log log handle +@param[in] size_limit file size limit + +@return 0 success, !0 for error */ +int logger_resize_size(LOGGER_HANDLE *log, unsigned long long size_limit) +{ + int ret= 0; + + if (size_limit == log->size_limit) + return ret; // nothing to do + + flogger_mutex_lock(&log->lock); + + if ((ret= logger_flush(log))) + goto exit; + + log->size_limit= size_limit; + +exit: + flogger_mutex_unlock(&log->lock); + return ret; +} + + +/** Set a new file name. + +@param[in,out] log log handle +@param[in] path new file name + +@return 0 success, !0 for error */ +int logger_rename_file(LOGGER_HANDLE *log, const char *path) +{ + int res=0; + + flogger_mutex_lock(&log->lock); + + if ((res= logger_flush(log))) + goto exit; + + if ((res= my_close(log->file, MYF(0)))) + { + /* Check errno for the cause */ + errno= my_errno; + goto exit; + } + + log->path_len= strlen(fn_format(log->path, path, + mysql_data_home, "", MY_UNPACK_FILENAME)); + + if ((log->file= my_open(log->path, LOG_FLAGS, MYF(0))) < 0) + { + errno= my_errno; + /* Check errno for the cause */ + res= 1; + } +exit: + flogger_mutex_unlock(&log->lock); + return res; +} diff --git a/mysys/file_logger.c b/plugin/server_audit/file_logger.c similarity index 100% rename from mysys/file_logger.c rename to plugin/server_audit/file_logger.c diff --git a/plugin/server_audit/server_audit.c b/plugin/server_audit/server_audit.c index be4e0b2294dfa..4b4528c1ce0ad 100644 --- a/plugin/server_audit/server_audit.c +++ b/plugin/server_audit/server_audit.c @@ -248,12 +248,14 @@ static my_off_t loc_tell(File fd) #ifdef HAVE_PSI_INTERFACE #undef HAVE_PSI_INTERFACE -#include -#include "../../mysys/file_logger.c" +// Note: as this file is still C, these files are +// added to this directory to avoid changing this. +#include "service_logger.h" +#include "file_logger.c" #define HAVE_PSI_INTERFACE #else -#include -#include "../../mysys/file_logger.c" +#include "service_logger.h" +#include "file_logger.c" #endif #endif /*!MARIADB_ONLY*/ diff --git a/plugin/server_audit/service_logger.h b/plugin/server_audit/service_logger.h new file mode 100644 index 0000000000000..b8d2b1eaf17a1 --- /dev/null +++ b/plugin/server_audit/service_logger.h @@ -0,0 +1,105 @@ +/* Copyright (C) 2012 Monty Program Ab + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef MYSQL_SERVICE_LOGGER_INCLUDED +#define MYSQL_SERVICE_LOGGER_INCLUDED + +#ifndef MYSQL_ABI_CHECK +#include +#endif + +/** + @file + logger service + + Log file with rotation implementation. + + This service implements logging with possible rotation + of the log files. Interface intentionally tries to be similar to FILE* + related functions. + + So that one can open the log with logger_open(), specifying + the limit on the logfile size and the rotations number. + + Then it's possible to write messages to the log with + logger_printf or logger_vprintf functions. + + As the size of the logfile grows over the specified limit, + it is renamed to 'logfile.1'. The former 'logfile.1' becomes + 'logfile.2', etc. The file 'logfile.rotations' is removed. + That's how the rotation works. + + The rotation can be forced with the logger_rotate() call. + + Finally the log should be closed with logger_close(). + +@notes: + Implementation checks the size of the log file before it starts new + printf into it. So the size of the file gets over the limit when it rotates. + + The access is secured with the mutex, so the log is threadsafe. +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct logger_handle_st LOGGER_HANDLE; + +extern struct logger_service_st { + void (*logger_init_mutexes)(); + LOGGER_HANDLE* (*open)(const char *path, + unsigned long long size_limit, + unsigned int rotations); + int (*close)(LOGGER_HANDLE *log); + int (*vprintf)(LOGGER_HANDLE *log, const char *fmt, va_list argptr); + int (*printf)(LOGGER_HANDLE *log, const char *fmt, ...); + int (*write)(LOGGER_HANDLE *log, const char *buffer, size_t size); + int (*rotate)(LOGGER_HANDLE *log); +} *logger_service; + +#ifdef MYSQL_DYNAMIC_PLUGIN + +#define logger_init_mutexes logger_service->logger_init_mutexes +#define logger_open(path, size_limit, rotations) \ + (logger_service->open(path, size_limit, rotations)) +#define logger_close(log) (logger_service->close(log)) +#define logger_rotate(log) (logger_service->rotate(log)) +#define logger_vprintf(log, fmt, argptr) (logger_service->\ + vprintf(log, fmt, argptr)) +#define logger_printf (*logger_service->printf) +#define logger_write(log, buffer, size) \ + (logger_service->write(log, buffer, size)) +#else + + void logger_init_mutexes(); + LOGGER_HANDLE *logger_open(const char *path, + unsigned long long size_limit, + unsigned int rotations); + int logger_close(LOGGER_HANDLE *log); + int logger_vprintf(LOGGER_HANDLE *log, const char *fmt, va_list argptr); + int logger_printf(LOGGER_HANDLE *log, const char *fmt, ...); + int logger_write(LOGGER_HANDLE *log, const char *buffer, size_t size); + int logger_rotate(LOGGER_HANDLE *log); +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /*MYSQL_SERVICE_LOGGER_INCLUDED*/ + diff --git a/plugin/sql_errlog/sql_errlog.c b/plugin/sql_errlog/sql_errlog.c index 6608bda5cce2a..b865f88511842 100644 --- a/plugin/sql_errlog/sql_errlog.c +++ b/plugin/sql_errlog/sql_errlog.c @@ -151,7 +151,7 @@ static int sql_error_log_init(void *p __attribute__((unused))) { logger_init_mutexes(); - logfile= logger_open(filename, size_limit, rotations); + logfile= logger_open(filename, size_limit, 0, rotations); if (logfile == NULL) { fprintf(stderr, "Could not create file '%s'\n", filename); @@ -175,7 +175,7 @@ static void rotate_log(MYSQL_THD thd __attribute__((unused)), void *var_ptr __attribute__((unused)), const void *save __attribute__((unused))) { - (void) logger_rotate(logfile); + (void) logger_rotate(logfile, 0); } diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 95a83d1244093..e714fd48d15ef 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -17,6 +17,7 @@ IF(WITH_WSREP AND NOT EMBEDDED_LIBRARY) SET(WSREP_SOURCES + wsrep_buffered_error_log.cc wsrep_client_service.cc wsrep_high_priority_service.cc wsrep_server_service.cc diff --git a/sql/handler.cc b/sql/handler.cc index 570fff8e0edcd..bbe7fc72877bd 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -2343,13 +2343,16 @@ int ha_rollback_trans(THD *thd, bool all) transaction_participant *ht= ha_info->ht(); if ((err= ht->rollback(thd, all))) { - // cannot happen my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err); error=1; #ifdef WITH_WSREP - WSREP_WARN("handlerton rollback failed, thd %lld %lld conf %d SQL %s", - thd->thread_id, thd->query_id, thd->wsrep_trx().state(), - thd->query()); + if (WSREP(thd)) + WSREP_WARN("ht->rollback() failed, thd %lld %lld conf %d " + " err=%s wsrep=%s SQL %s", + thd->thread_id, thd->query_id, thd->wsrep_trx().state(), + strerror(err), + wsrep::to_c_string(thd->wsrep_cs().current_error()), + wsrep_thd_query(thd)); #endif /* WITH_WSREP */ } status_var_increment(thd->status_var.ha_rollback_count); @@ -2361,7 +2364,7 @@ int ha_rollback_trans(THD *thd, bool all) } #ifdef WITH_WSREP - if (thd->is_error()) + if (WSREP(thd) && thd->is_error()) { WSREP_DEBUG("ha_rollback_trans(%lld, %s) rolled back: %s: %s; is_real %d", thd->thread_id, all?"TRUE":"FALSE", wsrep_thd_query(thd), diff --git a/sql/log.cc b/sql/log.cc index 59168b08f8139..e8129815478ad 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -68,6 +68,7 @@ #ifdef WITH_WSREP #include "wsrep_trans_observer.h" #include "wsrep_status.h" +#include "wsrep_var.h" #endif /* WITH_WSREP */ #ifdef HAVE_REPLICATION @@ -403,11 +404,12 @@ Silence_log_table_errors::handle_condition(THD *, return TRUE; } -sql_print_message_func sql_print_message_handlers[3] = +sql_print_message_func sql_print_message_handlers[4] = { sql_print_information, sql_print_warning, - sql_print_error + sql_print_error, + sql_print_debug }; @@ -10024,7 +10026,12 @@ static void print_buffer_to_file(enum loglevel level, const char *buffer, struct tm *start; THD *thd= 0; size_t tag_length= 0; - char tag[NAME_LEN]; + char tag[NAME_LEN] = {'\0'}; + bool buf_allocated= false; + char tmp_buf[MAX_LOG_BUFFER_SIZE]= {'\0'}; + size_t len= MAX_LOG_BUFFER_SIZE-1; + char *buf = &tmp_buf[0]; + DBUG_ENTER("print_buffer_to_file"); DBUG_PRINT("enter",("buffer: %s", buffer)); @@ -10049,7 +10056,11 @@ static void print_buffer_to_file(enum loglevel level, const char *buffer, localtime_r(&skr, &tm_tmp); start=&tm_tmp; - fprintf_stderr( "%d-%02d-%02d %2d:%02d:%02d %lu [%s] %.*s%.*s\n", + assert(level <= DEBUG_LEVEL); + const char* level_msg[] = + { "ERROR", "Warning", "Note", "Debug" }; + + len= snprintf(buf, len, "%d-%02d-%02d %2d:%02d:%02d %lu [%s] %.*s%.*s\n", start->tm_year + 1900, start->tm_mon+1, start->tm_mday, @@ -10057,14 +10068,51 @@ static void print_buffer_to_file(enum loglevel level, const char *buffer, start->tm_min, start->tm_sec, (unsigned long) (thd ? thd->thread_id : 0), - (level == ERROR_LEVEL ? "ERROR" : level == WARNING_LEVEL ? - "Warning" : "Note"), + level_msg[level], (int) tag_length, tag, (int) length, buffer); - fflush(stderr); + if (len > sizeof(tmp_buf) -1 ) + { + len += 2; // safety + buf= (char *)my_malloc(PSI_INSTRUMENT_ME, len, MYF(MY_WME)); + buf_allocated= true; + len = snprintf(buf, len, "%d-%02d-%02d %2d:%02d:%02d %lu [%s] %.*s%.*s\n", + start->tm_year + 1900, + start->tm_mon+1, + start->tm_mday, + start->tm_hour, + start->tm_min, + start->tm_sec, + (unsigned long) (thd ? thd->thread_id : 0), + level_msg[level], + (int) tag_length, tag, + (int) length, buffer); + } #ifdef WITH_WSREP + // Use normal logging if log level is not DEBUG OR + // if user requested DEBUG level logging and buffered + // error log is not on. + if (level < DEBUG_LEVEL || + ((wsrep_debug_mode & WSREP_DEBUG_MODE_DEBUG) && + !(wsrep_debug_mode & WSREP_DEBUG_MODE_BUFFERED))) + { +#endif + fprintf(stderr, "%s", buf); + fflush(stderr); +#ifdef WITH_WSREP + } + + // Use buffred logging if log level is not DEBUG AND + // buffered logging is requested OR + // if user requested DEBUG level logging and buffered + // error log on. + if ((level < DEBUG_LEVEL && (wsrep_debug_mode & WSREP_DEBUG_MODE_BUFFERED)) || + ((wsrep_debug_mode & WSREP_DEBUG_MODE_DEBUG) && + (wsrep_debug_mode & WSREP_DEBUG_MODE_BUFFERED))) + wsrep_buffered_error_log.log(buf, len); + if (level <= WARNING_LEVEL) { wsrep::reporter::log_level const lvl = (level <= ERROR_LEVEL ? @@ -10074,6 +10122,9 @@ static void print_buffer_to_file(enum loglevel level, const char *buffer, } #endif /* WITH_WSREP */ + if (buf_allocated) + my_free(buf); + mysql_mutex_unlock(&LOCK_error_log); DBUG_VOID_RETURN; } @@ -10111,6 +10162,17 @@ int vprint_msg_to_log(enum loglevel level, const char *format, va_list args) } #endif /* EMBEDDED_LIBRARY */ +void sql_print_debug(const char *format, ...) +{ + va_list args; + DBUG_ENTER("sql_print_debug"); + + va_start(args, format); + error_log_print(DEBUG_LEVEL, format, args); + va_end(args); + + DBUG_VOID_RETURN; +} void sql_print_error(const char *format, ...) { diff --git a/sql/log.h b/sql/log.h index 2f3b6508f58d9..05613b7e6445c 100644 --- a/sql/log.h +++ b/sql/log.h @@ -1372,6 +1372,8 @@ void sql_print_error(const char *format, ...); void sql_print_warning(const char *format, ...); void sql_print_information(const char *format, ...); void sql_print_information_v(const char *format, va_list ap); +void sql_print_debug(const char *format, ...); + typedef void (*sql_print_message_func)(const char *format, ...); extern sql_print_message_func sql_print_message_handlers[]; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 28f02f7162fdd..3218675229b66 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1333,9 +1333,10 @@ void Buffered_log::print() case WARNING_LEVEL: sql_print_warning("Buffered warning: %s", m_message.c_ptr_safe()); break; + case DEBUG_LEVEL: case INFORMATION_LEVEL: /* - Messages printed as "information" still end up in the mysqld *error* log, + Messages printed as "information" or "debug" still end up in the mysqld *error* log, but with a [Note] tag instead of an [ERROR] tag. While this is probably fine for a human reading the log, it is upsetting existing automated scripts used to parse logs, @@ -2065,6 +2066,10 @@ static void clean_up(bool print_message) free_error_messages(); /* Tell main we are ready */ logger.cleanup_end(); +#ifdef WITH_WSREP + wsrep_buffered_error_log.write_to_disk(); + wsrep_buffered_error_log.close(); +#endif /* WITH_WSREP */ sys_var_end(); free_charsets(); @@ -5038,6 +5043,13 @@ static int init_server_components() error_handler_hook= my_message_sql; proc_info_hook= set_thd_stage_info; +#ifdef WITH_WSREP + /* Init function will determine if we use buffered error log or not */ + wsrep_buffered_error_log.init(); + if (wsrep_debug) + wsrep_debug_mode |= WSREP_DEBUG_MODE_DEBUG; +#endif + /* Set up hook to handle disk full */ my_sleep_for_space= mariadb_sleep_for_space; diff --git a/sql/signal_handler.cc b/sql/signal_handler.cc index 96020c68b2bc5..30b09abddb40a 100644 --- a/sql/signal_handler.cc +++ b/sql/signal_handler.cc @@ -29,6 +29,10 @@ #include "wsrep_server_state.h" #endif /* WITH_WSREP */ +#ifdef WITH_WSREP +#include "wsrep_mysqld.h" +#endif + #ifdef _WIN32 #include #include @@ -147,6 +151,10 @@ extern "C" sig_handler handle_fatal_signal(int sig) bool print_invalid_query_pointer= false; #endif +#ifdef WITH_WSREP + wsrep_buffered_error_log.write_to_disk(); +#endif + if (segfaulted) { my_safe_printf_stderr("Fatal " SIGNAL_FMT " while backtracing\n", sig); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 12445dd778231..d12de5cd2dec0 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1651,7 +1651,8 @@ dispatch_command_return dispatch_command(enum enum_server_command command, THD * if (WSREP(thd) && thd->wsrep_next_trx_id() == WSREP_UNDEFINED_TRX_ID) { thd->set_wsrep_next_trx_id(thd->query_id); - WSREP_DEBUG("assigned new next trx id: %" PRIu64, thd->wsrep_next_trx_id()); + WSREP_DEBUG("Thread %llu assigned new next trx id: %" PRIu64, + thd_get_thread_id(thd),thd->wsrep_next_trx_id()); } #endif /* WITH_WSREP */ diff --git a/sql/sql_plugin_services.inl b/sql/sql_plugin_services.inl index 2114f3560a7ec..dfe1426dc50ae 100644 --- a/sql/sql_plugin_services.inl +++ b/sql/sql_plugin_services.inl @@ -114,7 +114,11 @@ static struct logger_service_st logger_service_handler= { logger_vprintf, logger_printf, logger_write, - logger_rotate + logger_rotate, + logger_rename_file, + logger_resize_buffer, + logger_resize_size, + logger_flush }; static struct thd_autoinc_service_st thd_autoinc_handler= { diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index af07c636bc2b6..c09b24d793370 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -69,7 +69,8 @@ #include #ifdef WITH_WSREP #include "wsrep_mysqld.h" -#endif +#include "wsrep_buffered_error_log.h" +#endif /* WITH_WSREP */ #define PCRE2_STATIC 1 /* Important on Windows */ #include "pcre2.h" /* pcre2 header file */ @@ -5224,7 +5225,7 @@ Sys_proxy_protocol_networks( ON_CHECK(check_proxy_protocol_networks), ON_UPDATE(fix_proxy_protocol_networks)); -static bool check_log_path(sys_var *self, THD *thd, set_var *var) +bool check_log_path(sys_var *self, THD *thd, set_var *var) { if (!var->value) return false; // DEFAULT is ok @@ -6520,6 +6521,37 @@ static Sys_var_charptr Sys_wsrep_allowlist( READ_ONLY GLOBAL_VAR(wsrep_allowlist), CMD_LINE(REQUIRED_ARG), DEFAULT("")); +static Sys_var_charptr_fscs Sys_wsrep_buffered_error_log_filename( + "wsrep_buffered_error_log_filename", "Filename of the buffered error log", + PREALLOCATED GLOBAL_VAR(wsrep_buffered_error_log_filename), CMD_LINE(REQUIRED_ARG), + DEFAULT(0), NO_MUTEX_GUARD, NOT_IN_BINLOG, + ON_CHECK(wsrep_buffered_error_log_filename_check), + ON_UPDATE(wsrep_buffered_error_log_filename_update)); + +static Sys_var_ulonglong Sys_wsrep_buffered_error_log_buffer_size( + "wsrep_buffered_error_log_buffer_size", "Size of the buffered error log buffer", + GLOBAL_VAR(wsrep_buffered_error_log_buffer_size), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, SIZE_T_MAX), DEFAULT(0), BLOCK_SIZE(1), NO_MUTEX_GUARD, + NOT_IN_BINLOG, + ON_CHECK(wsrep_buffered_error_log_buffer_size_check), + ON_UPDATE(wsrep_buffered_error_log_buffer_size_update)); + +static Sys_var_ulonglong Sys_wsrep_buffered_error_log_file_size( + "wsrep_buffered_error_log_file_size", "Max size of the buffered error log file", + GLOBAL_VAR(wsrep_buffered_error_log_file_size), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, SIZE_T_MAX), DEFAULT(0), BLOCK_SIZE(1), NO_MUTEX_GUARD, + NOT_IN_BINLOG, + ON_CHECK(wsrep_buffered_error_log_file_size_check), + ON_UPDATE(wsrep_buffered_error_log_file_size_update)); + +static Sys_var_uint Sys_wsrep_buffered_error_log_rotations ( + "wsrep_buffered_error_log_rotations", "Number of log rotations before log is removed", + GLOBAL_VAR(wsrep_buffered_error_log_rotations), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, 999999), + DEFAULT(10), BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG, + ON_CHECK(wsrep_buffered_error_log_rotations_check), + ON_UPDATE(wsrep_buffered_error_log_rotations_update)); + #endif /* WITH_WSREP */ static bool fix_host_cache_size(sys_var *, THD *, enum_var_type) diff --git a/sql/wsrep_buffered_error_log.cc b/sql/wsrep_buffered_error_log.cc new file mode 100644 index 0000000000000..27345362113b4 --- /dev/null +++ b/sql/wsrep_buffered_error_log.cc @@ -0,0 +1,160 @@ +/* Copyright (C) 2025 Codership Oy + + Created by Zsolt Parragi + Modified by Jan Lindström + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ + +#include "wsrep_buffered_error_log.h" +#include "wsrep.h" +#include "wsrep_mysqld.h" + +Buffered_error_logger wsrep_buffered_error_log; +const char *wsrep_buffered_error_log_filename= nullptr; +unsigned long long wsrep_buffered_error_log_buffer_size= 0; +unsigned long long wsrep_buffered_error_log_file_size= 0; +uint wsrep_buffered_error_log_rotations= 0; + +void Buffered_error_logger::init() +{ + logfile= nullptr; + + if (wsrep_buffered_error_log_buffer_size > 0 && + wsrep_buffered_error_log_file_size > 0 && + wsrep_buffered_error_log_filename != nullptr && + strlen(wsrep_buffered_error_log_filename) > 0) + { + logfile= logger_open(wsrep_buffered_error_log_filename, + wsrep_buffered_error_log_file_size, + wsrep_buffered_error_log_buffer_size, + wsrep_buffered_error_log_rotations); + + if (!logfile) + { + WSREP_WARN("Could not open buffered error log %s error=%s (%d).", + wsrep_buffered_error_log_filename, + strerror(errno), errno); + wsrep_disable_logging(); + } + else + { + wsrep_debug_mode |= WSREP_DEBUG_MODE_BUFFERED; + } + } + + return; +} + +void Buffered_error_logger::resize_buffer(const std::size_t buffer_size) +{ + if (!logfile) + return; + + if (logger_resize_buffer(logfile, buffer_size)) + { + wsrep_disable_logging(); + WSREP_WARN("Resize of buffered error log %s to size %zd failed error=%s (%ld).", + wsrep_buffered_error_log_filename, buffer_size, + strerror(errno), errno); + } +} + +void Buffered_error_logger::resize_file_size(const std::size_t file_size) +{ + if (!logfile) + return; + + if (logger_resize_size(logfile, file_size)) + { + wsrep_disable_logging(); + WSREP_WARN("Resize of buffered error log %s file size to %zd failed error=%s (%ld).", + wsrep_buffered_error_log_filename, file_size, + strerror(errno), errno); + } +} + +void Buffered_error_logger::rename_file(const char* new_name) +{ + if (!logfile) + return; + + if (logger_rename_file(logfile, new_name)) + { + wsrep_disable_logging(); + WSREP_WARN("Rename of buffered error log %s to %s failed error=%s (%ld).", + wsrep_buffered_error_log_filename, new_name, + strerror(errno), errno); + } +} + +Buffered_error_logger::~Buffered_error_logger() +{ + if (logfile) + { + logger_close(logfile); + logfile= nullptr; + } +} + +void Buffered_error_logger::log(const char *msg, const std::size_t len) +{ + if (logfile) + { + if (logger_write(logfile, msg, len)) + { + wsrep_disable_logging(); + WSREP_WARN("Log write to buffered error log %s failed error=%s (%ld).", + wsrep_buffered_error_log_filename, + strerror(errno), errno); + } + } +} + +void Buffered_error_logger::write_to_disk() +{ + if (logfile) + { + if (logger_flush(logfile)) + { + wsrep_disable_logging(); + WSREP_WARN("Log write to buffered error log %s failed error=%s (%ld).", + wsrep_buffered_error_log_filename, + strerror(errno), errno); + } + } +} + +void Buffered_error_logger::close() +{ + if (logfile) + { + logger_flush(logfile); + logger_close(logfile); + logfile= nullptr; + } +} + +void Buffered_error_logger::rotate(const uint n_rotations) +{ + if (!logfile || n_rotations == 0) + return; + + if (logger_rotate(logfile, n_rotations)) + { + wsrep_disable_logging(); + WSREP_WARN("Rotation of buffered error log %s failed error=%s (%ld).", + wsrep_buffered_error_log_filename, + strerror(errno), errno); + } +} diff --git a/sql/wsrep_buffered_error_log.h b/sql/wsrep_buffered_error_log.h new file mode 100644 index 0000000000000..9735282cb45df --- /dev/null +++ b/sql/wsrep_buffered_error_log.h @@ -0,0 +1,56 @@ +/* Copyright (C) 2025 Codership Oy + + Created by Zsolt Parragi + Modified by Jan Lindström + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#ifndef WSREP_BUFFERED_ERROR_LOG_H +#define WSREP_BUFFERED_ERROR_LOG_H 1 + +#include +#include +#include +#include +#include +#include +#include +#include + +/** Stores log messages in a fixed size buffer, which can be dumped by large + * chunks instead of line by line, to increase performance for high throughput + * scenarios. + * + * The buffer is also dumped by the crash reporting code and during shutdown to + * ensure that nothing is lost, only delayed. + */ +class Buffered_error_logger +{ +private: + LOGGER_HANDLE* logfile; + + public: + void init(); + void resize_buffer(const std::size_t buffer_size); + void resize_file_size(const std::size_t file_size); + void rename_file(const char* new_name); + void rotate(const uint n_rotations); + + ~Buffered_error_logger(); + + void log(const char *msg, const std::size_t len); + void write_to_disk(); + void close(); +}; + +#endif /* WSREP_BUFFERED_ERROR_LOG */ diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index cf8b9d36c9120..e759d54d5623c 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2008, 2023 Codership Oy - Copyright (c) 2020, 2022, MariaDB +/* Copyright (c) 2008, 2025 Codership Oy + Copyright (c) 2020, 2024, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -130,6 +130,8 @@ uint wsrep_ignore_apply_errors= 0; std::atomic wsrep_thread_create_failed; +ulong wsrep_debug_mode= 0; + /* * End configuration options */ @@ -4041,3 +4043,14 @@ void wsrep_commit_empty(THD* thd, bool all) } DBUG_VOID_RETURN; } + +void wsrep_disable_logging(void) +{ + // Disable buffered error logging + wsrep_debug_mode &= ~WSREP_DEBUG_MODE_BUFFERED; + // Disable also wsrep_debug logging + wsrep_debug_mode &= WSREP_DEBUG_MODE_DEBUG; + // Set wsrep_debug to NONE + wsrep_debug=0; + WSREP_WARN("Buffered error logging and wsrep debug logging disabled."); +} diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h index 183dc65b00707..204d42a0e301d 100644 --- a/sql/wsrep_mysqld.h +++ b/sql/wsrep_mysqld.h @@ -1,4 +1,4 @@ -/* Copyright 2008-2023 Codership Oy +/* Copyright 2008-2024 Codership Oy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -38,6 +38,7 @@ typedef struct st_mysql_show_var SHOW_VAR; #include "wsrep/streaming_context.hpp" #include "wsrep_api.h" #include +#include "wsrep_buffered_error_log.h" #define WSREP_UNDEFINED_TRX_ID ULONGLONG_MAX @@ -145,6 +146,13 @@ enum enum_wsrep_mode { extern const char *wsrep_fragment_units[]; extern const char *wsrep_SR_store_types[]; +/* Modes for wsrep debug error logging */ +enum enum_wsrep_debug_mode { + WSREP_DEBUG_MODE_OFF = 0x0, + WSREP_DEBUG_MODE_DEBUG = 0x1, + WSREP_DEBUG_MODE_BUFFERED = 0x2 +}; + // MySQL status variables extern my_bool wsrep_connected; extern const char* wsrep_cluster_state_uuid; @@ -158,6 +166,11 @@ extern const char* wsrep_provider_version; extern const char* wsrep_provider_vendor; extern char* wsrep_provider_capabilities; extern char* wsrep_cluster_capabilities; +extern unsigned long long wsrep_buffered_error_log_buffer_size; +extern unsigned long long wsrep_buffered_error_log_file_size; +extern const char* wsrep_buffered_error_log_filename; +extern Buffered_error_logger wsrep_buffered_error_log; +extern uint wsrep_buffered_error_log_rotations; int wsrep_show_status(THD *thd, SHOW_VAR *var, void *buff, system_status_var *status_var, enum_var_type scope); @@ -593,6 +606,9 @@ wsrep::key wsrep_prepare_key_for_toi(const char* db, const char* table, void wsrep_wait_ready(THD *thd); void wsrep_ready_set(bool ready_value); + +void wsrep_disable_logging(void); + #else /* !WITH_WSREP */ /* These macros are needed to compile MariaDB without WSREP support diff --git a/sql/wsrep_trans_observer.h b/sql/wsrep_trans_observer.h index 25e71638efd74..1e216803b9fd6 100644 --- a/sql/wsrep_trans_observer.h +++ b/sql/wsrep_trans_observer.h @@ -472,12 +472,6 @@ static inline int wsrep_after_statement(THD* thd) { DBUG_ENTER("wsrep_after_statement"); - WSREP_DEBUG("wsrep_after_statement for %lu client_state %s " - " client_mode %s trans_state %s", - thd_get_thread_id(thd), - wsrep::to_c_string(thd->wsrep_cs().state()), - wsrep::to_c_string(thd->wsrep_cs().mode()), - wsrep::to_c_string(thd->wsrep_cs().transaction().state())); int ret= ((thd->wsrep_cs().state() != wsrep::client_state::s_none && thd->wsrep_cs().mode() == Wsrep_client_state::m_local) && !thd->internal_transaction() ? diff --git a/sql/wsrep_var.cc b/sql/wsrep_var.cc index 83e017a2fc312..03b47476c72fd 100644 --- a/sql/wsrep_var.cc +++ b/sql/wsrep_var.cc @@ -1,4 +1,4 @@ -/* Copyright 2008-2022 Codership Oy +/* Copyright 2008-2025 Codership Oy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -596,6 +596,11 @@ bool wsrep_debug_update(sys_var *self, THD* thd, enum_var_type type) else Wsrep_server_state::instance().debug_log_level(wsrep_debug); + if (wsrep_debug) + wsrep_debug_mode |= WSREP_DEBUG_MODE_DEBUG; + else + wsrep_debug_mode &= ~WSREP_DEBUG_MODE_DEBUG; + return false; } @@ -1121,3 +1126,173 @@ bool wsrep_gtid_domain_id_update(sys_var* self, THD *thd, enum_var_type) return false; } +bool wsrep_buffered_error_log_buffer_size_update(sys_var *, THD *, enum_var_type) +{ + wsrep_buffered_error_log.resize_buffer(wsrep_buffered_error_log_buffer_size); + + if (wsrep_buffered_error_log_buffer_size) + wsrep_debug_mode |= WSREP_DEBUG_MODE_BUFFERED; + else + wsrep_debug_mode &= ~WSREP_DEBUG_MODE_BUFFERED; + + return false; +} + +bool wsrep_buffered_error_log_buffer_size_check(sys_var *self, THD* thd, set_var *var) +{ + const unsigned long long new_buffer_size= var->save_result.ulonglong_value; + + if (!new_buffer_size) + return false; + + if (!WSREP(thd)) + { + push_warning (thd, Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_VALUE_FOR_VAR, + "Cannot set 'wsrep_buffered_error_log_size' to a value other than " + "0 because wsrep is switched off."); + return true; + } + + const unsigned long long max_size= wsrep_buffered_error_log_file_size / 10; + const unsigned long long min_size= wsrep_debug ? 100 * 1024 : 10 * 1024; + + if (new_buffer_size < min_size || new_buffer_size > max_size) + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_VALUE_FOR_VAR, + "Parameter 'wsrep_buffered_error_log_size' should be at range" + " %llu-%llu because wsrep_buffered_error_log_file_size=%llu", + min_size, max_size, + wsrep_buffered_error_log_file_size); + return true; + } + + return false; +} + +bool wsrep_buffered_error_log_file_size_update(sys_var *, THD *, enum_var_type) +{ + wsrep_buffered_error_log.resize_file_size(wsrep_buffered_error_log_file_size); + return false; +} + +bool wsrep_buffered_error_log_file_size_check(sys_var *self, THD* thd, set_var *var) +{ + const unsigned long long new_file_size= var->save_result.ulonglong_value; + + if (!new_file_size) + return false; + + if (!WSREP(thd)) + { + push_warning (thd, Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_VALUE_FOR_VAR, + "Cannot set 'wsrep_buffered_error_log_file_size' to a value other than " + "0 because wsrep is switched off."); + return true; + } + + if (!wsrep_buffered_error_log_buffer_size) + { + push_warning (thd, Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_VALUE_FOR_VAR, + "Cannot set 'wsrep_buffered_error_log_file_size' to a value other than " + "0 because wsrep_buffered_error_log_buffer_size is 0."); + return true; + } + + if (new_file_size < wsrep_buffered_error_log_buffer_size * 10) + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_VALUE_FOR_VAR, + "Cannot set 'wsrep_buffered_error_log_file_size' too small " + "should be at least %llu.", wsrep_buffered_error_log_buffer_size * 10); + return true; + } + + return false; +} + +extern bool check_log_path(sys_var *self, THD *thd, set_var *var); // sys_var.cc + +bool wsrep_buffered_error_log_filename_update(sys_var*, THD* thd, enum_var_type type) +{ + // empty + if (!wsrep_buffered_error_log_filename || + strlen(wsrep_buffered_error_log_filename) == 0) + return false; + + wsrep_buffered_error_log.rename_file(wsrep_buffered_error_log_filename); + return false; +} + +bool wsrep_buffered_error_log_filename_check(sys_var *self, THD* thd, set_var *var) +{ + // Allow empty + if (!var->value || + !var->save_result.string_value.str || + strlen(var->save_result.string_value.str) == 0) + return false; + + if (!WSREP(thd)) + { + push_warning (thd, Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_VALUE_FOR_VAR, + "Cannot set 'wsrep_buffered_error_log_filename' " + "because wsrep is switched off."); + return true; + } + + if (!wsrep_buffered_error_log_buffer_size || !wsrep_buffered_error_log_file_size) + { + push_warning (thd, Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_VALUE_FOR_VAR, + "Cannot set 'wsrep_buffered_error_log_filename' " + "because wsrep_buffered_error_log_buffer_size or" + "wsrep_buffered_error_log_file_size is 0."); + return true; + } + + if (check_log_path(self, thd, var)) + { + push_warning_printf (thd, Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_VALUE_FOR_VAR, + "Cannot set 'wsrep_buffered_error_log_filename' to %s", + var->save_result.string_value.str); + return true; + } + + return false; +} + +bool wsrep_buffered_error_log_rotations_update(sys_var *, THD *, enum_var_type) +{ + wsrep_buffered_error_log.rotate(wsrep_buffered_error_log_rotations); + return false; +} + +bool wsrep_buffered_error_log_rotations_check(sys_var *self, THD* thd, set_var *var) +{ + const longlong new_rotations= (longlong)var->save_result.ulonglong_value; + + if (!WSREP(thd) && new_rotations) + { + push_warning (thd, Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_VALUE_FOR_VAR, + "Cannot set 'wsrep_buffered_error_log_rotations' to a value other than " + "0 because wsrep is switched off."); + return true; + } + + if (!wsrep_buffered_error_log_buffer_size && new_rotations) + { + push_warning (thd, Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_VALUE_FOR_VAR, + "Cannot set 'wsrep_buffered_error_log_rotations' to a value other than " + "0 because wsrep_buffered_error_log_buffer_size is 0."); + return true; + } + + return false; +} diff --git a/sql/wsrep_var.h b/sql/wsrep_var.h index 6b10530ae97ce..182812728c949 100644 --- a/sql/wsrep_var.h +++ b/sql/wsrep_var.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2013-2022 Codership Oy +/* Copyright (C) 2013-2025 Codership Oy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -109,6 +109,15 @@ extern bool wsrep_gtid_seq_no_check CHECK_ARGS; extern bool wsrep_gtid_domain_id_update UPDATE_ARGS; extern bool wsrep_mode_check CHECK_ARGS; + +extern bool wsrep_buffered_error_log_buffer_size_update UPDATE_ARGS; +extern bool wsrep_buffered_error_log_buffer_size_check CHECK_ARGS; +extern bool wsrep_buffered_error_log_file_size_update UPDATE_ARGS; +extern bool wsrep_buffered_error_log_file_size_check CHECK_ARGS; +extern bool wsrep_buffered_error_log_filename_check CHECK_ARGS; +extern bool wsrep_buffered_error_log_filename_update UPDATE_ARGS; +extern bool wsrep_buffered_error_log_rotations_update UPDATE_ARGS; +extern bool wsrep_buffered_error_log_rotations_check CHECK_ARGS; #else /* WITH_WSREP */ #define wsrep_provider_init(X)