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