// Copyright 2022 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef THIRD_PARTY_NEARBY_INTERNAL_DATA_LEVELDB_DATA_SET_H_ #define THIRD_PARTY_NEARBY_INTERNAL_DATA_LEVELDB_DATA_SET_H_ #include #include #include #include #include #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" #include "third_party/leveldb/include/db.h" #include "third_party/leveldb/include/iterator.h" #include "third_party/leveldb/include/options.h" #include "third_party/leveldb/include/slice.h" #include "third_party/leveldb/include/status.h" #include "internal/base/file_path.h" #include "internal/data/data_set.h" #if defined(_WIN32) #include "location/nearby/apps/better_together/windows/common/leveldb_env_windows.h" #endif // defined(_WIN32) #include "internal/platform/logging.h" #include "google/protobuf/message_lite.h" namespace nearby { namespace data { // DataSet implementation using leveldb as its persistent storage. Values are // serialized and stored in leveldb databases. template ::value, bool> = true> class LeveldbDataSet : public DataSet { public: using KeyEntryVector = std::vector>; explicit LeveldbDataSet(const FilePath& db_dir) : path_(db_dir.ToString()) { #if defined(_WIN32) // In Windows use the Unicode compatible environment. db_options_.env = nearby::windows::WindowsEnv::Default(); #endif // defined(_WIN32) db_options_.create_if_missing = true; } ~LeveldbDataSet() override = default; void Initialize(absl::AnyInvocable callback) override; void LoadEntry( absl::string_view key, absl::AnyInvocable) &&> callback) override; void LoadEntries( absl::AnyInvocable>) &&> callback) override; void LoadEntriesWithKeys( absl::AnyInvocable< void(bool, std::unique_ptr>>) &&> callback); void UpdateEntries(std::unique_ptr entries_to_save, std::unique_ptr> keys_to_remove, absl::AnyInvocable callback) override; void Destroy(absl::AnyInvocable callback) override; private: void Serialize(T const& value, std::string& str); void Deserialize(absl::string_view str, T& value); private: std::string path_; leveldb::Options db_options_; std::unique_ptr db_ = nullptr; InitStatus status_ = InitStatus::kNotInitialized; }; template ::value, bool> isMessageLite> void LeveldbDataSet::Initialize( absl::AnyInvocable callback) { leveldb::DB* db; leveldb::Status status = leveldb::DB::Open(db_options_, path_, &db); db_ = std::unique_ptr(db); if (status.ok()) { status_ = InitStatus::kOK; LOG(INFO) << "Database is initialized successfully.."; } else if (status.IsCorruption() || status.IsIOError()) { status_ = InitStatus::kCorrupt; LOG(WARNING) << "Database is corrupt."; } else { status_ = InitStatus::kError; LOG(ERROR) << "Failed to initialize database due to unknown error."; } std::move(callback)(status_); } template ::value, bool> isMessageLite> void LeveldbDataSet::LoadEntries( absl::AnyInvocable>) &&> callback) { auto result = std::make_unique>(); if (status_ != InitStatus::kOK) { std::move(callback)(false, std::move(result)); return; } std::unique_ptr it( db_->NewIterator(leveldb::ReadOptions())); for (it->SeekToFirst(); it->Valid(); it->Next()) { T value; Deserialize(it->value().ToString(), value); result->push_back(value); } if (it->status().ok()) { VLOG(1) << "Loaded " << result->size() << " entries from database."; std::move(callback)(true, std::move(result)); } else { LOG(ERROR) << "Failed to load entries from database."; result->clear(); std::move(callback)(false, std::move(result)); } } template ::value, bool> isMessageLite> void LeveldbDataSet::LoadEntry( absl::string_view key, absl::AnyInvocable) &&> callback) { auto result = std::make_unique(); if (status_ != InitStatus::kOK) { std::move(callback)(false, std::move(result)); return; } std::string value; if (!db_->Get(leveldb::ReadOptions(), std::string(key), &value).ok()) { LOG(WARNING) << "Failed to load entry from database with key: " << key; std::move(callback)(false, std::move(result)); return; } Deserialize(value, *result); std::move(callback)(true, std::move(result)); } template ::value, bool> isMessageLite> void LeveldbDataSet::LoadEntriesWithKeys( absl::AnyInvocable< void(bool, std::unique_ptr>>) &&> callback) { auto result = std::make_unique>>(); if (status_ != InitStatus::kOK) { std::move(callback)(false, std::move(result)); return; } std::unique_ptr it( db_->NewIterator(leveldb::ReadOptions())); for (it->SeekToFirst(); it->Valid(); it->Next()) { T value; Deserialize(it->value().ToString(), value); result->push_back({it->key().ToString(), value}); } if (it->status().ok()) { VLOG(1) << "Loaded " << result->size() << " entries from database."; std::move(callback)(true, std::move(result)); } else { LOG(WARNING) << "Failed to load entries from database."; result->clear(); std::move(callback)(false, std::move(result)); } } template ::value, bool> isMessageLite> void LeveldbDataSet::UpdateEntries( std::unique_ptr entries_to_save, std::unique_ptr> keys_to_remove, absl::AnyInvocable callback) { VLOG(1) << "UpdateEntries is called."; if (status_ != InitStatus::kOK) { std::move(callback)(false); return; } if (entries_to_save != nullptr) { for (const auto& [key, value] : *entries_to_save) { std::string str; Serialize(value, str); db_->Put(leveldb::WriteOptions(), key, leveldb::Slice(str)); } } if (keys_to_remove != nullptr) { for (const auto& it : *keys_to_remove) { db_->Delete(leveldb::WriteOptions(), it); } } std::move(callback)(true); } template ::value, bool> isMessageLite> void LeveldbDataSet::Destroy( absl::AnyInvocable callback) { VLOG(1) << "Destroy is called."; db_.reset(); leveldb::DestroyDB(path_, db_options_); std::move(callback)(true); } // Functions for serializing/deserializing data values to/from strings. Strings // are used as a convenient container that manages its memory. They don't need // to be human-readable. template ::value, bool> isMessageLite> void LeveldbDataSet::Serialize(T const& value, std::string& str) { value.SerializeToString(&str); } template ::value, bool> isMessageLite> void LeveldbDataSet::Deserialize(absl::string_view str, T& value) { value.ParseFromString(str); } } // namespace data } // namespace nearby #endif // THIRD_PARTY_NEARBY_INTERNAL_DATA_LEVELDB_DATA_SET_H_