// Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef CONTENT_BROWSER_LEVELDB_WRAPPER_IMPL_H_ #define CONTENT_BROWSER_LEVELDB_WRAPPER_IMPL_H_ #include #include #include #include #include "base/callback.h" #include "base/macros.h" #include "base/optional.h" #include "base/time/time.h" #include "content/common/leveldb_wrapper.mojom.h" #include "mojo/public/cpp/bindings/binding_set.h" #include "mojo/public/cpp/bindings/interface_ptr_set.h" namespace content { // This is a wrapper around a leveldb::mojom::LevelDBDatabase. Multiple // interface // pointers can be bound to the same object. The wrapper adds a couple of // features not found directly in leveldb. // 1) Adds the given prefix, if any, to all keys. This allows the sharing of one // database across many, possibly untrusted, consumers and ensuring that they // can't access each other's values. // 2) Enforces a max_size constraint. // 3) Informs observers when values scoped by prefix are modified. // 4) Throttles requests to avoid overwhelming the disk. class CONTENT_EXPORT LevelDBWrapperImpl : public mojom::LevelDBWrapper { public: using ValueMap = std::map, std::vector>; using ValueMapCallback = base::OnceCallback)>; class CONTENT_EXPORT Delegate { public: virtual void OnNoBindings() = 0; virtual std::vector PrepareToCommit() = 0; virtual void DidCommit(leveldb::mojom::DatabaseError error) = 0; // Called during loading if no data was found. Needs to call |callback|. virtual void MigrateData(ValueMapCallback callback); virtual void OnMapLoaded(leveldb::mojom::DatabaseError error); }; // |no_bindings_callback| will be called when this object has no more // bindings and all pending modifications have been processed. LevelDBWrapperImpl(leveldb::mojom::LevelDBDatabase* database, const std::string& prefix, size_t max_size, base::TimeDelta default_commit_delay, int max_bytes_per_hour, int max_commits_per_hour, Delegate* delegate); ~LevelDBWrapperImpl() override; void Bind(mojom::LevelDBWrapperRequest request); bool empty() const { return bytes_used_ == 0; } size_t bytes_used() const { return bytes_used_; } // Commence aggressive flushing. This should be called early during startup, // before any localStorage writing. Currently scheduled writes will not be // rescheduled and will be flushed at the scheduled time after which // aggressive flushing will commence. static void EnableAggressiveCommitDelay(); // Commits any uncommitted data to the database as soon as possible. This // usually means data will be committed immediately, but if we're currently // waiting on the result of initializing our map the commit won't happen // until the load has finished. void ScheduleImmediateCommit(); // Clears the in-memory cache if currently no changes are pending. If there // are uncommitted changes this method does nothing. void PurgeMemory(); // LevelDBWrapper: void AddObserver(mojom::LevelDBObserverAssociatedPtrInfo observer) override; void Put(const std::vector& key, const std::vector& value, const std::string& source, PutCallback callback) override; void Delete(const std::vector& key, const std::string& source, DeleteCallback callback) override; void DeleteAll(const std::string& source, DeleteAllCallback callback) override; void Get(const std::vector& key, GetCallback callback) override; void GetAll( mojom::LevelDBWrapperGetAllCallbackAssociatedPtrInfo complete_callback, GetAllCallback callback) override; private: // Used to rate limit commits. class RateLimiter { public: RateLimiter(size_t desired_rate, base::TimeDelta time_quantum); void add_samples(size_t samples) { samples_ += samples; } // Computes the total time needed to process the total samples seen // at the desired rate. base::TimeDelta ComputeTimeNeeded() const; // Given the elapsed time since the start of the rate limiting session, // computes the delay needed to mimic having processed the total samples // seen at the desired rate. base::TimeDelta ComputeDelayNeeded( const base::TimeDelta elapsed_time) const; private: float rate_; float samples_; base::TimeDelta time_quantum_; }; struct CommitBatch { bool clear_all_first; std::set> changed_keys; CommitBatch(); ~CommitBatch(); }; void OnConnectionError(); void LoadMap(const base::Closure& completion_callback); void OnMapLoaded(leveldb::mojom::DatabaseError status, std::vector data); void OnGotMigrationData(std::unique_ptr data); void OnLoadComplete(); void CreateCommitBatchIfNeeded(); void StartCommitTimer(); base::TimeDelta ComputeCommitDelay() const; void CommitChanges(); void OnCommitComplete(leveldb::mojom::DatabaseError error); std::vector prefix_; mojo::BindingSet bindings_; mojo::AssociatedInterfacePtrSet observers_; Delegate* delegate_; leveldb::mojom::LevelDBDatabase* database_; std::unique_ptr map_; std::vector on_load_complete_tasks_; size_t bytes_used_; size_t max_size_; base::TimeTicks start_time_; base::TimeDelta default_commit_delay_; RateLimiter data_rate_limiter_; RateLimiter commit_rate_limiter_; int commit_batches_in_flight_ = 0; std::unique_ptr commit_batch_; base::WeakPtrFactory weak_ptr_factory_; static bool s_aggressive_flushing_enabled_; DISALLOW_COPY_AND_ASSIGN(LevelDBWrapperImpl); }; } // namespace content #endif // CONTENT_BROWSER_LEVELDB_WRAPPER_IMPL_H_