-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Migrate LevelDB data out of ~/Documents #4609
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 48 commits
Commits
Show all changes
51 commits
Select commit
Hold shift + click to select a range
a884b14
Fix use after move in LevelDbPersistenceForTesting
wilhuff fbbeac9
Fix size-related errors when compiling in 32-bit mode
wilhuff bf038ac
Fix test-only memory leaks
wilhuff 5723728
Fix test-only memory leaks
wilhuff 577b10f
Prevent spec tests from appearing to leak
wilhuff 4b3c2ac
Avoid temporaries in methods that @throw
wilhuff ba75105
Fix memory leak in acknowledge mutation
wilhuff 6a34b23
Manually implement assignment operators in ByteString
wilhuff 788885e
Avoid racing with enqueued operations
wilhuff f5f7980
Prevent leaking GrpcCompletions that complete after Shutdown
wilhuff f73e5d9
Review feedback
wilhuff d960df5
Rework GrpcCompletion ownership
wilhuff a7d2604
Remove unused -[FIRFirestore isLoggingEnabled]
wilhuff e227971
Remove objc_compatibility.h
wilhuff 7cb336e
Remove/cleanup unused #if __OBJC__ blocks
wilhuff a27ec5e
Remove dead code
wilhuff fc8cd0d
Tidy up FIRFirestore
wilhuff 21dd819
Remove MakeNSStringNoCopy
wilhuff d52e152
Remove worthless include
wilhuff 4819635
Remove dead code and use forward declarations in FSTHelpers
wilhuff 265ba25
Remove Comparator<NSString*>
wilhuff 41aa0f2
Fix typos
wilhuff f588e39
Minor fixes for LevelDbTransaction:
wilhuff 31fd374
Move status_testing.h into testutil
wilhuff 2b574dd
Pull AppDataDirectory into LevelDbOpener
wilhuff 0169a02
Move StorageDirectory into LevelDbOpener and reorder arguments.
wilhuff 7af8a53
Add util::LegacyDocumentsDir
wilhuff dcad811
Factor out common filesystem testing utilities
wilhuff 479cfd1
Add Filesystem functions IsEmptDir and Rename
wilhuff c4b1bee
Migrate data from older nonstandard locations to better ones.
wilhuff 0e29334
Hide enable_shared_from_this weirdness
wilhuff 42f32ce
Review feedback
wilhuff 029769d
Merge branch 'master' into wilhuff/00-fixes
wilhuff 2821f72
Merge branch 'wilhuff/00-fixes' into wilhuff/01-cleanup
wilhuff 879febe
Empty commit
wilhuff 00f8a0d
Merge branch 'master' into wilhuff/01-cleanup
wilhuff 3eeb1f0
Merge branch 'wilhuff/01-cleanup' into wilhuff/02-leveldb-migration
wilhuff 6d1d3ca
Merge branch 'master' into wilhuff/02-leveldb-migration
wilhuff 7ad7363
Review feedback
wilhuff 98ad0e4
2020
wilhuff b5d2cfe
Fix build on Linux
wilhuff c05c104
Implement Filesystem::Rename on Windows
wilhuff ad3c7d3
style.sh generated changes
wilhuff 6783f11
Update changelog
wilhuff 64fec35
Review feedback 2
wilhuff 25cfc85
Rework PrepareDataDir to use a helper
wilhuff cc38a11
Update changelog
wilhuff c77c5d1
Mostly revert "Rework PrepareDataDir to use a helper"
wilhuff c7e27b5
Fix build on tvOS
wilhuff 73c8360
Use the correct target conditionals for tests
wilhuff c77d07c
Merge branch 'master' into wilhuff/02-leveldb-migration
wilhuff File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
246 changes: 246 additions & 0 deletions
246
Firestore/core/src/firebase/firestore/local/leveldb_opener.cc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,246 @@ | ||
/* | ||
* Copyright 2020 Google | ||
* | ||
* 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 | ||
* | ||
* http://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. | ||
*/ | ||
|
||
#include "Firestore/core/src/firebase/firestore/local/leveldb_opener.h" | ||
|
||
#include <string> | ||
#include <utility> | ||
|
||
#include "Firestore/core/src/firebase/firestore/core/database_info.h" | ||
#include "Firestore/core/src/firebase/firestore/local/leveldb_persistence.h" | ||
#include "Firestore/core/src/firebase/firestore/local/local_serializer.h" | ||
#include "Firestore/core/src/firebase/firestore/remote/serializer.h" | ||
#include "Firestore/core/src/firebase/firestore/util/filesystem.h" | ||
#include "Firestore/core/src/firebase/firestore/util/log.h" | ||
#include "Firestore/core/src/firebase/firestore/util/path.h" | ||
#include "Firestore/core/src/firebase/firestore/util/statusor.h" | ||
#include "Firestore/core/src/firebase/firestore/util/string_format.h" | ||
#include "absl/strings/match.h" | ||
|
||
namespace firebase { | ||
namespace firestore { | ||
namespace local { | ||
namespace { | ||
|
||
using core::DatabaseInfo; | ||
using remote::Serializer; | ||
using util::Filesystem; | ||
using util::Path; | ||
using util::Status; | ||
using util::StatusOr; | ||
using util::StringFormat; | ||
|
||
constexpr const char* kReservedPathComponent = "firestore"; | ||
|
||
Status FromCause(const std::string& message, const Status& cause) { | ||
if (cause.ok()) return cause; | ||
|
||
return Status(cause.code(), message).CausedBy(cause); | ||
} | ||
|
||
} // namespace | ||
|
||
LevelDbOpener::LevelDbOpener(DatabaseInfo database_info, Filesystem* fs) | ||
: database_info_(std::move(database_info)), | ||
fs_(fs ? fs : Filesystem::Default()) { | ||
} | ||
|
||
LevelDbOpener::LevelDbOpener(DatabaseInfo database_info, | ||
Path firestore_app_data_dir) | ||
: database_info_(std::move(database_info)), | ||
app_data_dir_(std::move(firestore_app_data_dir)), | ||
fs_(Filesystem::Default()) { | ||
} | ||
|
||
util::StatusOr<std::unique_ptr<LevelDbPersistence>> LevelDbOpener::Create( | ||
const LruParams& lru_params) { | ||
auto maybe_dir = PrepareDataDir(); | ||
if (!maybe_dir.ok()) return maybe_dir.status(); | ||
Path db_data_dir = maybe_dir.ValueOrDie(); | ||
|
||
LOG_DEBUG("Using %s for LevelDB storage", db_data_dir.ToUtf8String()); | ||
|
||
Serializer remote_serializer(database_info_.database_id()); | ||
LocalSerializer local_serializer(std::move(remote_serializer)); | ||
|
||
return LevelDbPersistence::Create(db_data_dir, std::move(local_serializer), | ||
lru_params); | ||
} | ||
|
||
StatusOr<Path> LevelDbOpener::LevelDbDataDir() { | ||
StatusOr<Path> maybe_dir = FirestoreAppDataDir(); | ||
if (!maybe_dir.ok()) return maybe_dir; | ||
return StorageDir(maybe_dir.ValueOrDie()); | ||
} | ||
|
||
StatusOr<Path> LevelDbOpener::PrepareDataDir() { | ||
StatusOr<Path> maybe_dir = LevelDbDataDir(); | ||
if (!maybe_dir.ok()) return maybe_dir; | ||
Path db_data_dir = std::move(maybe_dir).ValueOrDie(); | ||
|
||
// Check for the preferred location. If it exists, we're done. | ||
Status dir_status = fs_->IsDirectory(db_data_dir); | ||
if (dir_status.ok()) { | ||
return db_data_dir; | ||
} else if (dir_status.code() != Error::NotFound) { | ||
return dir_status; | ||
} | ||
|
||
// The preferred dir doesn't exist so check for the legacy location. If it | ||
// exists, migrate. | ||
maybe_dir = FirestoreLegacyAppDataDir(); | ||
mikelehen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Path legacy_db_data_dir; | ||
if (maybe_dir.ok()) { | ||
legacy_db_data_dir = StorageDir(std::move(maybe_dir).ValueOrDie()); | ||
dir_status = fs_->IsDirectory(legacy_db_data_dir); | ||
} else { | ||
dir_status = maybe_dir.status(); | ||
} | ||
|
||
if (dir_status.ok()) { | ||
// The legacy directory does exist, so migrate | ||
return MigrateDataDir(legacy_db_data_dir, db_data_dir); | ||
|
||
} else if (dir_status.code() != Error::NotFound && | ||
dir_status.code() != Error::Unimplemented) { | ||
return dir_status; | ||
} | ||
|
||
// Either we couldn't find the legacy directory or this platform has no legacy | ||
// directory so create the new directory. | ||
Status created = fs_->RecursivelyCreateDir(db_data_dir); | ||
if (!created.ok()) { | ||
std::string message = | ||
StringFormat("Could not create LevelDB data directory %s", | ||
db_data_dir.ToUtf8String()); | ||
|
||
return FromCause(message, created); | ||
} | ||
|
||
return db_data_dir; | ||
} | ||
|
||
StatusOr<Path> LevelDbOpener::FirestoreAppDataDir() { | ||
if (app_data_dir_.empty()) { | ||
auto maybe_dir = fs_->AppDataDir(kReservedPathComponent); | ||
if (!maybe_dir.ok()) { | ||
return FromCause( | ||
"Failed to find the App data directory for the current user", | ||
maybe_dir.status()); | ||
} | ||
app_data_dir_ = std::move(maybe_dir).ValueOrDie(); | ||
} | ||
return app_data_dir_; | ||
} | ||
|
||
StatusOr<Path> LevelDbOpener::FirestoreLegacyAppDataDir() { | ||
if (legacy_app_data_dir_.empty()) { | ||
auto maybe_dir = fs_->LegacyDocumentsDir(kReservedPathComponent); | ||
if (!maybe_dir.ok()) { | ||
return FromCause( | ||
"Failed to find the Documents directory for the current user", | ||
maybe_dir.status()); | ||
} | ||
legacy_app_data_dir_ = std::move(maybe_dir).ValueOrDie(); | ||
} | ||
return legacy_app_data_dir_; | ||
} | ||
|
||
Path LevelDbOpener::StorageDir(const Path& base_path) { | ||
// Use two different path formats: | ||
// | ||
// * persistence_key / project_id . database_id / name | ||
// * persistence_key / project_id / name | ||
// | ||
// project_ids are DNS-compatible names and cannot contain dots so there's | ||
// no danger of collisions. | ||
std::string project_key = database_info_.database_id().project_id(); | ||
if (!database_info_.database_id().IsDefaultDatabase()) { | ||
absl::StrAppend(&project_key, ".", | ||
database_info_.database_id().database_id()); | ||
} | ||
|
||
// Reserve one additional path component to allow multiple physical databases | ||
return Path::JoinUtf8(base_path, database_info_.persistence_key(), | ||
project_key, "main"); | ||
} | ||
|
||
StatusOr<Path> LevelDbOpener::MigrateDataDir( | ||
const firebase::firestore::util::Path& legacy_db_data_dir, | ||
const firebase::firestore::util::Path& db_data_dir) { | ||
// At this point the legacy location exists and the preferred location doesn't | ||
// so just move into place. | ||
LOG_DEBUG( | ||
"Migrating LevelDB storage from legacy location: %s\nMigrating to: %s", | ||
legacy_db_data_dir.ToUtf8String(), db_data_dir.ToUtf8String()); | ||
|
||
Path db_data_parent = db_data_dir.Dirname(); | ||
Status created = fs_->RecursivelyCreateDir(db_data_parent); | ||
if (!created.ok()) { | ||
std::string message = | ||
StringFormat("Could not create LevelDB data directory %s", | ||
db_data_parent.ToUtf8String()); | ||
LOG_ERROR("Migration failed: %s. Existing data unchanged.", message); | ||
return FromCause(message, created); | ||
} | ||
|
||
Status renamed = fs_->Rename(legacy_db_data_dir, db_data_dir); | ||
if (!renamed.ok()) { | ||
std::string message = StringFormat( | ||
"Failed to migrate LevelDB data from %s to %s", | ||
legacy_db_data_dir.ToUtf8String(), db_data_dir.ToUtf8String()); | ||
LOG_ERROR("Migration failed: %s. Existing data unchanged.", message); | ||
return FromCause(message, renamed); | ||
} | ||
|
||
RecursivelyCleanupLegacyDirs(legacy_db_data_dir); | ||
return db_data_dir; | ||
} | ||
|
||
void LevelDbOpener::RecursivelyCleanupLegacyDirs(Path legacy_dir) { | ||
// The legacy_dir must be within the container_dir. | ||
HARD_ASSERT(!legacy_app_data_dir_.empty()); | ||
HARD_ASSERT(absl::StartsWith(legacy_dir.ToUtf8String(), | ||
legacy_app_data_dir_.ToUtf8String())); | ||
|
||
// The container directory contains a trailing "firestore" component | ||
HARD_ASSERT(absl::EndsWith(legacy_app_data_dir_.ToUtf8String(), | ||
kReservedPathComponent)); | ||
|
||
Path parent_most = legacy_app_data_dir_.Dirname(); | ||
for (; legacy_dir != parent_most; legacy_dir = legacy_dir.Dirname()) { | ||
mikelehen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Status is_dir = fs_->IsDirectory(legacy_dir); | ||
if (is_dir.ok()) { | ||
if (util::IsEmptyDir(legacy_dir)) { | ||
Status removed = fs_->RemoveDir(legacy_dir); | ||
if (!removed.ok()) { | ||
LOG_WARN("Could not remove directory %s: %s", | ||
legacy_dir.ToUtf8String(), removed.ToString()); | ||
break; | ||
} | ||
} | ||
|
||
} else if (is_dir.code() != Error::NotFound) { | ||
LOG_WARN("Could not remove directory %s: %s", legacy_dir.ToUtf8String(), | ||
is_dir.ToString()); | ||
break; | ||
} | ||
} | ||
} | ||
|
||
} // namespace local | ||
} // namespace firestore | ||
} // namespace firebase |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unused?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one is required because the compiler needs a declaration for the type to know that it's a subclass of
Persistence
and therefore assignable to thepersistence_
member.