MyRocks built-in percona(8.0.13-4)

我在将RocksDB引擎作为内置引擎编入percona的经历

更新(2019.10.21)

按照原文的修改方式的确引起了mtr单元测试log_components_filter故障,在之前基础上的bug fix patch如下:

diff --git a/include/mysql/components/services/log_builtins.h b/include/mysql/components/services/log_builtins.h
index 0000941..1877519 100644
--- a/include/mysql/components/services/log_builtins.h
+++ b/include/mysql/components/services/log_builtins.h
@@ -1422,15 +1422,7 @@ inline bool init_logging_service_for_plugin(
   return false;
 }
 
-/*  We define the following two interfaces empty because:
-     1. rocksdb is built-in engine now;
-     2. other built-in engines, like innodb, myisam, etc., do not call
-        the following two interfaces;
-     3. we keep the code where rocksdb engine calls the following
-        interfaces, instead of remove it, for futural unexpection.
-*/
-#elif defined(EXTRA_CODE_FOR_UNIT_TESTING) || defined(ROCKSDB_PLATFORM_POSIX)
+#elif defined(EXTRA_CODE_FOR_UNIT_TESTING)
 
 /**
   Method is used by unit tests.
diff --git a/storage/rocksdb/ha_rocksdb.cc b/storage/rocksdb/ha_rocksdb.cc
index e38c15e..3e2c968 100644
--- a/storage/rocksdb/ha_rocksdb.cc
+++ b/storage/rocksdb/ha_rocksdb.cc
@@ -82,11 +82,6 @@
 #include "./rdb_psi.h"
 #include "./rdb_threads.h"
 
-// MySQL 8.0 logger service interface
-static SERVICE_TYPE(registry) *reg_srv = nullptr;
-SERVICE_TYPE(log_builtins) *log_bi = nullptr;
-SERVICE_TYPE(log_builtins_string) *log_bs = nullptr;
-
 #include <sys/eventfd.h>
 #include <boost/thread/thread.hpp>
 #include <boost/lockfree/queue.hpp>
@@ -2450,11 +2445,6 @@ static uint rocksdb_partition_flags() { return (HA_CANNOT_PARTITION_FK); }
 
 static int rocksdb_init_func(void *const p) {
 
-  // Initialize error logging service.
-  if (init_logging_service_for_plugin(&reg_srv, &log_bi, &log_bs)) {
-    return HA_EXIT_FAILURE;
-  }
-
   if (rdb_check_rocksdb_corruption()) {
     LogPluginErrMsg(ERROR_LEVEL, 0,
                     "There was corruption detected in the RockDB data files. "
@@ -2586,7 +2576,6 @@ static int rocksdb_init_func(void *const p) {
     LogPluginErrMsg(
         ERROR_LEVEL, 0,
         "Can't enable both use_direct_reads and allow_mmap_reads\n");
-    deinit_logging_service_for_plugin(&reg_srv, &log_bi, &log_bs);
     return HA_EXIT_FAILURE;
   }
 
@@ -2625,7 +2614,6 @@ static int rocksdb_init_func(void *const p) {
     LogPluginErrMsg(ERROR_LEVEL, 0,
                     "Can't enable both use_direct_io_for_flush_and_compaction "
                     "and allow_mmap_writes\n");
-    deinit_logging_service_for_plugin(&reg_srv, &log_bi, &log_bs);
     return HA_EXIT_FAILURE;
   }
 
@@ -2634,7 +2622,6 @@ static int rocksdb_init_func(void *const p) {
     LogPluginErrMsg(ERROR_LEVEL, 0,
                     "rocksdb_flush_log_at_trx_commit needs to be 0 to use "
                     "allow_mmap_writes");
-    deinit_logging_service_for_plugin(&reg_srv, &log_bi, &log_bs);
     return HA_EXIT_FAILURE;
   }
 
@@ -2718,14 +2705,12 @@ static int rocksdb_init_func(void *const p) {
     if (!status.ok()) {
       LogPluginErrMsg(ERROR_LEVEL, 0, "Persistent cache returned error: (%s)",
                       status.getState());
-      deinit_logging_service_for_plugin(&reg_srv, &log_bi, &log_bs);
       return HA_EXIT_FAILURE;
     }
     rocksdb_tbl_options->persistent_cache = pcache;
   } else if (strlen(rocksdb_persistent_cache_path)) {
     LogPluginErrMsg(ERROR_LEVEL, 0,
                     "ust specify rocksdb_persistent_cache_size_mb");
-    deinit_logging_service_for_plugin(&reg_srv, &log_bi, &log_bs);
     return HA_EXIT_FAILURE;
   }
 
@@ -2734,7 +2719,6 @@ static int rocksdb_init_func(void *const p) {
                             rocksdb_default_cf_options,
                             rocksdb_override_cf_options)) {
     LogPluginErrMsg(ERROR_LEVEL, 0, "Failed to initialize CF options map.");
-    deinit_logging_service_for_plugin(&reg_srv, &log_bi, &log_bs);
     return HA_EXIT_FAILURE;
   }
 
@@ -2799,7 +2783,6 @@ static int rocksdb_init_func(void *const p) {
 
   if (dict_manager.init(rdb, &cf_manager)) {
     LogPluginErrMsg(ERROR_LEVEL, 0, "Failed to initialize data dictionary.");
-    deinit_logging_service_for_plugin(&reg_srv, &log_bi, &log_bs);
     return HA_EXIT_FAILURE;
   }
 
@@ -2810,7 +2793,6 @@ static int rocksdb_init_func(void *const p) {
 #endif  // defined(ROCKSDB_INCLUDE_VALIDATE_TABLES) &&
         // ROCKSDB_INCLUDE_VALIDATE_TABLES
     LogPluginErrMsg(ERROR_LEVEL, 0, "Failed to initialize DDL manager.");
-    deinit_logging_service_for_plugin(&reg_srv, &log_bi, &log_bs);
     return HA_EXIT_FAILURE;
   }
 
@@ -2852,7 +2834,6 @@ static int rocksdb_init_func(void *const p) {
   if (err != 0) {
     LogPluginErrMsg(ERROR_LEVEL, 0,
                     "Couldn't start the background thread: (errno=%d)", err);
-    deinit_logging_service_for_plugin(&reg_srv, &log_bi, &log_bs);
     return HA_EXIT_FAILURE;
   }
 
@@ -2865,7 +2846,6 @@ static int rocksdb_init_func(void *const p) {
   if (err != 0) {
     LogPluginErrMsg(ERROR_LEVEL, 0,
                     "Couldn't start the drop index thread: (errno=%d)", err);
-    deinit_logging_service_for_plugin(&reg_srv, &log_bi, &log_bs);
     return HA_EXIT_FAILURE;
   }
 
@@ -2879,7 +2859,6 @@ static int rocksdb_init_func(void *const p) {
                           HA_ERR_ROCKSDB_LAST);
   if (err != 0) {
     LogPluginErrMsg(ERROR_LEVEL, 0, "Couldn't initialize error messages");
-    deinit_logging_service_for_plugin(&reg_srv, &log_bi, &log_bs);
     return HA_EXIT_FAILURE;
   }
 
@@ -2985,8 +2964,6 @@ static int rocksdb_done_func(void *const p) {
 
   my_error_unregister(HA_ERR_ROCKSDB_FIRST, HA_ERR_ROCKSDB_LAST);
 
-  deinit_logging_service_for_plugin(&reg_srv, &log_bi, &log_bs);
-
   // clear the initialized flag and unlock
   rdb_get_hton_init_state()->set_initialized(false);
 

出发点

使用MySQL(本文中的MySQL和percona混用)时,我们常在配置文件中指定参数默认值,如binlog_formatdefault-storage-engine

percona提供存储引擎MyRocks(本文中MyRocks和RocksDB都指Percona中的RocksDB存储引擎),以动态加载的方式供用户安装。我们或许想在配置文件中也制定RocksDB的相关参数,比如rocksdb_deadlock_detect等。但由于RocksDB作为动态链接库存在,MySQL启动时并无RocksDB任何信息(只有当MySQL启动完毕后才能加载,比如percona官方给出的ps-admin --enable-rocksdb,或手动安装install plugin rocksdb soname 'ha_rocksdb.so'。),配置文件中的rocksdb_xxx参数被认为是unkown variable,导致无法启动MySQL Server。

所以,我们考虑把RocksDB作为percona的内置引擎编译,就像MyISAM一样。

Patch

先给出结论:给出针对percona 8.0.13-4版本的patch,使得MyRocks built-in percona

diff --git a/include/mysql/components/services/log_builtins.h b/include/mysql/components/services/log_builtins.h
index 7e61b0c..5d56d91 100644
--- a/include/mysql/components/services/log_builtins.h
+++ b/include/mysql/components/services/log_builtins.h
@@ -1385,7 +1385,15 @@ inline bool init_logging_service_for_plugin(
   return false;
 }
 
-#elif defined(EXTRA_CODE_FOR_UNIT_TESTING)
+/*
+   We define the following two interfaces empty because:
+     1. rocksdb is built-in engine now;
+     2. other built-in engines, like innodb, myisam, etc., do not call
+        the following two interfaces;
+     3. we keep the code where rocksdb engine calls the following
+        interfaces, instead of remove it, for futural unexpection.
+*/
+#elif defined(EXTRA_CODE_FOR_UNIT_TESTING) || defined(ROCKSDB_PLATFORM_POSIX)
 
 /**
   Method is used by unit tests.
diff --git a/storage/rocksdb/CMakeLists.txt b/storage/rocksdb/CMakeLists.txt
index 8e9086d..a823887 100644
--- a/storage/rocksdb/CMakeLists.txt
+++ b/storage/rocksdb/CMakeLists.txt
@@ -150,6 +150,11 @@ INCLUDE_DIRECTORIES(
   ${CMAKE_CURRENT_SOURCE_DIR}/third_party/zstd/lib/dictBuilder
 )
 
+SET(PARTITION_BASE_DIR ${CMAKE_SOURCE_DIR}/sql/partitioning)
+SET(PARTITION_SOURCE ${PARTITION_BASE_DIR}/partition_base.cc)
+ADD_CONVENIENCE_LIBRARY(partition_base ${PARTITION_SOURCE})
+ADD_DEPENDENCIES(partition_base GenError)
+
 ADD_DEFINITIONS(-DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DZLIB -DLZ4
   -DZSTD -DROCKSDB_SUPPORT_THREAD_LOCAL)
 
@@ -186,8 +191,16 @@ SET(ROCKSDB_SOURCES
 
 SET(rocksdb_static_libs ${rocksdb_static_libs} ${ZLIB_LIBRARY} regex "-lrt")
 
-MYSQL_ADD_PLUGIN(rocksdb ${ROCKSDB_SOURCES} STORAGE_ENGINE DEFAULT MODULE_ONLY
-  LINK_LIBRARIES ${rocksdb_static_libs}
+#MYSQL_ADD_PLUGIN(rocksdb ${ROCKSDB_SOURCES} STORAGE_ENGINE DEFAULT MODULE_ONLY
+#  LINK_LIBRARIES ${rocksdb_static_libs}
+#)
+
+MYSQL_ADD_PLUGIN(rocksdb_se ${ROCKSDB_SOURCES}
+  STORAGE_ENGINE
+  STATIC_ONLY
+  MANDATORY
+  MODULE_OUTPUT_NAME ha_rocksdb
+  LINK_LIBRARIES ${rocksdb_static_libs} partition_base
 )
 
 # TODO: read this file list from src.mk:TOOL_SOURCES

patch解读

1. CMakeList.txt

MYSQL_ADD_PLUGIN(rocksdb_se ${ROCKSDB_SOURCES} #引擎名取rocksdb_se,下一节说明为何不是rocksdb或其他
  STORAGE_ENGINE
  STATIC_ONLY #静态编译成librocksdb_se.a,MODULE_ONLY动态编译为ha_rocksdb.so
  MANDATORY   #此关键字也在后面说明必要性
  MODULE_OUTPUT_NAME ha_rocksdb #参考innodb和myisam的写法,未验证必要性
  LINK_LIBRARIES ${rocksdb_static_libs} partition_base #partition_base后文说明
)

2. Why not rocksdb, but rocksdb_se ?

上一节我们看到,CMakeList中我们给引擎起名为rocksdb_se而非rocksdb(参考myisam和innodb,都是直呼myisam或innodb),那么为什么rocksdb引擎要增加_se

我们尝试MYSQL_ADD_PLUGIN(rocksdb ${ROCKSDB_SOURCES} ...)的方式构建,但编译遇到错误:

... sql_builtin.cc:madatory_plugins: error: undefined reference to builtin_rocksdb_plugin

builtin_rocksdb_plugin是什么,我们到构建目录查看sql_builtin.cc一探究竟:

//in sql_builtin.cc
builtin_plugin builtin_perfschema_plugin, builtin_csv_plugin, builtin_innobase_plugin, ... //一些引擎名
struct st_mysql_plugin *mysql_optional_plugins[] = {builtin_archive_plugin, builtin_blackhole_plugin, ...} //一些引擎名,看似是optional的,不是我们关注的重点
struct st_mysql_plugin *mysql_mandatory_plugins[] = {builtin_binlog_plugin, ...,  builtin_innobase_plugin, builtin_rocksdb_plugin, ...} //一些引擎名,我们发现builtin_rocksdb_plugin“混迹其中”

mysql_mandatory_plugins是一个数组,元素是一些mandatory的引擎,和CMakeList.txt中的MANDATORY对应,我们推测此处builtin_rocksdb_plugin和CMakeList.txt中MYSQL_ADD_PLUGIN(rocksdb ...)相对应(在plugin.cmake文件所定义的MYSQL_ADD_PLUGIN宏定义得以印证)。

mysql_mandatory_plugins使用了一个未定义的元素builtin_rocksdb_plugin,那么一定其他代码声明了builtin_rocksdb_plugin,哪个文件声明了builtin_rocksdb_plugin呢?

我们在构建目录下正则匹配全部含有builtin_rocksdb_plugin关键字的文件(grep -a 'builtin_rocksdb_plugin' -R -n *),发现在librocksdb.a文件中,零零散散有builtin_rocksdb_plugin的字样(毕竟是二进制文件),同时还有builtin_rocksdb_se_plugin_interface_versionbuiltin_rocksdb_se_sizeof_struct_st_plugin这样的字眼,这些又是什么呢?看起来这些名字是非常统一,格式化的。

我们想参考下MyISAM是如何的(因为我们期待RocksDB和MyISAM效果一样地被内置进去,又不作为默认引擎,这也是不参考innodb的理由),查看libmyisam.a检索builtin_myisam_plugin,我们也发现了builtin_myisam_plugin_interface_version等字样。

于是我们可以推测,builtin_xxx_plugin, builtin_xxx_plugin_interface_version等应该是一套机制生成的,存储引擎被声明的时候经由这套机制生成builtin_xxx_plugin注册到mysql_mandatory_plugins和mysql_optional_plugins数组当中。

继续检索builtin、plugin_interface_version等字眼,我们定位到这样一个宏定义:

#define mysql_declare_plugin(NAME)                                        \
  __MYSQL_DECLARE_PLUGIN(NAME, builtin_##NAME##_plugin_interface_version, \
                         builtin_##NAME##_sizeof_struct_st_plugin,        \
                         builtin_##NAME##_plugin)

根据名字推测,这个宏定义声明了plugin,那么mysql_declare_plugin被哪里调用了?

//in ha_rocksdb.cc
mysql_declare_plugin(rocksdb_se){
    MYSQL_STORAGE_ENGINE_PLUGIN,       /* Plugin Type */
    &rocksdb_storage_engine,           /* Plugin Descriptor */
    "ROCKSDB",                         /* Plugin Name */
    ... //还有其他构造属性
} ...
 
