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