diff options
author | Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com> | 2017-09-04 05:23:35 +0300 |
---|---|---|
committer | Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com> | 2017-09-04 05:23:35 +0300 |
commit | 199f94231af6c9cde101995911fab8f8efa7a441 (patch) | |
tree | ec8e81344206318bf9b8d242478b653d5b8c6590 /programs | |
parent | 22417f35fdc10142a1bb18e6ecbc5e3ae1df021b (diff) |
Enabled integrity checking in toy key value store, was fairly amazed to see it worked first time
Diffstat (limited to 'programs')
-rw-r--r-- | programs/key-value-store/Readme.md | 7 | ||||
-rw-r--r-- | programs/key-value-store/include/key_value_store.hpp | 37 | ||||
-rw-r--r-- | programs/key-value-store/main.cpp | 188 |
3 files changed, 146 insertions, 86 deletions
diff --git a/programs/key-value-store/Readme.md b/programs/key-value-store/Readme.md index bc6dfd3e..d9d81cb8 100644 --- a/programs/key-value-store/Readme.md +++ b/programs/key-value-store/Readme.md @@ -34,6 +34,13 @@ index update. Retrieving 1M key-value pairs ... Fetched at 1945525 items per sec ``` +- 1Kb values Linux with ext4, integrity, no durability: + ``` + Inserting 1M key-value pairs ... + Inserted at 581057 items per sec + Retrieving 1M key-value pairs ... + Fetched at 1519756 items per sec + ``` - 16 byte values Windows with NTFS, no integrity, no durability: ``` Inserting 1M key-value pairs ... diff --git a/programs/key-value-store/include/key_value_store.hpp b/programs/key-value-store/include/key_value_store.hpp index 111e0e04..dc83add4 100644 --- a/programs/key-value-store/include/key_value_store.hpp +++ b/programs/key-value-store/include/key_value_store.hpp @@ -186,7 +186,7 @@ namespace key_value_store afio::file_handle::mode::read #else // Linux won't allow taking an exclusive lock on a read only file - afio::file_handle::mode::write + mode #endif ; if(_smallfiles.read.empty()) @@ -239,12 +239,13 @@ namespace key_value_store throw maximum_writers_reached(); } // Set up the index, either r/w or read only with copy on write - afio::section_handle sh = afio::section_handle::section(_indexfile, 0, (mode == afio::file_handle::mode::write) ? afio::section_handle::flag::readwrite : (afio::section_handle::flag::read | afio::section_handle::flag::cow)).value(); + afio::section_handle::flag mapflags = (mode == afio::file_handle::mode::write) ? afio::section_handle::flag::readwrite : (afio::section_handle::flag::read | afio::section_handle::flag::cow); + afio::section_handle sh = afio::section_handle::section(_indexfile, 0, mapflags).value(); afio::file_handle::extent_type len = sh.length(); len -= sizeof(index::index); len /= sizeof(index::open_hash_index::value_type); size_t offset = sizeof(index::index); - _index.emplace(sh, len, offset); + _index.emplace(sh, len, offset, mapflags); _indexheader = reinterpret_cast<index::index *>((char *) _index->container().data() - offset); if(_indexheader->writes_occurring[_mysmallfileidx] != 0) { @@ -260,7 +261,7 @@ namespace key_value_store basic_key_value_store &operator=(const basic_key_value_store &) = delete; basic_key_value_store &operator=(basic_key_value_store &&) = delete; - basic_key_value_store(const afio::path_handle &dir, size_t hashtableentries, afio::file_handle::mode mode = afio::file_handle::mode::write, afio::file_handle::caching caching = afio::file_handle::caching::temporary) + basic_key_value_store(const afio::path_handle &dir, size_t hashtableentries, bool enable_integrity = false, afio::file_handle::mode mode = afio::file_handle::mode::write, afio::file_handle::caching caching = afio::file_handle::caching::temporary) : _indexfile(afio::file_handle::file(dir, "index", mode, (mode == afio::file_handle::mode::write) ? afio::file_handle::creation::if_needed : afio::file_handle::creation::open_existing, caching).value()) { if(mode == afio::file_handle::mode::write) @@ -275,8 +276,12 @@ namespace key_value_store afio::file_handle::extent_type size = sizeof(index::index) + (hashtableentries) * sizeof(index::open_hash_index::value_type); size = afio::utils::round_up_to_page_size(size); _indexfile.truncate(size).value(); - auto goodmagic = _goodmagic; - _indexfile.write(0, (const char *) &goodmagic, 8).value(); + index::index i; + memset(&i, 0, sizeof(i)); + i.magic = _goodmagic; + i.all_writes_synced = _indexfile.are_writes_durable(); + i.contents_hashed = enable_integrity; + _indexfile.write(0, (char *) &i, sizeof(i)).value(); } else { @@ -290,14 +295,14 @@ namespace key_value_store if(_indexheader->contents_hashed) { } + // Now we've finished the checks, reset writes_occurring and all_writes_synced + index::index i; + _indexfile.read(0, (char *) &i, sizeof(i)).value(); + memset(i.writes_occurring, 0, sizeof(i.writes_occurring)); + i.all_writes_synced = _indexfile.are_writes_durable(); + memset(&i.hash, 0, sizeof(i.hash)); + _indexfile.write(0, (char *) &i, sizeof(i)).value(); } - // Reset writes_occurring and all_writes_synced - index::index i; - _indexfile.read(0, (char *) &i, sizeof(i)).value(); - memset(i.writes_occurring, 0, sizeof(i.writes_occurring)); - i.all_writes_synced = _indexfile.are_writes_durable(); - memset(&i.hash, 0, sizeof(i.hash)); - _indexfile.write(0, (char *) &i, sizeof(i)).value(); } } // Take a shared lock, blocking if someone is still setting things up @@ -320,13 +325,13 @@ namespace key_value_store } } //! \overload - basic_key_value_store(const afio::path_view &dir, size_t hashtableentries, afio::file_handle::mode mode = afio::file_handle::mode::write, afio::file_handle::caching caching = afio::file_handle::caching::temporary) - : basic_key_value_store(afio::directory_handle::directory({}, dir, afio::directory_handle::mode::write, afio::directory_handle::creation::if_needed).value(), hashtableentries, mode, caching) + basic_key_value_store(const afio::path_view &dir, size_t hashtableentries, bool enable_integrity = false, afio::file_handle::mode mode = afio::file_handle::mode::write, afio::file_handle::caching caching = afio::file_handle::caching::temporary) + : basic_key_value_store(afio::directory_handle::directory({}, dir, afio::directory_handle::mode::write, afio::directory_handle::creation::if_needed).value(), hashtableentries, enable_integrity, mode, caching) { } //! Opens the store for read only access basic_key_value_store(const afio::path_view &dir) - : basic_key_value_store(afio::path_handle::path(dir).value(), 0, afio::file_handle::mode::read) + : basic_key_value_store(afio::path_handle::path(dir).value(), 0, false, afio::file_handle::mode::read) { } ~basic_key_value_store() diff --git a/programs/key-value-store/main.cpp b/programs/key-value-store/main.cpp index aa8a608f..a03059a3 100644 --- a/programs/key-value-store/main.cpp +++ b/programs/key-value-store/main.cpp @@ -24,6 +24,53 @@ Distributed under the Boost Software License, Version 1.0. #include "include/key_value_store.hpp" +void benchmark(key_value_store::basic_key_value_store &store, const char *desc) +{ + std::cout << "\n" << desc << ":" << std::endl; + // Write 1M values and see how long it takes + static std::vector<std::pair<uint64_t, std::string>> values; + if(values.empty()) + { + std::cout << " Generating 1M key-value pairs ..." << std::endl; + for(size_t n = 0; n < 1000000; n++) + { + std::string randomvalue = AFIO_V2_NAMESPACE::utils::random_string(1024 / 2); + values.push_back({100 + n, randomvalue}); + } + } + std::cout << " Inserting 1M key-value pairs ..." << std::endl; + { + auto begin = std::chrono::high_resolution_clock::now(); + for(size_t n = 0; n < values.size(); n += 1024) + { + key_value_store::transaction tr(store); + for(size_t m = 0; m < 1024; m++) + { + if(n + m >= values.size()) + break; + auto &i = values[n + m]; + tr.update_unsafe(i.first, i.second); + } + tr.commit(); + } + auto end = std::chrono::high_resolution_clock::now(); + auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count(); + std::cout << " Inserted at " << (1000000000ULL / diff) << " items per sec" << std::endl; + } + std::cout << " Retrieving 1M key-value pairs ..." << std::endl; + { + auto begin = std::chrono::high_resolution_clock::now(); + for(auto &i : values) + { + if(!store.find(i.first)) + abort(); + } + auto end = std::chrono::high_resolution_clock::now(); + auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count(); + std::cout << " Fetched at " << (1000000000ULL / diff) << " items per sec" << std::endl; + } +} + int main() { #ifdef _WIN32 @@ -35,27 +82,66 @@ int main() std::error_code ec; AFIO_V2_NAMESPACE::filesystem::remove_all("teststore", ec); } - key_value_store::basic_key_value_store store("teststore", 2000000); { - key_value_store::transaction tr(store); - tr.fetch(78); - tr.update(78, "niall"); - tr.commit(); - auto kvi = store.find(78); - if(kvi) + key_value_store::basic_key_value_store store("teststore", 10); { - std::cout << "Key 78 has value " << kvi.value << " and it was last updated at " << kvi.transaction_counter << std::endl; + key_value_store::transaction tr(store); + tr.fetch(78); + tr.update(78, "niall"); + tr.commit(); + auto kvi = store.find(78); + if(kvi) + { + std::cout << "Key 78 has value " << kvi.value << " and it was last updated at " << kvi.transaction_counter << std::endl; + } + else + { + std::cerr << "FAILURE: Key 78 was not found!" << std::endl; + } } - else { - std::cerr << "FAILURE: Key 78 was not found!" << std::endl; + key_value_store::transaction tr(store); + tr.fetch(79); + tr.update(79, "douglas"); + tr.commit(); + auto kvi = store.find(79); + if(kvi) + { + std::cout << "Key 79 has value " << kvi.value << " and it was last updated at " << kvi.transaction_counter << std::endl; + } + else + { + std::cerr << "FAILURE: Key 79 was not found!" << std::endl; + } + } + { + key_value_store::transaction tr(store); + tr.fetch(78); + tr.remove(78); + tr.commit(); + auto kvi = store.find(78, 0); + if(kvi) + { + std::cerr << "FAILURE: Revision 0 of Key 78 has value " << kvi.value << " and it was last updated at " << kvi.transaction_counter << std::endl; + } + else + { + std::cout << "Revision 0 of key 78 was not found!" << std::endl; + } + kvi = store.find(78, 1); + if(kvi) + { + std::cout << "Revision 1 of Key 78 has value " << kvi.value << " and it was last updated at " << kvi.transaction_counter << std::endl; + } + else + { + std::cerr << "FAILURE: Revision 1Key 78 was not found!" << std::endl; + } } } + // test read only { - key_value_store::transaction tr(store); - tr.fetch(79); - tr.update(79, "douglas"); - tr.commit(); + key_value_store::basic_key_value_store store("teststore"); auto kvi = store.find(79); if(kvi) { @@ -67,69 +153,31 @@ int main() } } { - key_value_store::transaction tr(store); - tr.fetch(78); - tr.remove(78); - tr.commit(); - auto kvi = store.find(78, 0); - if(kvi) - { - std::cerr << "FAILURE: Revision 0 of Key 78 has value " << kvi.value << " and it was last updated at " << kvi.transaction_counter << std::endl; - } - else - { - std::cout << "Revision 0 of key 78 was not found!" << std::endl; - } - kvi = store.find(78, 1); - if(kvi) - { - std::cout << "Revision 1 of Key 78 has value " << kvi.value << " and it was last updated at " << kvi.transaction_counter << std::endl; - } - else - { - std::cerr << "FAILURE: Revision 1Key 78 was not found!" << std::endl; - } + std::error_code ec; + AFIO_V2_NAMESPACE::filesystem::remove_all("teststore", ec); } - - // Write 1M values and see how long it takes - std::vector<std::pair<uint64_t, std::string>> values; - std::cout << "\nGenerating 1M key-value pairs ..." << std::endl; - for(size_t n = 0; n < 1000000; n++) { - std::string randomvalue = AFIO_V2_NAMESPACE::utils::random_string(1024 / 2); - values.push_back({100 + n, randomvalue}); + key_value_store::basic_key_value_store store("teststore", 2000000); + benchmark(store, "no integrity, no durability"); } - std::cout << "Inserting 1M key-value pairs ..." << std::endl; { - auto begin = std::chrono::high_resolution_clock::now(); - for(size_t n = 0; n < values.size(); n += 1024) - { - key_value_store::transaction tr(store); - for(size_t m = 0; m < 1024; m++) - { - if(n + m >= values.size()) - break; - auto &i = values[n + m]; - tr.update_unsafe(i.first, i.second); - } - tr.commit(); - } - auto end = std::chrono::high_resolution_clock::now(); - auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count(); - std::cout << "Inserted at " << (1000000000ULL / diff) << " items per sec" << std::endl; + std::error_code ec; + AFIO_V2_NAMESPACE::filesystem::remove_all("teststore", ec); } - std::cout << "Retrieving 1M key-value pairs ..." << std::endl; { - auto begin = std::chrono::high_resolution_clock::now(); - for(auto &i : values) - { - if(!store.find(i.first)) - abort(); - } - auto end = std::chrono::high_resolution_clock::now(); - auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count(); - std::cout << "Fetched at " << (1000000000ULL / diff) << " items per sec" << std::endl; + key_value_store::basic_key_value_store store("teststore", 2000000, true); + benchmark(store, "integrity, no durability"); } +#if 0 + { + std::error_code ec; + AFIO_V2_NAMESPACE::filesystem::remove_all("teststore", ec); + } + { + key_value_store::basic_key_value_store store("teststore", 2000000, true, AFIO_V2_NAMESPACE::file_handle::mode::write, AFIO_V2_NAMESPACE::file_handle::caching::reads); + benchmark(store, "integrity, durability"); + } +#endif } catch(const std::exception &e) { |