//in ha_myisam.cc
mysql_declare_plugin(myisam){
    MYSQL_STORAGE_ENGINE_PLUGIN,
    &myisam_storage_engine,
    "MyISAM",
    ...
} ...

我们已经找到了源头,源文件在声明rocksdb作为存储引擎时,赋名为rocksdb_se,因此宏定义生成了builtin_rocksdb_se_plugin。

最后,我们在CMakeList中将库名写为rocksdb_se即可。

3. What is partition_base ?

CMakeList.txt中,我们增加了一个依赖partition_base,原因是什么呢?

解决了rocksdb_se的问题后,编译不再报错“undefined reference to builtin_rocksdb_plugin”,但有新错误产生:

undefined reference to native_part::Partition_base::Partition_base

我们在ha_rocksdb.cc中包含了partition_base.h,而且错误为undefined reference而非not declared,也就是说,头文件的声明已经获取到,源文件的函数定义未获取到。

所以,我们在CMakeList中增加一库partition_base,只包含partition_base.cc,作为rocksdb的依赖。

4. log_builtins.h?

构建报错:

init_logging_service_for_plugin is not declared in this scope
deinit_logging_service_for_plugin is not declared in this scope

然而,我们在ha_rocksdb.cc中已#include <log_builtins.h>,但为什么还会报错呢?

