metadata_database.h revision f2477e01787aa58f445919b809d89e252beef54f
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/memory/scoped_ptr.h" 15#include "base/memory/scoped_vector.h" 16#include "base/memory/weak_ptr.h" 17#include "base/values.h" 18#include "chrome/browser/sync_file_system/drive_backend/tracker_set.h" 19#include "chrome/browser/sync_file_system/sync_callbacks.h" 20#include "chrome/browser/sync_file_system/sync_status_code.h" 21 22namespace base { 23class FilePath; 24class SequencedTaskRunner; 25class SingleThreadTaskRunner; 26} 27 28namespace leveldb { 29class DB; 30class WriteBatch; 31} 32 33namespace google_apis { 34class ChangeResource; 35class FileResource; 36class ResourceEntry; 37} 38 39namespace sync_file_system { 40namespace drive_backend { 41 42class FileDetails; 43class FileMetadata; 44class FileTracker; 45class ServiceMetadata; 46struct DatabaseContents; 47 48// MetadataDatabase holds and maintains a LevelDB instance and its indexes, 49// which holds 1)ServiceMetadata, 2)FileMetadata and 3)FileTracker. 50// 1) ServiceMetadata is a singleton in the database which holds information for 51// the backend. 52// 2) FileMetadata represents a remote-side file and holds latest known 53// metadata of the remote file. 54// 3) FileTracker represents a synced or to-be-synced file and maintains 55// the local-side folder tree. 56// 57// The term "file" includes files, folders and other resources on Drive. 58// 59// FileTrackers form a tree structure on the database, which represents the 60// FileSystem trees of SyncFileSystem. The tree has a FileTracker named 61// sync-root as its root node, and a set of FileTracker named app-root. An 62// app-root represents a remote folder for an installed Chrome App and holds all 63// synced contents for the App. 64// 65// One FileMetadata is created for each tracked remote file, which is identified 66// by FileID. 67// One FileTracker is created for every different {parent tracker, FileID} pair, 68// excluding non-app-root inactive parent trackers. Multiple trackers may be 69// associated to one FileID when the file has multiple parents. Multiple 70// trackers may have the same {parent tracker, title} pair when the associated 71// remote files have the same title. 72// 73// Files have following state: 74// - Unknown file 75// - Has a dirty inactive tracker and empty synced_details. 76// - Is initial state of a tracker, only file_id and parent_tracker_id field 77// are known. 78// - Folder 79// - Is either one of sync-root folder, app-root folder or a regular folder. 80// - Sync-root folder holds app-root folders as its direct children, and 81// holds entire SyncFileSystem files as its descentants. Its tracker 82// should be stored in ServiceMetadata by its tracker_id. 83// - App-root folder holds all files for an application as its descendants. 84// - File 85// - Unsupported file 86// - Represents unsupported files such as hosted documents. Must be 87// inactive. 88// 89// Invariants: 90// - Any tracker in the database must either: 91// - be sync-root, 92// - have an app-root as its parent tracker, or 93// - have an active tracker as its parent. 94// That is, all trackers must be reachable from sync-root via app-root folders 95// and active trackers. 96// 97// - Any active tracker must either: 98// - have |needs_folder_listing| flag and dirty flag, or 99// - have all children at the stored largest change ID. 100// 101// - If multiple trackers have the same parent tracker and same title, they 102// must not have same |file_id|, and at most one of them may be active. 103// - If multiple trackers have the same |file_id|, at most one of them may be 104// active. 105// 106class MetadataDatabase { 107 public: 108 typedef std::map<std::string, FileMetadata*> FileByID; 109 typedef std::map<int64, FileTracker*> TrackerByID; 110 typedef std::map<std::string, TrackerSet> TrackersByFileID; 111 typedef std::map<std::string, TrackerSet> TrackersByTitle; 112 typedef std::map<int64, TrackersByTitle> TrackersByParentAndTitle; 113 typedef std::map<std::string, FileTracker*> TrackerByAppID; 114 typedef std::vector<std::string> FileIDList; 115 116 typedef base::Callback< 117 void(SyncStatusCode status, scoped_ptr<MetadataDatabase> instance)> 118 CreateCallback; 119 120 // The entry point of the MetadataDatabase for production code. 121 static void Create(base::SequencedTaskRunner* task_runner, 122 const base::FilePath& database_path, 123 const CreateCallback& callback); 124 ~MetadataDatabase(); 125 126 int64 GetLargestFetchedChangeID() const; 127 int64 GetSyncRootTrackerID() const; 128 bool HasSyncRoot() const; 129 130 // Returns all file metadata for the given |app_id|. 131 scoped_ptr<base::ListValue> DumpFiles(const std::string& app_id); 132 133 // TODO(tzik): Move GetLargestKnownChangeID() to private section, and hide its 134 // handling in the class, instead of letting user do. 135 // 136 // Gets / updates the largest known change ID. 137 // The largest known change ID is on-memory and not persist over restart. 138 // This is supposed to use when a task fetches ChangeList in parallel to other 139 // operation. When a task starts fetching paged ChangeList one by one, it 140 // should update the largest known change ID on the first round and background 141 // remaining fetch job. 142 // Then, when other tasks that update FileMetadata by UpdateByFileResource, 143 // it should use largest known change ID as the |change_id| that prevents 144 // FileMetadata from overwritten by ChangeList. 145 // Also if other tasks try to update a remote resource whose change is not yet 146 // retrieved the task should fail due to etag check, so we should be fine. 147 int64 GetLargestKnownChangeID() const; 148 void UpdateLargestKnownChangeID(int64 change_id); 149 150 // Populates empty database with initial data. 151 // Adds a file metadata and a file tracker for |sync_root_folder|, and adds 152 // file metadata and file trackers for each |app_root_folders|. 153 // Newly added tracker for |sync_root_folder| is active and non-dirty. 154 // Newly added trackers for |app_root_folders| are inactive and non-dirty. 155 // Trackers for |app_root_folders| are not yet registered as app-roots, but 156 // are ready to register. 157 void PopulateInitialData( 158 int64 largest_change_id, 159 const google_apis::FileResource& sync_root_folder, 160 const ScopedVector<google_apis::FileResource>& app_root_folders, 161 const SyncStatusCallback& callback); 162 163 // Returns true if the folder associated to |app_id| is enabled. 164 bool IsAppEnabled(const std::string& app_id) const; 165 166 // Registers existing folder as the app-root for |app_id|. The folder 167 // must be an inactive folder that does not yet associated to any App. 168 // This method associates the folder with |app_id| and activates it. 169 void RegisterApp(const std::string& app_id, 170 const std::string& folder_id, 171 const SyncStatusCallback& callback); 172 173 // Inactivates the folder associated to the app to disable |app_id|. 174 // Does nothing if |app_id| is already disabled. 175 void DisableApp(const std::string& app_id, 176 const SyncStatusCallback& callback); 177 178 // Activates the folder associated to |app_id| to enable |app_id|. 179 // Does nothing if |app_id| is already enabled. 180 void EnableApp(const std::string& app_id, 181 const SyncStatusCallback& callback); 182 183 // Unregisters the folder as the app-root for |app_id|. If |app_id| does not 184 // exist, does nothing. The folder is left as an inactive regular folder. 185 // Note that the inactivation drops all descendant files since they are no 186 // longer reachable from sync-root via active folder or app-root. 187 void UnregisterApp(const std::string& app_id, 188 const SyncStatusCallback& callback); 189 190 // Finds the app-root folder for |app_id|. Returns true if exists. 191 // Copies the result to |tracker| if it is non-NULL. 192 bool FindAppRootTracker(const std::string& app_id, 193 FileTracker* tracker) const; 194 195 // Finds the file identified by |file_id|. Returns true if the file is found. 196 // Copies the metadata identified by |file_id| into |file| if exists and 197 // |file| is non-NULL. 198 bool FindFileByFileID(const std::string& file_id, FileMetadata* file) const; 199 200 // Finds the tracker identified by |tracker_id|. Returns true if the tracker 201 // is found. 202 // Copies the tracker identified by |tracker_id| into |tracker| if exists and 203 // |tracker| is non-NULL. 204 bool FindTrackerByTrackerID(int64 tracker_id, FileTracker* tracker) const; 205 206 // Finds the trackers tracking |file_id|. Returns true if the trackers are 207 // found. 208 bool FindTrackersByFileID(const std::string& file_id, 209 TrackerSet* trackers) const; 210 211 // Finds the set of trackers whose parent's tracker ID is |parent_tracker_id|, 212 // and who has |title| as its title in the synced_details. 213 // Copies the tracker set to |trackers| if it is non-NULL. 214 // Returns true if the trackers are found. 215 bool FindTrackersByParentAndTitle( 216 int64 parent_tracker_id, 217 const std::string& title, 218 TrackerSet* trackers) const; 219 220 // Builds the file path for the given tracker. Returns true on success. 221 // |path| can be NULL. 222 // The file path is relative to app-root and have a leading path separator. 223 bool BuildPathForTracker(int64 tracker_id, base::FilePath* path) const; 224 225 // Returns false if no registered app exists associated to |app_id|. 226 // If |full_path| is active, assigns the tracker of |full_path| to |tracker|. 227 // Otherwise, assigns the nearest active ancestor to |full_path| to |tracker|. 228 // Also, assigns the full path of |tracker| to |path|. 229 bool FindNearestActiveAncestor(const std::string& app_id, 230 const base::FilePath& full_path, 231 FileTracker* tracker, 232 base::FilePath* path) const; 233 234 // Updates database by |changes|. 235 // Marks each tracker for modified file as dirty and adds new trackers if 236 // needed. 237 void UpdateByChangeList(int64 largest_change_id, 238 ScopedVector<google_apis::ChangeResource> changes, 239 const SyncStatusCallback& callback); 240 241 // TODO(tzik): Drop |chaneg_id| paramater. 242 // Updates database by |resource|. 243 // Marks each tracker for modified file as dirty and adds new trackers if 244 // needed. 245 void UpdateByFileResource(int64 change_id, 246 const google_apis::FileResource& resource, 247 const SyncStatusCallback& callback); 248 249 // TODO(tzik): Drop |change_id| parameter. 250 // Adds new FileTracker and FileMetadata. The database must not have 251 // |resource| beforehand. 252 // The newly added tracker under |parent_tracker_id| is active and non-dirty. 253 // Deactivates existing active tracker if exists that has the same title and 254 // parent_tracker to the newly added tracker. 255 void ReplaceActiveTrackerWithNewResource( 256 int64 change_id, 257 int64 parent_tracker_id, 258 const google_apis::FileResource& resource, 259 const SyncStatusCallback& callback); 260 261 // Adds |child_file_ids| to |folder_id| as its children. 262 // This method affects the active tracker only. 263 // If the tracker has no further change to sync, unmarks its dirty flag. 264 void PopulateFolderByChildList(const std::string& folder_id, 265 const FileIDList& child_file_ids, 266 const SyncStatusCallback& callback); 267 268 // Updates |synced_details| of the tracker with |updated_details|. 269 void UpdateTracker(int64 tracker_id, 270 const FileDetails& updated_details, 271 const SyncStatusCallback& callback); 272 273 // Changes the priority of the tracker to low. 274 void LowerTrackerPriority(int64 tracker_id); 275 276 // Returns true if there is a normal priority dirty tracker. 277 // Assigns the dirty tracker if exists and |tracker| is non-NULL. 278 bool GetNormalPriorityDirtyTracker(FileTracker* tracker) const; 279 280 // Returns true if there is a low priority dirty tracker. 281 // Assigns the dirty tracker if exists and |tracker| is non-NULL. 282 bool GetLowPriorityDirtyTracker(FileTracker* tracker) const; 283 284 // Sets |app_ids| to a list of all registered app ids. 285 void GetRegisteredAppIDs(std::vector<std::string>* app_ids); 286 287 // Marks |tracker_id| dirty. 288 void MarkTrackerDirty(int64 tracker_id, 289 const SyncStatusCallback& callback); 290 291 private: 292 friend class ListChangesTaskTest; 293 friend class MetadataDatabaseTest; 294 friend class RegisterAppTaskTest; 295 friend class SyncEngineInitializerTest; 296 297 struct DirtyTrackerComparator { 298 bool operator()(const FileTracker* left, 299 const FileTracker* right) const; 300 }; 301 302 typedef std::set<FileTracker*, DirtyTrackerComparator> DirtyTrackers; 303 304 explicit MetadataDatabase(base::SequencedTaskRunner* task_runner); 305 static void CreateOnTaskRunner(base::SingleThreadTaskRunner* callback_runner, 306 base::SequencedTaskRunner* task_runner, 307 const base::FilePath& database_path, 308 const CreateCallback& callback); 309 static SyncStatusCode CreateForTesting( 310 scoped_ptr<leveldb::DB> db, 311 scoped_ptr<MetadataDatabase>* metadata_database_out); 312 SyncStatusCode InitializeOnTaskRunner(const base::FilePath& database_path); 313 void BuildIndexes(DatabaseContents* contents); 314 315 SyncStatusCode SetLargestChangeIDForTesting(int64 largest_changestamp); 316 317 // Database manipulation methods. 318 void RegisterTrackerAsAppRoot(const std::string& app_id, 319 int64 tracker_id, 320 leveldb::WriteBatch* batch); 321 void MakeTrackerActive(int64 tracker_id, leveldb::WriteBatch* batch); 322 void MakeTrackerInactive(int64 tracker_id, leveldb::WriteBatch* batch); 323 void MakeAppRootDisabled(int64 tracker_id, leveldb::WriteBatch* batch); 324 void MakeAppRootEnabled(int64 tracker_id, leveldb::WriteBatch* batch); 325 326 void UnregisterTrackerAsAppRoot(const std::string& app_id, 327 leveldb::WriteBatch* batch); 328 void RemoveAllDescendantTrackers(int64 root_tracker_id, 329 leveldb::WriteBatch* batch); 330 331 void CreateTrackerForParentAndFileID(const FileTracker& parent_tracker, 332 const std::string& file_id, 333 leveldb::WriteBatch* batch); 334 335 void RemoveTracker(int64 tracker_id, leveldb::WriteBatch* batch); 336 void RemoveTrackerIgnoringSameTitle(int64 tracker_id, 337 leveldb::WriteBatch* batch); 338 void RemoveTrackerInternal(int64 tracker_id, 339 leveldb::WriteBatch* batch, 340 bool ignoring_same_title); 341 void MaybeAddTrackersForNewFile(const FileMetadata& file, 342 leveldb::WriteBatch* batch); 343 344 void MarkSingleTrackerDirty(FileTracker* tracker, 345 leveldb::WriteBatch* batch); 346 void MarkTrackerSetDirty(TrackerSet* trackers, 347 leveldb::WriteBatch* batch); 348 void MarkTrackersDirtyByFileID(const std::string& file_id, 349 leveldb::WriteBatch* batch); 350 void MarkTrackersDirtyByPath(int64 parent_tracker_id, 351 const std::string& title, 352 leveldb::WriteBatch* batch); 353 354 void EraseTrackerFromFileIDIndex(FileTracker* tracker, 355 leveldb::WriteBatch* batch); 356 void EraseTrackerFromPathIndex(FileTracker* tracker); 357 void EraseFileFromDatabase(const std::string& file_id, 358 leveldb::WriteBatch* batch); 359 360 int64 GetNextTrackerID(leveldb::WriteBatch* batch); 361 362 void RecursiveMarkTrackerAsDirty(int64 root_tracker_id, 363 leveldb::WriteBatch* batch); 364 bool CanActivateTracker(const FileTracker& tracker); 365 bool ShouldKeepDirty(const FileTracker& tracker) const; 366 367 bool HasDisabledAppRoot(const FileTracker& tracker) const; 368 bool HasActiveTrackerForFileID(const std::string& file_id) const; 369 bool HasActiveTrackerForPath(int64 parent_tracker, 370 const std::string& title) const; 371 372 void WriteToDatabase(scoped_ptr<leveldb::WriteBatch> batch, 373 const SyncStatusCallback& callback); 374 375 bool HasNewerFileMetadata(const std::string& file_id, int64 change_id); 376 377 scoped_refptr<base::SequencedTaskRunner> task_runner_; 378 scoped_ptr<leveldb::DB> db_; 379 380 scoped_ptr<ServiceMetadata> service_metadata_; 381 int64 largest_known_change_id_; 382 383 FileByID file_by_id_; // Owned. 384 TrackerByID tracker_by_id_; // Owned. 385 386 // Maps FileID to trackers. The active tracker must be unique per FileID. 387 // This must be updated when updating |active| field of a tracker. 388 TrackersByFileID trackers_by_file_id_; // Not owned. 389 390 // Maps AppID to the app-root tracker. 391 // This must be updated when a tracker is registered/unregistered as an 392 // app-root. 393 TrackerByAppID app_root_by_app_id_; // Not owned. 394 395 // Maps |tracker_id| to its children grouped by their |title|. 396 // If the title is unknown for a tracker, treats its title as empty. Empty 397 // titled file must not be active. 398 // The active tracker must be unique per its parent_tracker and its title. 399 // This must be updated when updating |title|, |active| or 400 // |parent_tracker_id|. 401 TrackersByParentAndTitle trackers_by_parent_and_title_; 402 403 // Holds all trackers which marked as dirty. 404 // This must be updated when updating |dirty| field of a tracker. 405 DirtyTrackers dirty_trackers_; // Not owned. 406 DirtyTrackers low_priority_dirty_trackers_; // Not owned. 407 408 base::WeakPtrFactory<MetadataDatabase> weak_ptr_factory_; 409 410 DISALLOW_COPY_AND_ASSIGN(MetadataDatabase); 411}; 412 413} // namespace drive_backend 414} // namespace sync_file_system 415 416#endif // CHROME_BROWSER_SYNC_FILE_SYSTEM_DRIVE_BACKEND_METADATA_DATABASE_H_ 417