metadata_database.h revision 58537e28ecd584eab876aee8be7156509866d23a
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 "chrome/browser/sync_file_system/drive_backend/tracker_set.h"
18#include "chrome/browser/sync_file_system/sync_callbacks.h"
19#include "chrome/browser/sync_file_system/sync_status_code.h"
20
21namespace base {
22class FilePath;
23class SequencedTaskRunner;
24class SingleThreadTaskRunner;
25}
26
27namespace leveldb {
28class DB;
29class WriteBatch;
30}
31
32namespace google_apis {
33class ChangeResource;
34class FileResource;
35class ResourceEntry;
36}
37
38namespace sync_file_system {
39namespace drive_backend {
40
41class FileDetails;
42class FileMetadata;
43class FileTracker;
44class ServiceMetadata;
45struct DatabaseContents;
46
47// MetadataDatabase holds and maintains a LevelDB instance and its indexes,
48// which holds 1)ServiceMetadata, 2)FileMetadata and 3)FileTracker.
49// 1) ServiceMetadata is a singleton in the database which holds information for
50//    the backend.
51// 2) FileMetadata represents a remote-side file and holds latest known
52//    metadata of the remote file.
53// 3) FileTracker represents a synced or to-be-synced file and maintains
54//    the local-side folder tree.
55//
56// The term "file" includes files, folders and other resources on Drive.
57//
58// FileTrackers form a tree structure on the database, which represents the
59// FileSystem trees of SyncFileSystem.  The tree has a FileTracker named
60// sync-root as its root node, and a set of FileTracker named app-root.  An
61// app-root represents a remote folder for an installed Chrome App and holds all
62// synced contents for the App.
63//
64// One FileMetadata is created for each tracked remote file, which is identified
65// by FileID.
66// One FileTracker is created for every different {parent tracker, FileID} pair,
67// excluding non-app-root inactive parent trackers. Multiple trackers may be
68// associated to one FileID when the file has multiple parents. Multiple
69// trackers may have the same {parent tracker, title} pair when the associated
70// remote files have the same title.
71//
72// Files have following state:
73//   - Unknown file
74//     - Has a dirty inactive tracker and empty synced_details.
75//     - Is initial state of a tracker, only file_id and parent_tracker_id field
76//       are known.
77//   - Folder
78//     - Is either one of sync-root folder, app-root folder or a regular folder.
79//     - Sync-root folder holds app-root folders as its direct children, and
80//       holds entire SyncFileSystem files as its descentants.  Its tracker
81//       should be stored in ServiceMetadata by its tracker_id.
82//     - App-root folder holds all files for an application as its descendants.
83//   - File
84//   - Unsupported file
85//     - Represents unsupported files such as hosted documents. Must be
86//       inactive.
87//
88// Invariants:
89//   - Any tracker in the database must either:
90//     - be sync-root,
91//     - have an app-root as its parent tracker, or
92//     - have an active tracker as its parent.
93//   That is, all trackers must be reachable from sync-root via app-root folders
94//   and active trackers.
95//
96//   - Any active tracker must either:
97//     - have |needs_folder_listing| flag and dirty flag, or
98//     - have all children at the stored largest change ID.
99//
100//   - If multiple trackers have the same parent tracker and same title, they
101//     must not have same |file_id|, and at most one of them may be active.
102//   - If multiple trackers have the same |file_id|, at most one of them may be
103//     active.
104//
105class MetadataDatabase {
106 public:
107  typedef std::map<std::string, FileMetadata*> FileByID;
108  typedef std::map<int64, FileTracker*> TrackerByID;
109  typedef std::map<std::string, TrackerSet> TrackersByFileID;
110  typedef std::map<std::string, TrackerSet> TrackersByTitle;
111  typedef std::map<int64, TrackersByTitle> TrackersByParentAndTitle;
112  typedef std::map<std::string, FileTracker*> TrackerByAppID;
113  typedef std::vector<std::string> FileIDList;
114
115  typedef base::Callback<
116      void(SyncStatusCode status, scoped_ptr<MetadataDatabase> instance)>
117      CreateCallback;
118
119  // The entry point of the MetadataDatabase for production code.
120  static void Create(base::SequencedTaskRunner* task_runner,
121                     const base::FilePath& database_path,
122                     const CreateCallback& callback);
123  ~MetadataDatabase();
124
125  int64 GetLargestChangeID() const;
126  int64 GetSyncRootTrackerID() const;
127  bool HasSyncRoot() const;
128
129  // Populates empty database with initial data.
130  // Adds a file metadata and a file tracker for |sync_root_folder|, and adds
131  // file metadata and file trackers for each |app_root_folders|.
132  // Newly added tracker for |sync_root_folder| is active and non-dirty.
133  // Newly added trackers for |app_root_folders| are inactive and non-dirty.
134  // Trackers for |app_root_folders| are not yet registered as app-roots, but
135  // are ready to register.
136  void PopulateInitialData(
137      int64 largest_change_id,
138      const google_apis::FileResource& sync_root_folder,
139      const ScopedVector<google_apis::FileResource>& app_root_folders,
140      const SyncStatusCallback& callback);
141
142  // Registers existing folder as the app-root for |app_id|.  The folder
143  // must be an inactive folder that does not yet associated to any App.
144  // This method associates the folder with |app_id| and activates it.
145  void RegisterApp(const std::string& app_id,
146                   const std::string& folder_id,
147                   const SyncStatusCallback& callback);
148
149  // Inactivates the folder associated to the app to disable |app_id|.
150  // Does nothing if |app_id| is already disabled.
151  void DisableApp(const std::string& app_id,
152                  const SyncStatusCallback& callback);
153
154  // Activates the folder associated to |app_id| to enable |app_id|.
155  // Does nothing if |app_id| is already enabled.
156  void EnableApp(const std::string& app_id,
157                 const SyncStatusCallback& callback);
158
159  // Unregisters the folder as the app-root for |app_id|.  If |app_id| does not
160  // exist, does nothing.  The folder is left as an inactive regular folder.
161  // Note that the inactivation drops all descendant files since they are no
162  // longer reachable from sync-root via active folder or app-root.
163  void UnregisterApp(const std::string& app_id,
164                     const SyncStatusCallback& callback);
165
166  // Finds the app-root folder for |app_id|.  Returns true if exists.
167  // Copies the result to |tracker| if it is non-NULL.
168  bool FindAppRootTracker(const std::string& app_id,
169                          FileTracker* tracker) const;
170
171  // Finds the file identified by |file_id|.  Returns true if the file is found.
172  // Copies the metadata identified by |file_id| into |file| if exists and
173  // |file| is non-NULL.
174  bool FindFileByFileID(const std::string& file_id, FileMetadata* file) const;
175
176  // Finds the tracker identified by |tracker_id|.  Returns true if the tracker
177  // is found.
178  // Copies the tracker identified by |tracker_id| into |tracker| if exists and
179  // |tracker| is non-NULL.
180  bool FindTrackerByTrackerID(int64 tracker_id, FileTracker* tracker) const;
181
182  // Finds the trackers tracking |file_id|.  Returns true if the trackers are
183  // found.
184  bool FindTrackersByFileID(const std::string& file_id,
185                            TrackerSet* trackers) const;
186
187  // Finds the set of trackers whose parent's tracker ID is |parent_tracker_id|,
188  // and who has |title| as its title in the synced_details.
189  // Copies the tracker set to |trackers| if it is non-NULL.
190  size_t FindTrackersByParentAndTitle(
191      int64 parent_tracker_id,
192      const std::string& title,
193      TrackerSet* trackers) const;
194
195  // Builds the file path for the given tracker.  Returns true on success.
196  // |path| can be NULL.
197  // The file path is relative to app-root and have a leading path separator.
198  bool BuildPathForTracker(int64 tracker_id, base::FilePath* path) const;
199
200  // Updates database by |changes|.
201  // Marks each tracker for modified file as dirty and adds new trackers if
202  // needed.
203  void UpdateByChangeList(ScopedVector<google_apis::ChangeResource> changes,
204                          const SyncStatusCallback& callback);
205
206  // Adds |child_file_ids| to |folder_id| as its children.
207  // This method affects the active tracker only.
208  // If the tracker has no further change to sync, unmarks its dirty flag.
209  void PopulateFolderByChildList(const std::string& folder_id,
210                                 const FileIDList& child_file_ids,
211                                 const SyncStatusCallback& callback);
212
213  // Updates |synced_details| of the tracker with |updated_details|.
214  void UpdateTracker(int64 tracker_id,
215                     const FileDetails& updated_details,
216                     const SyncStatusCallback& callback);
217
218 private:
219  struct DirtyTrackerComparator {
220    bool operator()(const FileTracker* left,
221                    const FileTracker* right) const;
222  };
223
224  typedef std::set<FileTracker*, DirtyTrackerComparator> DirtyTrackers;
225
226  friend class MetadataDatabaseTest;
227
228  explicit MetadataDatabase(base::SequencedTaskRunner* task_runner);
229  static void CreateOnTaskRunner(base::SingleThreadTaskRunner* callback_runner,
230                                 base::SequencedTaskRunner* task_runner,
231                                 const base::FilePath& database_path,
232                                 const CreateCallback& callback);
233  static SyncStatusCode CreateForTesting(
234      scoped_ptr<leveldb::DB> db,
235      scoped_ptr<MetadataDatabase>* metadata_database_out);
236  SyncStatusCode InitializeOnTaskRunner(const base::FilePath& database_path);
237  void BuildIndexes(DatabaseContents* contents);
238
239  // Database manipulation methods.
240  void RegisterTrackerAsAppRoot(const std::string& app_id,
241                                int64 tracker_id,
242                                leveldb::WriteBatch* batch);
243  void MakeTrackerActive(int64 tracker_id, leveldb::WriteBatch* batch);
244  void MakeTrackerInactive(int64 tracker_id, leveldb::WriteBatch* batch);
245  void MakeAppRootDisabled(int64 tracker_id, leveldb::WriteBatch* batch);
246  void MakeAppRootEnabled(int64 tracker_id, leveldb::WriteBatch* batch);
247
248  void UnregisterTrackerAsAppRoot(const std::string& app_id,
249                                  leveldb::WriteBatch* batch);
250  void RemoveAllDescendantTrackers(int64 root_tracker_id,
251                                   leveldb::WriteBatch* batch);
252
253  void CreateTrackerForParentAndFileID(const FileTracker& parent_tracker,
254                                       const std::string& file_id,
255                                       leveldb::WriteBatch* batch);
256
257  void RemoveTracker(int64 tracker_id, leveldb::WriteBatch* batch);
258  void RemoveTrackerIgnoringSiblings(int64 tracker_id,
259                                     leveldb::WriteBatch* batch);
260  void RemoveTrackerInternal(int64 tracker_id,
261                             leveldb::WriteBatch* batch,
262                             bool ignoring_siblings);
263  void MaybeAddTrackersForNewFile(const FileMetadata& file,
264                                  leveldb::WriteBatch* batch);
265
266  void MarkTrackerSetDirty(TrackerSet* trackers,
267                           leveldb::WriteBatch* batch);
268  void MarkTrackersDirtyByFileID(const std::string& file_id,
269                                 leveldb::WriteBatch* batch);
270  void MarkTrackersDirtyByPath(int64 parent_tracker_id,
271                               const std::string& title,
272                               leveldb::WriteBatch* batch);
273
274  void EraseTrackerFromFileIDIndex(FileTracker* tracker,
275                                   leveldb::WriteBatch* batch);
276  void EraseTrackerFromPathIndex(FileTracker* tracker);
277  void EraseFileFromDatabase(const std::string& file_id,
278                             leveldb::WriteBatch* batch);
279
280  int64 GetNextTrackerID(leveldb::WriteBatch* batch);
281
282  void RecursiveMarkTrackerAsDirty(int64 root_tracker_id,
283                                   leveldb::WriteBatch* batch);
284  bool CanActivateTracker(const FileTracker& tracker);
285  bool ShouldKeepDirty(const FileTracker& tracker) const;
286
287  bool HasDisabledAppRoot(const FileTracker& tracker) const;
288  bool HasActiveTrackerForFileID(const std::string& file_id) const;
289  bool HasActiveTrackerForPath(int64 parent_tracker,
290                               const std::string& title) const;
291
292  void UpdateTrackerTitle(FileTracker* tracker,
293                          const std::string& new_title,
294                          leveldb::WriteBatch* batch);
295
296  void WriteToDatabase(scoped_ptr<leveldb::WriteBatch> batch,
297                       const SyncStatusCallback& callback);
298
299  scoped_refptr<base::SequencedTaskRunner> task_runner_;
300  scoped_ptr<leveldb::DB> db_;
301
302  scoped_ptr<ServiceMetadata> service_metadata_;
303
304  FileByID file_by_id_;  // Owned.
305  TrackerByID tracker_by_id_;  // Owned.
306
307  // Maps FileID to trackers.  The active tracker must be unique per FileID.
308  // This must be updated when updating |active| field of a tracker.
309  TrackersByFileID trackers_by_file_id_;  // Not owned.
310
311  // Maps AppID to the app-root tracker.
312  // This must be updated when a tracker is registered/unregistered as an
313  // app-root.
314  TrackerByAppID app_root_by_app_id_;  // Not owned.
315
316  // Maps |tracker_id| to its children grouped by their |title|.
317  // If the title is unknown for a tracker, treats its title as empty. Empty
318  // titled file must not be active.
319  // The active tracker must be unique per its parent_tracker and its title.
320  // This must be updated when updating |title|, |active| or
321  // |parent_tracker_id|.
322  TrackersByParentAndTitle trackers_by_parent_and_title_;
323
324  // Holds all trackers which marked as dirty.
325  // This must be updated when updating |dirty| field of a tracker.
326  DirtyTrackers dirty_trackers_;  // Not owned.
327
328  base::WeakPtrFactory<MetadataDatabase> weak_ptr_factory_;
329
330  DISALLOW_COPY_AND_ASSIGN(MetadataDatabase);
331};
332
333}  // namespace drive_backend
334}  // namespace sync_file_system
335
336#endif  // CHROME_BROWSER_SYNC_FILE_SYSTEM_DRIVE_BACKEND_METADATA_DATABASE_H_
337