#include "gtest/gtest.h" // #include "leveldb/env.h" // #include "leveldb/db.h" #include "fielddb/field_db.h" #include #include "helper.h" using namespace fielddb; constexpr int value_size = 2048; constexpr int data_size = 128 << 20; #define AGE_RANGE 100 std::vector cities = { "Beijing", "Shanghai", "Guangzhou", "Shenzhen", "Hangzhou", "Chengdu", "Chongqing", "Wuhan", "Suzhou", "Tianjin" }; //检查insert和queryByIndex的数据是否对应 //封装了一个线程安全的全局set ThreadSafeSet shanghaiKeys; ThreadSafeSet age20Keys; //复杂的测试要注意这两个全局变量, //目前只有InsertFieldData和InsertOneField和writeFieldData会往里加, //DeleteFieldData和InsertOneField会删除, //其他测试之间有必要手动clear const WriteOptions op; Status OpenDB(std::string dbName, FieldDB **db) { Options options; options.create_if_missing = true; return FieldDB::OpenFieldDB(options, dbName, db); } // void ClearDB(FieldDB *db){ // //destroy和恢复没做前先用这个清理数据库,否则跑不同的数据多做几次测试会污染 // WriteOptions writeOptions; // int key_num = data_size / value_size; // for (int i = 0; i < key_num; i++) { // int key_ = i+1; // std::string key = std::to_string(key_); // Status s = db->Delete(WriteOptions(), key); // ASSERT_TRUE(s.ok()); // } // } //只插一条特定数据的测试 void InsertOneField(FieldDB *db, std::string key = "0") { WriteOptions writeOptions; FieldArray fields = { {"name", "special#" + key}, {"address", "Shanghai"}, {"age", "20"} }; Status s = db->PutFields(WriteOptions(), key, fields); ASSERT_TRUE(s.ok()); shanghaiKeys.insert(key); age20Keys.insert(key); } //只删一条特定数据的测试 void DeleteOneField(FieldDB *db, std::string key = "0") { WriteOptions writeOptions; Status s = db->Delete(WriteOptions(), key); ASSERT_TRUE(s.ok()); shanghaiKeys.erase(key); age20Keys.erase(key); } //与上面对应 void GetOneField(FieldDB *db, std::string key = "0") { ReadOptions readOptions; FieldArray fields_ret; Status s = db->GetFields(readOptions, key, &fields_ret); ASSERT_TRUE(s.ok()); for (const Field& pairs : fields_ret) { if (pairs.first == "name"){ ASSERT_EQ(pairs.second, "special#" + key); } else if (pairs.first == "address"){ ASSERT_EQ(pairs.second, "Shanghai"); } else if (pairs.first == "age"){ ASSERT_EQ(pairs.second, "20"); } else assert(false); } } void InsertFieldData(FieldDB *db, int seed = 0/*随机种子*/) { std::cout << "-------inserting-------" << std::endl; // size_t writeSize = 0; WriteOptions writeOptions; int key_num = data_size / value_size; // srand线程不安全,这种可以保证多线程时随机序列也一致 std::mt19937 rng(seed); for (int i = 0; i < key_num; i++) { int randThisTime = rng(); //确保读写一个循环只rand一次,否则随机序列会不一致 //让批量写入的key>0, 单独写入的key<=0,方便测试观察 int key_ = std::abs(randThisTime) % key_num + 1; std::string key = std::to_string(key_); // std::string key = std::to_string(seed*key_num+i+1); std::string name = "customer#" + std::to_string(key_); std::string address = cities[randThisTime % cities.size()]; std::string age = std::to_string(std::abs(randThisTime) % AGE_RANGE); FieldArray fields = { {"name", name}, {"address", address}, {"age", age} }; if (address == "Shanghai") { shanghaiKeys.insert(key); } if (age == "20") { age20Keys.insert(key); } Status s = db->PutFields(WriteOptions(), key, fields); ASSERT_TRUE(s.ok()); // writeSize += key.size() + SerializeValue(fields).size(); } // std::cout << writeSize << std::endl; } void DeleteFieldData(FieldDB *db, int seed = 0/*随机种子*/) { std::cout << "-------deleting-------" << std::endl; WriteOptions writeOptions; int key_num = data_size / value_size; // srand线程不安全,这种可以保证多线程时随机序列也一致 std::mt19937 rng(seed); shanghaiKeys.clear(); age20Keys.clear(); for (int i = 0; i < key_num; i++) { int randThisTime = rng(); //确保读写一个循环只rand一次,否则随机序列会不一致 int key_ = std::abs(randThisTime) % key_num + 1; std::string key = std::to_string(key_); Status s = db->Delete(WriteOptions(), key); ASSERT_TRUE(s.ok()); } } void WriteFieldData(FieldDB *db, int seed = 0/*随机种子*/) { std::cout << "-------writing-------" << std::endl; WriteOptions writeOptions; int key_num = data_size / value_size; // srand线程不安全,这种可以保证多线程时随机序列也一致 std::mt19937 rng(seed); WriteBatch wb; for (int i = 0; i < key_num; i++) { int randThisTime = rng(); //确保读写一个循环只rand一次,否则随机序列会不一致 //让批量写入的key>0, 单独写入的key<=0,方便测试观察 int key_ = std::abs(randThisTime) % key_num + 1; std::string key = std::to_string(key_); std::string name = "customer#" + std::to_string(key_); std::string address = cities[randThisTime % cities.size()]; std::string age = std::to_string(std::abs(randThisTime) % AGE_RANGE); FieldArray fields = { {"name", name}, {"address", address}, {"age", age} }; if (address == "Shanghai") { shanghaiKeys.insert(key); } if (age == "20") { age20Keys.insert(key); } wb.Put(key, SerializeValue(fields)); } Status s = db->Write(writeOptions, &wb); ASSERT_TRUE(s.ok()); } //并发时不一定能读到,加个参数控制 void GetFieldData(FieldDB *db, bool allowNotFound, int seed = 0) { std::cout << "-------getting-------" << std::endl; ReadOptions readOptions; int key_num = data_size / value_size; // 点查 std::mt19937 rng(seed); for (int i = 0; i < 100; i++) { int randThisTime = rng(); int key_ = std::abs(randThisTime) % key_num + 1; std::string key = std::to_string(key_); FieldArray fields_ret; Status s = db->GetFields(readOptions, key, &fields_ret); if (!allowNotFound){ //必须读到 // if (!s.ok()){ // std::cout << key << std::endl; // } ASSERT_TRUE(s.ok()); } else { //不必须读到,但只要读到address必须正确 if(s.IsNotFound()) continue; } for (const Field& pairs : fields_ret) { if (pairs.first == "name"){ } else if (pairs.first == "address"){ std::string city = pairs.second; ASSERT_NE(std::find(cities.begin(), cities.end(), city), cities.end()); } else if (pairs.first == "age"){ int age = std::stoi(pairs.second); ASSERT_TRUE(age >= 0 && age < AGE_RANGE); } else assert(false); } } } //检查对应种子有没有删除干净 //删除期间即使其他种子也不能并发写,因为即使种子不同,随机出的key可能相同 void GetDeleteData(FieldDB *db, int seed = 0) { std::cout << "-------getting-------" << std::endl; ReadOptions readOptions; int key_num = data_size / value_size; std::mt19937 rng(seed); for (int i = 0; i < 100; i++) { int randThisTime = rng(); int key_ = std::abs(randThisTime) % key_num + 1; std::string key = std::to_string(key_); FieldArray fields_ret; Status s = db->GetFields(readOptions, key, &fields_ret); ASSERT_TRUE(s.IsNotFound()); } } void findKeysByCity(FieldDB *db) { std::cout << "-------getting field address-------" << std::endl; Field field = {"address", "Shanghai"}; std::vector resKeys = db->FindKeysByField(field); //打印比较,因为shanghaikey可能被后写入的、其他address的key覆盖,打印出的后一个数应该小于前一个数 //如果随机种子相同,每次打印出的两个数也应该相同 std::cout << "address: " << shanghaiKeys.size() << " " << resKeys.size() << std::endl; for (const std::string &key : resKeys){ ASSERT_TRUE(shanghaiKeys.haveKey(key)); } } // haveIndex表明数据库有没有该索引(address) void findKeysByCityIndex(FieldDB *db, bool haveIndex) { std::cout << "-------getting field address by index-------" << std::endl; Field field = {"address", "Shanghai"}; Status s; std::vector resKeys = db->QueryByIndex(field, &s); if (haveIndex) ASSERT_TRUE(s.ok()); else { ASSERT_TRUE(s.IsNotFound()); return; } std::cout << "address: " << shanghaiKeys.size() << " " << resKeys.size() << std::endl;//打印比较 for (const std::string &key : resKeys){ ASSERT_TRUE(shanghaiKeys.haveKey(key)); } } void findKeysByAgeIndex(FieldDB *db, bool haveIndex) { std::cout << "-------getting field age by index-------" << std::endl; Field field = {"age", "20"}; Status s; std::vector resKeys = db->QueryByIndex(field, &s); if (haveIndex) ASSERT_TRUE(s.ok()); else { ASSERT_TRUE(s.IsNotFound()); return; } std::cout << "age: " << age20Keys.size() << " " << resKeys.size() << std::endl; for (const std::string &key : resKeys){ ASSERT_TRUE(age20Keys.haveKey(key)); } } void checkDataInKVAndIndex(FieldDB *db, std::string fieldName = "address") { Field field; if (fieldName == "address") field = {"address", "Shanghai"}; else if (fieldName == "age") field = {"age", "20"}; else assert(0);//只支持这两个字段检查 Status s; std::vector resKeys1 = db->QueryByIndex(field, &s); //indexdb根据索引查到的数据 std::vector resKeys2 = db->FindKeysByField(field); //kvdb强行遍历查到的数据 std::sort(resKeys1.begin(), resKeys1.end()); std::sort(resKeys2.begin(), resKeys2.end()); std::cout << resKeys1.size() << " " << resKeys2.size() << std::endl; ASSERT_EQ(resKeys1, resKeys2); }