-
Notifications
You must be signed in to change notification settings - Fork 6
Migrate Complex Open DB Code
mytrygithub edited this page Jul 10, 2023
·
11 revisions
在很多场景下,使用 RocksDB 的老代码,其 Open DB 的代码非常复杂,甚至可能有一套管理 DB/ColumnFamily 的框架(例如 flink 的 rocksdb state backend)。
要把这样的项目迁移到 ToplingDB,并使用 SidePluginRepo 来配置和管理 DB/ColumnFamily 对象,需要的改动会比较多,所以,为了降低这种代码迁移成本,我们设计了一个新的方案。
这个功能很久以前已经在 SidePluginRepo 有初步支持了,但是当初觉得这是一个鸡肋功能,并未进行深入的设计考虑,现在,我们对此进行了完善和测试。
class SidePluginRepo { // 省略无关的成员
public:
// The caller should ensure DB handle's life time is longer than SidePluginRepo
void Put(const std::string& name, DB*);
void Put(const std::string& name, DB*, const std::vector<ColumnFamilyHandle*>&);
void Put(const std::string& name, DB_MultiCF*);
void Put(const std::string& name, const std::shared_ptr<Options>&);
void Put(const std::string& name, const std::shared_ptr<DBOptions>&);
void Put(const std::string& name, const std::shared_ptr<ColumnFamilyOptions>&);
bool Get(const std::string& name, std::shared_ptr<Options>*) const;
bool Get(const std::string& name, std::shared_ptr<DBOptions>*) const;
bool Get(const std::string& name, std::shared_ptr<ColumnFamilyOptions>*) const;
Auto Get(const std::string& name) const; // C++ trick: shared_ptr<DBOptions> dbo = repo["dbo"];
Auto operator[](const std::string& name) const; // C++ trick
};
class SidePluginRepo { // 省略无关的成员
public void put(String name, RocksDB db);
public void put(String name, String spec, Options opt);
public void put(String name, String spec, DBOptions dbo);
public void put(String name, String spec, ColumnFamilyOptions cfo);
// will get a clone on each call, to sync, put after modified
public ColumnFamilyOptions getCFOptions(String name);
public DBOptions getDBOptions(String name);
}
// begin new code 1
SidePluginRepo repo;
repo.ImportAutoFile(json_or_yaml_file1); // basic conf
shared_ptr<DBOptions> dbo = repo["dbo"];
shared_ptr<ColumnFamilyOptions> cfo = repo["cfo"];
// end new code 1
OldCodeUpdateDBO(dbo);
OldCodeUpdateCFO(cfo);
// maybe update "dbo" and "cfo", "basic conf" and "update conf" can be
// both present or just one of the two.
// here dbo/cfo are from old user code, we put them to repo
repo.Put("dbo", dbo);
repo.Put("cfo", cfo);
repo.ImportAutoFile(json_or_yaml_file2); // update conf
// begin old code, open db, omit error check
vector<ColumnFamilyOptions> cf_desc;
vector<ColumnFamilyHandle*> cf_handles;
Add_Many_cfo_to(cf_desc);
DB* db = nullptr;
std::string dbpath = FromSomeConf(); // DB::Open's name param is really path
DB::Open(dbpath, cf_desc, &cf_handles, &db);
// end old code
// begin new code 2
std::string dbname; // name, not path, new concept of SidePluginRepo
repo.Put(dbname, db, cf_handles);
repo.StartHttpServer();
// end new code 2
// old code ...
repo.CloseAllDB(false); // new code 3, close
一般情况,只需要这些修改,RocksDB 到 ToplingDB 的迁移就完成了。
其中,我们可以先从 json/yaml 文件导入 option,再用代码修改 option,然后再次用导入另一个 json/yaml 文件对 option 做最后的修改,这两次 ImportAutoFile,根据具体情况,可以缩减为一次。总的原则是:后面的覆盖前面的。
使用 Java,大部分与 C++ 几乎完全相同,最主要的不同是,Java 中 importAutoFile 之后必须重新用对象名向 repo 中 get 相应的对象引用:
repo.put("dbo", dbo);
repo.put("cfo", cfo);
repo.importAutoFile(json_or_yaml_file2); // update conf
dbo = repo.getDBOptions("dbo"); // update to dbo in java
cfo = repo.getCFOptions("cfo"); // update to cfo in java
另一个不同的地方是:
// C++ Code:
// shared_ptr<DBOptions> dbo = repo["dbo"];
// shared_ptr<ColumnFamilyOptions> cfo = repo["cfo"];
// Java Code:
DBOptions dbo = repo.getDBOptions("dbo");
ColumnFamilyOptions cfo = repo.getCFOptions("cfo");
完整示例:
// begin new code 1
SidePluginRepo repo = new SidePluginRepo();
repo.importAutoFile(json_or_yaml_file1); // basic conf
DBOptions dbo = repo.getDBOptions("dbo");
ColumnFamilyOptions cfo = repo.getCFOptions("cfo");
// end new code 1
oldCodeUpdateDBO(dbo);
oldCodeUpdateCFO(cfo);
// maybe update "dbo" and "cfo", "basic conf" and "update conf" can be
// both present or just one of the two.
// here dbo/cfo are from old user code, we put them to repo
repo.put("dbo", dbo);
repo.put("cfo", cfo);
repo.importAutoFile(json_or_yaml_file2); // update conf
dbo = repo.getDBOptions("dbo"); // update to dbo in java
cfo = repo.getCFOptions("cfo"); // update to cfo in java
// begin old code, open db, omit error check
List<ColumnFamilyOptions> cf_desc = new ArrayList<ColumnFamilyOptions>();
List<ColumnFamilyHandle> cf_handles = new ArrayList<ColumnFamilyHandle>();
// Add_Many_cfo_to(cf_desc); // weird, rocksdb jni cfname is byte[]
cf_desc.add(new ColumnFamilyDescriptor("cfo".getBytes(), cfo));
cf_desc.add(new ColumnFamilyDescriptor("more cfo".getBytes(), more_cfo));
String dbpath = FromSomeConf(); // DB::Open's name param is really path
RocksDB db = RocksDB.open(dbpath, cf_desc);
// end old code
// begin new code 2
String dbname; // name, not path, new concept of SidePluginRepo
repo.put(dbname, db, cf_handles);
repo.startHttpServer();
// end new code 2
// old code ...
repo.closeAllDB(); // new code 3, close
Java 代码也一样,两次 importAutoFile,根据具体情况,可以缩减为一次,主要看是用 Java 代码覆盖 json/yaml 中的同名配置项(DBOptions/ColumnFamilyOptions 的成员),还是用 json/yaml 覆盖 Java 代码中设定的同名配置项。总的原则是:后面的覆盖前面的。