metadata_database.h revision 116680a4aac90f2aa7413d9095a592090648e557
1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#ifndef CHROME_BROWSER_SYNC_FILE_SYSTEM_DRIVE_BACKEND_METADATA_DATABASE_H_ 6#define CHROME_BROWSER_SYNC_FILE_SYSTEM_DRIVE_BACKEND_METADATA_DATABASE_H_ 7 8#include <map> 9#include <set> 10#include <string> 11#include <vector> 12 13#include "base/callback_forward.h" 14#include "base/containers/hash_tables.h" 15#include "base/containers/scoped_ptr_hash_map.h" 16#include "base/memory/scoped_ptr.h" 17#include "base/memory/scoped_vector.h" 18#include "base/memory/weak_ptr.h" 19#include "base/sequence_checker.h" 20#include "base/values.h" 21#include "chrome/browser/sync_file_system/drive_backend/tracker_id_set.h" 22#include "chrome/browser/sync_file_system/sync_callbacks.h" 23#include "chrome/browser/sync_file_system/sync_status_code.h" 24 25namespace base { 26class FilePath; 27class SequencedTaskRunner; 28class SingleThreadTaskRunner; 29} 30 31namespace leveldb { 32class DB; 33class Env; 34class WriteBatch; 35} 36 37namespace google_apis { 38class ChangeResource; 39class FileResource; 40class ResourceEntry; 41} 42 43namespace tracked_objects { 44class Location; 45} 46 47namespace sync_file_system { 48namespace drive_backend { 49 50class FileDetails; 51class FileMetadata; 52class FileTracker; 53class MetadataDatabaseIndexInterface; 54class ServiceMetadata; 55 56// MetadataDatabase holds and maintains a LevelDB instance and its indexes, 57// which holds 1)ServiceMetadata, 2)FileMetadata and 3)FileTracker. 58// 1) ServiceMetadata is a singleton in the database which holds information for 59// the backend. 60// 2) FileMetadata represents a remote-side file and holds latest known 61// metadata of the remote file. 62// 3) FileTracker represents a synced or to-be-synced file and maintains 63// the local-side folder tree. 64// 65// The term "file" includes files, folders and other resources on Drive. 66// 67// FileTrackers form a tree structure on the database, which represents the 68// FileSystem trees of SyncFileSystem. The tree has a FileTracker named 69// sync-root as its root node, and a set of FileTracker named app-root. An 70// app-root represents a remote folder for an installed Chrome App and holds all 71// synced contents for the App. 72// 73// One FileMetadata is created for each tracked remote file, which is identified 74// by FileID. 75// One FileTracker is created for every different {parent tracker, FileID} pair, 76// excluding non-app-root inactive parent trackers. Multiple trackers may be 77// associated to one FileID when the file has multiple parents. Multiple 78// trackers may have the same {parent tracker, title} pair when the associated 79// remote files have the same title. 80// 81// Files have following state: 82// - Unknown file 83// - Has a dirty inactive tracker and empty synced_details. 84// - Is initial state of a tracker, only file_id and parent_tracker_id field 85// are known. 86// - Folder 87// - Is either one of sync-root folder, app-root folder or a regular folder. 88// - Sync-root folder holds app-root folders as its direct children, and 89// holds entire SyncFileSystem files as its descentants. Its tracker 90// should be stored in ServiceMetadata by its tracker_id. 91// - App-root folder holds all files for an application as its descendants. 92// - File 93// - Unsupported file 94// - Represents unsupported files such as hosted documents. Must be 95// inactive. 96// 97// Invariants: 98// - Any tracker in the database must either: 99// - be sync-root, 100// - have an app-root as its parent tracker, or 101// - have an active tracker as its parent. 102// That is, all trackers must be reachable from sync-root via app-root folders 103// and active trackers. 104// 105// - Any active tracker must either: 106// - have |needs_folder_listing| flag and dirty flag, or 107// - have all children at the stored largest change ID. 108// 109// - If multiple trackers have the same parent tracker and same title, they 110// must not have same |file_id|, and at most one of them may be active. 111// - If multiple trackers have the same |file_id|, at most one of them may be 112// active. 113// 114class MetadataDatabase { 115 public: 116 typedef std::vector<std::string> FileIDList; 117 118 typedef base::Callback< 119 void(SyncStatusCode status, scoped_ptr<MetadataDatabase> instance)> 120 CreateCallback; 121 122 enum ActivationStatus { 123 ACTIVATION_PENDING, 124 ACTIVATION_FAILED_ANOTHER_ACTIVE_TRACKER, 125 }; 126 127 enum UpdateOption { 128 UPDATE_TRACKER_FOR_UNSYNCED_FILE, 129 UPDATE_TRACKER_FOR_SYNCED_FILE, 130 }; 131 132 // The entry point of the MetadataDatabase for production code. 133 // If |env_override| is non-NULL, internal LevelDB uses |env_override| instead 134 // of leveldb::Env::Default(). Use leveldb::MemEnv in test code for faster 135 // testing. 136 static void Create(base::SequencedTaskRunner* worker_task_runner, 137 base::SequencedTaskRunner* file_task_runner, 138 const base::FilePath& database_path, 139 leveldb::Env* env_override, 140 const CreateCallback& callback); 141 static SyncStatusCode CreateForTesting( 142 scoped_ptr<leveldb::DB> db, 143 scoped_ptr<MetadataDatabase>* metadata_database_out); 144 145 ~MetadataDatabase(); 146 147 static void ClearDatabase(scoped_ptr<MetadataDatabase> metadata_database); 148 149 int64 GetLargestFetchedChangeID() const; 150 int64 GetSyncRootTrackerID() const; 151 bool HasSyncRoot() const; 152 153 // Returns all file metadata for the given |app_id|. 154 scoped_ptr<base::ListValue> DumpFiles(const std::string& app_id); 155 156 // Returns all database data. 157 scoped_ptr<base::ListValue> DumpDatabase(); 158 159 // TODO(tzik): Move GetLargestKnownChangeID() to private section, and hide its 160 // handling in the class, instead of letting user do. 161 // 162 // Gets / updates the largest known change ID. 163 // The largest known change ID is on-memory and not persist over restart. 164 // This is supposed to use when a task fetches ChangeList in parallel to other 165 // operation. When a task starts fetching paged ChangeList one by one, it 166 // should update the largest known change ID on the first round and background 167 // remaining fetch job. 168 // Then, when other tasks that update FileMetadata by UpdateByFileResource, 169 // it should use largest known change ID as the |change_id| that prevents 170 // FileMetadata from overwritten by ChangeList. 171 // Also if other tasks try to update a remote resource whose change is not yet 172 // retrieved the task should fail due to etag check, so we should be fine. 173 int64 GetLargestKnownChangeID() const; 174 void UpdateLargestKnownChangeID(int64 change_id); 175 176 // Populates empty database with initial data. 177 // Adds a file metadata and a file tracker for |sync_root_folder|, and adds 178 // file metadata and file trackers for each |app_root_folders|. 179 // Newly added tracker for |sync_root_folder| is active and non-dirty. 180 // Newly added trackers for |app_root_folders| are inactive and non-dirty. 181 // Trackers for |app_root_folders| are not yet registered as app-roots, but 182 // are ready to register. 183 void PopulateInitialData( 184 int64 largest_change_id, 185 const google_apis::FileResource& sync_root_folder, 186 const ScopedVector<google_apis::FileResource>& app_root_folders, 187 const SyncStatusCallback& callback); 188 189 // Returns true if the folder associated to |app_id| is enabled. 190 bool IsAppEnabled(const std::string& app_id) const; 191 192 // Registers existing folder as the app-root for |app_id|. The folder 193 // must be an inactive folder that does not yet associated to any App. 194 // This method associates the folder with |app_id| and activates it. 195 void RegisterApp(const std::string& app_id, 196 const std::string& folder_id, 197 const SyncStatusCallback& callback); 198 199 // Inactivates the folder associated to the app to disable |app_id|. 200 // Does nothing if |app_id| is already disabled. 201 void DisableApp(const std::string& app_id, 202 const SyncStatusCallback& callback); 203 204 // Activates the folder associated to |app_id| to enable |app_id|. 205 // Does nothing if |app_id| is already enabled. 206 void EnableApp(const std::string& app_id, 207 const SyncStatusCallback& callback); 208 209 // Unregisters the folder as the app-root for |app_id|. If |app_id| does not 210 // exist, does nothing. The folder is left as an inactive regular folder. 211 // Note that the inactivation drops all descendant files since they are no 212 // longer reachable from sync-root via active folder or app-root. 213 void UnregisterApp(const std::string& app_id, 214 const SyncStatusCallback& callback); 215 216 // Finds the app-root folder for |app_id|. Returns true if exists. 217 // Copies the result to |tracker| if it is non-NULL. 218 bool FindAppRootTracker(const std::string& app_id, 219 FileTracker* tracker) const; 220 221 // Finds the file identified by |file_id|. Returns true if the file is found. 222 // Copies the metadata identified by |file_id| into |file| if exists and 223 // |file| is non-NULL. 224 bool FindFileByFileID(const std::string& file_id, FileMetadata* file) const; 225 226 // Finds the tracker identified by |tracker_id|. Returns true if the tracker 227 // is found. 228 // Copies the tracker identified by |tracker_id| into |tracker| if exists and 229 // |tracker| is non-NULL. 230 bool FindTrackerByTrackerID(int64 tracker_id, FileTracker* tracker) const; 231 232 // Finds the trackers tracking |file_id|. Returns true if the trackers are 233 // found. 234 bool FindTrackersByFileID(const std::string& file_id, 235 TrackerIDSet* trackers) const; 236 237 // Finds the set of trackers whose parent's tracker ID is |parent_tracker_id|, 238 // and who has |title| as its title in the synced_details. 239 // Copies the tracker set to |trackers| if it is non-NULL. 240 // Returns true if the trackers are found. 241 bool FindTrackersByParentAndTitle( 242 int64 parent_tracker_id, 243 const std::string& title, 244 TrackerIDSet* trackers) const; 245 246 // Builds the file path for the given tracker. Returns true on success. 247 // |path| can be NULL. 248 // The file path is relative to app-root and have a leading path separator. 249 bool BuildPathForTracker(int64 tracker_id, base::FilePath* path) const; 250 251 // Builds the file path for the given tracker for display purpose. 252 // This may return a path ending with '<unknown>' if the given tracker does 253 // not have title information (yet). This may return an empty path. 254 base::FilePath BuildDisplayPathForTracker(const FileTracker& tracker) const; 255 256 // Returns false if no registered app exists associated to |app_id|. 257 // If |full_path| is active, assigns the tracker of |full_path| to |tracker|. 258 // Otherwise, assigns the nearest active ancestor to |full_path| to |tracker|. 259 // Also, assigns the full path of |tracker| to |path|. 260 bool FindNearestActiveAncestor(const std::string& app_id, 261 const base::FilePath& full_path, 262 FileTracker* tracker, 263 base::FilePath* path) const; 264 265 // Updates database by |changes|. 266 // Marks each tracker for modified file as dirty and adds new trackers if 267 // needed. 268 void UpdateByChangeList(int64 largest_change_id, 269 ScopedVector<google_apis::ChangeResource> changes, 270 const SyncStatusCallback& callback); 271 272 // Updates database by |resource|. 273 // Marks each tracker for modified file as dirty and adds new trackers if 274 // needed. 275 void UpdateByFileResource(const google_apis::FileResource& resource, 276 const SyncStatusCallback& callback); 277 void UpdateByFileResourceList( 278 ScopedVector<google_apis::FileResource> resources, 279 const SyncStatusCallback& callback); 280 281 void UpdateByDeletedRemoteFile(const std::string& file_id, 282 const SyncStatusCallback& callback); 283 void UpdateByDeletedRemoteFileList(const FileIDList& file_ids, 284 const SyncStatusCallback& callback); 285 286 // Adds new FileTracker and FileMetadata. The database must not have 287 // |resource| beforehand. 288 // The newly added tracker under |parent_tracker_id| is active and non-dirty. 289 // Deactivates existing active tracker if exists that has the same title and 290 // parent_tracker to the newly added tracker. 291 void ReplaceActiveTrackerWithNewResource( 292 int64 parent_tracker_id, 293 const google_apis::FileResource& resource, 294 const SyncStatusCallback& callback); 295 296 // Adds |child_file_ids| to |folder_id| as its children. 297 // This method affects the active tracker only. 298 // If the tracker has no further change to sync, unmarks its dirty flag. 299 void PopulateFolderByChildList(const std::string& folder_id, 300 const FileIDList& child_file_ids, 301 const SyncStatusCallback& callback); 302 303 // Updates |synced_details| of the tracker with |updated_details|. 304 void UpdateTracker(int64 tracker_id, 305 const FileDetails& updated_details, 306 const SyncStatusCallback& callback); 307 308 // Activates a tracker identified by |parent_tracker_id| and |file_id| if the 309 // tracker can be activated without inactivating other trackers that have the 310 // same |file_id| but different paths. 311 // If |file_id| has another active tracker, the function returns 312 // ACTIVATION_FAILED_ANOTHER_ACTIVE_TRACKER and does not invoke |callback|. 313 // If there is another active tracker that has the same path but different 314 // |file_id|, inactivates the tracker. 315 // In success case, returns ACTIVATION_PENDING and invokes |callback| upon 316 // completion. 317 // 318 // The tracker to be activated must: 319 // - have a tracked metadata in the database, 320 // - have |synced_details| with valid |title|. 321 ActivationStatus TryActivateTracker(int64 parent_tracker_id, 322 const std::string& file_id, 323 const SyncStatusCallback& callback); 324 325 // Changes the priority of the tracker to low. 326 void LowerTrackerPriority(int64 tracker_id); 327 void PromoteLowerPriorityTrackersToNormal(); 328 329 // Returns true if there is a normal priority dirty tracker. 330 // Assigns the dirty tracker if exists and |tracker| is non-NULL. 331 bool GetNormalPriorityDirtyTracker(FileTracker* tracker) const; 332 333 // Returns true if there is a low priority dirty tracker. 334 bool HasLowPriorityDirtyTracker() const; 335 336 bool HasDirtyTracker() const; 337 size_t CountDirtyTracker() const; 338 size_t CountFileMetadata() const; 339 size_t CountFileTracker() const; 340 341 bool GetMultiParentFileTrackers(std::string* file_id, 342 TrackerIDSet* trackers); 343 bool GetConflictingTrackers(TrackerIDSet* trackers); 344 345 // Sets |app_ids| to a list of all registered app ids. 346 void GetRegisteredAppIDs(std::vector<std::string>* app_ids); 347 348 private: 349 friend class MetadataDatabaseTest; 350 struct CreateParam; 351 352 MetadataDatabase(base::SequencedTaskRunner* worker_task_runner, 353 base::SequencedTaskRunner* file_task_runner, 354 const base::FilePath& database_path, 355 leveldb::Env* env_override); 356 static void CreateOnFileTaskRunner( 357 scoped_ptr<CreateParam> create_param, 358 const CreateCallback& callback); 359 SyncStatusCode InitializeOnFileTaskRunner(); 360 361 // Database manipulation methods. 362 void RegisterTrackerAsAppRoot(const std::string& app_id, 363 int64 tracker_id, 364 leveldb::WriteBatch* batch); 365 366 void CreateTrackerForParentAndFileID(const FileTracker& parent_tracker, 367 const std::string& file_id, 368 leveldb::WriteBatch* batch); 369 void CreateTrackerForParentAndFileMetadata(const FileTracker& parent_tracker, 370 const FileMetadata& file_metadata, 371 UpdateOption option, 372 leveldb::WriteBatch* batch); 373 void CreateTrackerInternal(const FileTracker& parent_tracker, 374 const std::string& file_id, 375 const FileDetails* details, 376 UpdateOption option, 377 leveldb::WriteBatch* batch); 378 379 void MaybeAddTrackersForNewFile(const FileMetadata& file, 380 UpdateOption option, 381 leveldb::WriteBatch* batch); 382 383 int64 IncrementTrackerID(leveldb::WriteBatch* batch); 384 385 bool CanActivateTracker(const FileTracker& tracker); 386 bool ShouldKeepDirty(const FileTracker& tracker) const; 387 388 bool HasDisabledAppRoot(const FileTracker& tracker) const; 389 bool HasActiveTrackerForFileID(const std::string& file_id) const; 390 bool HasActiveTrackerForPath(int64 parent_tracker, 391 const std::string& title) const; 392 393 void RemoveUnneededTrackersForMissingFile(const std::string& file_id, 394 leveldb::WriteBatch* batch); 395 void UpdateByFileMetadata(const tracked_objects::Location& from_where, 396 scoped_ptr<FileMetadata> file, 397 UpdateOption option, 398 leveldb::WriteBatch* batch); 399 400 void WriteToDatabase(scoped_ptr<leveldb::WriteBatch> batch, 401 const SyncStatusCallback& callback); 402 403 bool HasNewerFileMetadata(const std::string& file_id, int64 change_id); 404 405 scoped_ptr<base::ListValue> DumpTrackers(); 406 scoped_ptr<base::ListValue> DumpMetadata(); 407 408 void AttachSyncRoot(const google_apis::FileResource& sync_root_folder, 409 leveldb::WriteBatch* batch); 410 void AttachInitialAppRoot(const google_apis::FileResource& app_root_folder, 411 leveldb::WriteBatch* batch); 412 413 void ForceActivateTrackerByPath(int64 parent_tracker_id, 414 const std::string& title, 415 const std::string& file_id, 416 leveldb::WriteBatch* batch); 417 418 void DetachFromSequence(); 419 420 scoped_refptr<base::SequencedTaskRunner> worker_task_runner_; 421 scoped_refptr<base::SequencedTaskRunner> file_task_runner_; 422 base::FilePath database_path_; 423 leveldb::Env* env_override_; 424 scoped_ptr<leveldb::DB> db_; 425 426 int64 largest_known_change_id_; 427 428 scoped_ptr<MetadataDatabaseIndexInterface> index_; 429 430 base::WeakPtrFactory<MetadataDatabase> weak_ptr_factory_; 431 432 base::SequenceChecker worker_sequence_checker_; 433 434 DISALLOW_COPY_AND_ASSIGN(MetadataDatabase); 435}; 436 437} // namespace drive_backend 438} // namespace sync_file_system 439 440#endif // CHROME_BROWSER_SYNC_FILE_SYSTEM_DRIVE_BACKEND_METADATA_DATABASE_H_ 441