metadata_database.h revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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/containers/hash_tables.h" 14#include "base/containers/scoped_ptr_hash_map.h" 15#include "base/files/file_path.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_status_code.h" 23 24namespace leveldb { 25class Env; 26} 27 28namespace google_apis { 29class ChangeResource; 30class FileResource; 31class ResourceEntry; 32} 33 34namespace tracked_objects { 35class Location; 36} 37 38namespace sync_file_system { 39namespace drive_backend { 40 41class FileDetails; 42class FileMetadata; 43class FileTracker; 44class LevelDBWrapper; 45class MetadataDatabaseIndexInterface; 46class ServiceMetadata; 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::vector<std::string> FileIDList; 109 110 enum ActivationStatus { 111 ACTIVATION_PENDING, 112 ACTIVATION_FAILED_ANOTHER_ACTIVE_TRACKER, 113 }; 114 115 enum UpdateOption { 116 UPDATE_TRACKER_FOR_UNSYNCED_FILE, 117 UPDATE_TRACKER_FOR_SYNCED_FILE, 118 }; 119 120 // The entry point of the MetadataDatabase for production code. 121 // If |env_override| is non-NULL, internal LevelDB uses |env_override| instead 122 // of leveldb::Env::Default(). Use leveldb::MemEnv in test code for faster 123 // testing. 124 static scoped_ptr<MetadataDatabase> Create( 125 const base::FilePath& database_path, 126 leveldb::Env* env_override, 127 SyncStatusCode* status); 128 static scoped_ptr<MetadataDatabase> CreateInternal( 129 const base::FilePath& database_path, 130 leveldb::Env* env_override, 131 bool enable_on_disk_index, 132 SyncStatusCode* status); 133 static SyncStatusCode CreateForTesting( 134 scoped_ptr<LevelDBWrapper> db, 135 bool enable_on_disk_index, 136 scoped_ptr<MetadataDatabase>* metadata_database_out); 137 138 ~MetadataDatabase(); 139 140 static void ClearDatabase(scoped_ptr<MetadataDatabase> metadata_database); 141 142 int64 GetLargestFetchedChangeID() const; 143 int64 GetSyncRootTrackerID() const; 144 bool HasSyncRoot() const; 145 146 // Returns all file metadata for the given |app_id|. 147 scoped_ptr<base::ListValue> DumpFiles(const std::string& app_id); 148 149 // Returns all database data. 150 scoped_ptr<base::ListValue> DumpDatabase(); 151 152 // TODO(tzik): Move GetLargestKnownChangeID() to private section, and hide its 153 // handling in the class, instead of letting user do. 154 // 155 // Gets / updates the largest known change ID. 156 // The largest known change ID is on-memory and not persist over restart. 157 // This is supposed to use when a task fetches ChangeList in parallel to other 158 // operation. When a task starts fetching paged ChangeList one by one, it 159 // should update the largest known change ID on the first round and background 160 // remaining fetch job. 161 // Then, when other tasks that update FileMetadata by UpdateByFileResource, 162 // it should use largest known change ID as the |change_id| that prevents 163 // FileMetadata from overwritten by ChangeList. 164 // Also if other tasks try to update a remote resource whose change is not yet 165 // retrieved the task should fail due to etag check, so we should be fine. 166 int64 GetLargestKnownChangeID() const; 167 void UpdateLargestKnownChangeID(int64 change_id); 168 169 // Populates empty database with initial data. 170 // Adds a file metadata and a file tracker for |sync_root_folder|, and adds 171 // file metadata and file trackers for each |app_root_folders|. 172 // Newly added tracker for |sync_root_folder| is active and non-dirty. 173 // Newly added trackers for |app_root_folders| are inactive and non-dirty. 174 // Trackers for |app_root_folders| are not yet registered as app-roots, but 175 // are ready to register. 176 SyncStatusCode PopulateInitialData( 177 int64 largest_change_id, 178 const google_apis::FileResource& sync_root_folder, 179 const ScopedVector<google_apis::FileResource>& app_root_folders); 180 181 // Returns true if the folder associated to |app_id| is enabled. 182 bool IsAppEnabled(const std::string& app_id) const; 183 184 // Registers existing folder as the app-root for |app_id|. The folder 185 // must be an inactive folder that does not yet associated to any App. 186 // This method associates the folder with |app_id| and activates it. 187 SyncStatusCode RegisterApp(const std::string& app_id, 188 const std::string& folder_id); 189 190 // Inactivates the folder associated to the app to disable |app_id|. 191 // Does nothing if |app_id| is already disabled. 192 SyncStatusCode DisableApp(const std::string& app_id); 193 194 // Activates the folder associated to |app_id| to enable |app_id|. 195 // Does nothing if |app_id| is already enabled. 196 SyncStatusCode EnableApp(const std::string& app_id); 197 198 // Unregisters the folder as the app-root for |app_id|. If |app_id| does not 199 // exist, does nothing. The folder is left as an inactive regular folder. 200 // Note that the inactivation drops all descendant files since they are no 201 // longer reachable from sync-root via active folder or app-root. 202 SyncStatusCode UnregisterApp(const std::string& app_id); 203 204 // Finds the app-root folder for |app_id|. Returns true if exists. 205 // Copies the result to |tracker| if it is non-NULL. 206 bool FindAppRootTracker(const std::string& app_id, 207 FileTracker* tracker) const; 208 209 // Finds the file identified by |file_id|. Returns true if the file is found. 210 // Copies the metadata identified by |file_id| into |file| if exists and 211 // |file| is non-NULL. 212 bool FindFileByFileID(const std::string& file_id, FileMetadata* file) const; 213 214 // Finds the tracker identified by |tracker_id|. Returns true if the tracker 215 // is found. 216 // Copies the tracker identified by |tracker_id| into |tracker| if exists and 217 // |tracker| is non-NULL. 218 bool FindTrackerByTrackerID(int64 tracker_id, FileTracker* tracker) const; 219 220 // Finds the trackers tracking |file_id|. Returns true if the trackers are 221 // found. 222 bool FindTrackersByFileID(const std::string& file_id, 223 TrackerIDSet* trackers) const; 224 225 // Finds the set of trackers whose parent's tracker ID is |parent_tracker_id|, 226 // and who has |title| as its title in the synced_details. 227 // Copies the tracker set to |trackers| if it is non-NULL. 228 // Returns true if the trackers are found. 229 bool FindTrackersByParentAndTitle( 230 int64 parent_tracker_id, 231 const std::string& title, 232 TrackerIDSet* trackers) const; 233 234 // Builds the file path for the given tracker. Returns true on success. 235 // |path| can be NULL. 236 // The file path is relative to app-root and have a leading path separator. 237 bool BuildPathForTracker(int64 tracker_id, base::FilePath* path) const; 238 239 // Builds the file path for the given tracker for display purpose. 240 // This may return a path ending with '<unknown>' if the given tracker does 241 // not have title information (yet). This may return an empty path. 242 base::FilePath BuildDisplayPathForTracker(const FileTracker& tracker) const; 243 244 // Returns false if no registered app exists associated to |app_id|. 245 // If |full_path| is active, assigns the tracker of |full_path| to |tracker|. 246 // Otherwise, assigns the nearest active ancestor to |full_path| to |tracker|. 247 // Also, assigns the full path of |tracker| to |path|. 248 bool FindNearestActiveAncestor(const std::string& app_id, 249 const base::FilePath& full_path, 250 FileTracker* tracker, 251 base::FilePath* path) const; 252 253 // Updates database by |changes|. 254 // Marks each tracker for modified file as dirty and adds new trackers if 255 // needed. 256 SyncStatusCode UpdateByChangeList( 257 int64 largest_change_id, 258 ScopedVector<google_apis::ChangeResource> changes); 259 260 // Updates database by |resource|. 261 // Marks each tracker for modified file as dirty and adds new trackers if 262 // needed. 263 SyncStatusCode UpdateByFileResource( 264 const google_apis::FileResource& resource); 265 SyncStatusCode UpdateByFileResourceList( 266 ScopedVector<google_apis::FileResource> resources); 267 268 SyncStatusCode UpdateByDeletedRemoteFile(const std::string& file_id); 269 SyncStatusCode UpdateByDeletedRemoteFileList(const FileIDList& file_ids); 270 271 // Adds new FileTracker and FileMetadata. The database must not have 272 // |resource| beforehand. 273 // The newly added tracker under |parent_tracker_id| is active and non-dirty. 274 // Deactivates existing active tracker if exists that has the same title and 275 // parent_tracker to the newly added tracker. 276 SyncStatusCode ReplaceActiveTrackerWithNewResource( 277 int64 parent_tracker_id, 278 const google_apis::FileResource& resource); 279 280 // Adds |child_file_ids| to |folder_id| as its children. 281 // This method affects the active tracker only. 282 // If the tracker has no further change to sync, unmarks its dirty flag. 283 SyncStatusCode PopulateFolderByChildList(const std::string& folder_id, 284 const FileIDList& child_file_ids); 285 286 // Updates |synced_details| of the tracker with |updated_details|. 287 SyncStatusCode UpdateTracker(int64 tracker_id, 288 const FileDetails& updated_details); 289 290 // Activates a tracker identified by |parent_tracker_id| and |file_id| if the 291 // tracker can be activated without inactivating other trackers that have the 292 // same |file_id| but different paths. 293 // If |file_id| has another active tracker, the function returns 294 // ACTIVATION_FAILED_ANOTHER_ACTIVE_TRACKER and does not invoke |callback|. 295 // If there is another active tracker that has the same path but different 296 // |file_id|, inactivates the tracker. 297 // In success case, returns ACTIVATION_PENDING and invokes |callback| upon 298 // completion. 299 // 300 // The tracker to be activated must: 301 // - have a tracked metadata in the database, 302 // - have |synced_details| with valid |title|. 303 ActivationStatus TryActivateTracker(int64 parent_tracker_id, 304 const std::string& file_id, 305 SyncStatusCode* status); 306 307 // Changes the priority of the tracker to low. 308 void DemoteTracker(int64 tracker_id); 309 bool PromoteDemotedTrackers(); 310 void PromoteDemotedTracker(int64 tracker_id); 311 312 // Returns true if there is a normal priority dirty tracker. 313 // Assigns the dirty tracker if exists and |tracker| is non-NULL. 314 bool GetDirtyTracker(FileTracker* tracker) const; 315 316 // Returns true if there is a low priority dirty tracker. 317 bool HasDemotedDirtyTracker() const; 318 319 bool HasDirtyTracker() const; 320 size_t CountDirtyTracker() const; 321 size_t CountFileMetadata() const; 322 size_t CountFileTracker() const; 323 324 bool GetMultiParentFileTrackers(std::string* file_id, 325 TrackerIDSet* trackers); 326 bool GetConflictingTrackers(TrackerIDSet* trackers); 327 328 // Sets |app_ids| to a list of all registered app ids. 329 void GetRegisteredAppIDs(std::vector<std::string>* app_ids); 330 331 // Clears dirty flag of trackers that can be cleared without external 332 // interactien. 333 SyncStatusCode SweepDirtyTrackers(const std::vector<std::string>& file_ids); 334 335 private: 336 friend class MetadataDatabaseTest; 337 338 MetadataDatabase(const base::FilePath& database_path, 339 bool enable_on_disk_index, 340 leveldb::Env* env_override); 341 SyncStatusCode Initialize(); 342 343 // Database manipulation methods. 344 void RegisterTrackerAsAppRoot(const std::string& app_id, 345 int64 tracker_id); 346 347 void CreateTrackerForParentAndFileID(const FileTracker& parent_tracker, 348 const std::string& file_id); 349 void CreateTrackerForParentAndFileMetadata(const FileTracker& parent_tracker, 350 const FileMetadata& file_metadata, 351 UpdateOption option); 352 void CreateTrackerInternal(const FileTracker& parent_tracker, 353 const std::string& file_id, 354 const FileDetails* details, 355 UpdateOption option); 356 357 void MaybeAddTrackersForNewFile(const FileMetadata& file, 358 UpdateOption option); 359 360 int64 IncrementTrackerID(); 361 362 bool CanActivateTracker(const FileTracker& tracker); 363 bool ShouldKeepDirty(const FileTracker& tracker) const; 364 365 bool HasDisabledAppRoot(const FileTracker& tracker) const; 366 bool HasActiveTrackerForFileID(const std::string& file_id) const; 367 bool HasActiveTrackerForPath(int64 parent_tracker, 368 const std::string& title) const; 369 370 void RemoveUnneededTrackersForMissingFile(const std::string& file_id); 371 void UpdateByFileMetadata(const tracked_objects::Location& from_where, 372 scoped_ptr<FileMetadata> file, 373 UpdateOption option); 374 375 SyncStatusCode WriteToDatabase(); 376 377 bool HasNewerFileMetadata(const std::string& file_id, int64 change_id); 378 379 scoped_ptr<base::ListValue> DumpTrackers(); 380 scoped_ptr<base::ListValue> DumpMetadata(); 381 382 void AttachSyncRoot(const google_apis::FileResource& sync_root_folder); 383 void AttachInitialAppRoot(const google_apis::FileResource& app_root_folder); 384 385 void ForceActivateTrackerByPath(int64 parent_tracker_id, 386 const std::string& title, 387 const std::string& file_id); 388 389 bool CanClearDirty(const FileTracker& tracker); 390 391 base::FilePath database_path_; 392 leveldb::Env* env_override_; 393 scoped_ptr<LevelDBWrapper> db_; 394 395 bool enable_on_disk_index_; 396 397 int64 largest_known_change_id_; 398 399 scoped_ptr<MetadataDatabaseIndexInterface> index_; 400 401 base::WeakPtrFactory<MetadataDatabase> weak_ptr_factory_; 402 403 DISALLOW_COPY_AND_ASSIGN(MetadataDatabase); 404}; 405 406} // namespace drive_backend 407} // namespace sync_file_system 408 409#endif // CHROME_BROWSER_SYNC_FILE_SYSTEM_DRIVE_BACKEND_METADATA_DATABASE_H_ 410