仔细阅读源码后,我们发现代码结构简化如下:

#if defined (MYSQL_DYNAMIC_PLUGIN) //动态链接库声明定义了init和deinit接口
declare init_logging_service_for_plugin
declare deinit_logging_service_for_plugin
#elif defined(EXTRA_CODE_FOR_UNIT_TESTING) //单元测试情况下也定义init和deinit接口
declare init_logging_service_for_plugin
declare deinit_logging_service_for_plugin
#endif //rocksdb现在既不是动态库,也不满足单元测试,于是没有声明init和deinit接口

此处因为缩进问题,困扰好久才得以发现

我们最终在rocksdb情况下(可以参考CMakeList.txt发现ROCKSDB_PLATFORM_POSIX),把这两个接口定义为空,出于:

  1. 将rocksdb与MYSQL_DYNAMIC_PLUGIN下的接口做相同定义,存在级联的仅在not declared错误
  2. myisam/innodb/blackhole等内置引擎,是没有调用init/deinit(缩写)两个接口的,所以rocksdb也应向已有内置引擎靠拢
  3. 为什么不删除rocksdb调用init/deinit的代码?a) 可能存在未知的影响,b) 也为后续可能迁回动态rocksdb链接库留有余地。

总结

在cmake、静态、动态链接上比较菜,所以问题拖了三天解决,期间得到帮助才得以解决。

不知为何,percona不将rocksdb作为内置引擎?(我们发现,增加rocksdb作为内置引擎,percona启动感觉慢了些)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335