1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
|
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_SERVICES_STORAGE_INDEXED_DB_SCOPES_LEVELDB_SCOPE_H_
#define COMPONENTS_SERVICES_STORAGE_INDEXED_DB_SCOPES_LEVELDB_SCOPE_H_
#include <stdint.h>
#include <limits>
#include <string>
#include <utility>
#include <vector>
#include "base/check_op.h"
#include "base/containers/flat_map.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/numerics/checked_math.h"
#include "base/sequence_checker.h"
#include "components/services/storage/indexed_db/leveldb/leveldb_state.h"
#include "components/services/storage/indexed_db/locks/partitioned_lock.h"
#include "components/services/storage/indexed_db/scopes/leveldb_scope_deletion_mode.h"
#include "components/services/storage/indexed_db/scopes/leveldb_scopes_coding.h"
#include "components/services/storage/indexed_db/scopes/scopes_metadata.pb.h"
#include "third_party/leveldatabase/src/include/leveldb/slice.h"
#include "third_party/leveldatabase/src/include/leveldb/status.h"
#include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
namespace content::indexed_db {
// LevelDBScope is a specialized type of transaction used only for writing data,
// and is created using the |LevelDBScopes::CreateScope| method.
// It has optimizations like:
// * Supporting deferred deletion for ranges that will never be accessed again.
// This guarantees the data will be deleted, but it will delete it on a
// different thread sometime in the future.
// * Storing changes in leveldb instead of in memory, so the user doesn't have
// to worry about having a set of changes that gets too big.
// * Not bothering looking up 'undo' optimizations for empty ranges.
// To support this, the following invariants have to be followed:
// * The locks the scope was created with should protect it from colliding with
// modifications by other parties.
// * All 'Get' operations in the range/s locked by this scope will first call
// |WriteChangesAndUndoLog()| so all writes are submitted to LevelDB.
// * Any ranges deleted with |kDeferred| or |kDeferredWithCompaction| could
// continue to have the data for a while, so they should never be accessed
// again.
//
// Error handling on |Put|, |Delete*|, and |WriteChangesAndUndoLog|:
// * After a corruption error (Status::IsCorruption() returns true), all
// operations on the scope must cease, commit cannot be called, and the whole
// database state must be wiped.
// * After an IO error (Status::IsIOError() returns true), all operations on the
// scope must cease, commit cannot be called, but the scope can attempt to be
// reverted. If the revert fails for any reason, this is treated as a
// corruption. A better route is to immediately abort all usage of the leveldb
// state and close the database. A fresh database open could resolve the IO
// error.
//
// This class is not thread safe, and should be used on the same sequence as
// |LevelDBScopes::CreateScope|.
class LevelDBScope {
public:
using RollbackCallback =
base::OnceCallback<void(int64_t scope_id,
std::vector<PartitionedLock> locks)>;
using TearDownCallback = base::RepeatingCallback<void(leveldb::Status)>;
using CleanupCallback = base::OnceCallback<void(int64_t scope_id)>;
LevelDBScope(const LevelDBScope&) = delete;
LevelDBScope& operator=(const LevelDBScope&) = delete;
~LevelDBScope();
int64_t scope_id() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return scope_id_;
}
[[nodiscard]] leveldb::Status Put(const leveldb::Slice& key,
const leveldb::Slice& value);
[[nodiscard]] leveldb::Status Delete(const leveldb::Slice& key);
// Deletes the range. |begin| is always inclusive. See
// |LevelDBScopeDeletionMode| for the different types of range deletion.
[[nodiscard]] leveldb::Status DeleteRange(const leveldb::Slice& begin,
const leveldb::Slice& end,
LevelDBScopeDeletionMode mode);
// Submits pending changes & the undo log to LevelDB. Required to be able to
// read any keys that have been submitted to |Put|, |Delete|, or
// |DeleteRange|.
[[nodiscard]] leveldb::Status WriteChangesAndUndoLog();
// In the case of LevelDBScopes being in the mode
// TaskRunnerMode::kUseCurrentSequence, rollbacks happen synchronously. The
// status of this possibly synchronous rollback is returned.
void Rollback();
uint64_t GetMemoryUsage() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return buffer_batch_.ApproximateSize();
}
private:
friend class LevelDBScopes;
class UndoLogWriter;
enum class Mode { kInMemory, kUndoLogOnDisk };
bool IsUndoLogMode() const { return mode_ == Mode::kUndoLogOnDisk; }
LevelDBScope(int64_t scope_id,
std::vector<uint8_t> prefix,
size_t write_batch_size,
scoped_refptr<LevelDBState> level_db,
std::vector<PartitionedLock> locks,
RollbackCallback rollback_callback);
// Called by LevelDBScopes. Saves all data, release all locks, and returns the
// status & the mode of this scope. The caller (LevelDBScopes) is expected to
// queue up a cleanup task if the mode is kUndoLogOnDisk. This instance should
// not be used after this call.
[[nodiscard]] std::pair<leveldb::Status, Mode> Commit(bool sync_on_commit);
// Submits pending changes & the undo log to LevelDB. Required to be able to
// read any keys that have been submitted to Put, Delete, or
// DeleteRange. |sync| makes the write a synchronous write.
leveldb::Status WriteChangesAndUndoLogInternal(bool sync);
void AddUndoPutTask(std::string key, std::string value);
void AddUndoDeleteTask(std::string key);
void AddUndoDeleteRangeTask(std::string begin, std::string end);
// Writes the current |undo_task_buffer_| to the |write_batch_|, and
// decrements the |undo_sequence_number_|.
void AddBufferedUndoTask();
void AddCleanupDeleteAndCompactRangeTask(std::string begin, std::string end);
// Writes the current |cleanup_task_buffer_| to the |write_batch_|, and
// decrements the |cleanup_sequence_number_|.
void AddBufferedCleanupTask();
void SetModeToUndoLog();
// Returns true if the the given key is either part of the scope metadata
// (starts with |prefix_|) or is in a known empty range. If true, then this
// key should NOT have an undo entry generated.
// On the first match of an empty range, the DELETE_RANGE undo log entry is
// written to the buffer_batch_. Since that range is known to be empty, all
// keys within it can be reversed by a delete range operation, and only one
// delete range per empty range is needed.
bool CanSkipWritingUndoEntry(const leveldb::Slice& key);
void AddCommitPoint();
[[nodiscard]] leveldb::Status WriteBufferBatch(bool sync);
#if DCHECK_IS_ON()
std::vector<std::pair<std::string, std::string>> deferred_delete_ranges_;
bool IsInDeferredDeletionRange(const leveldb::Slice& key);
#endif
SEQUENCE_CHECKER(sequence_checker_);
const int64_t scope_id_;
// The undo tasks are written in reverse, as they are executed in ascending
// order (and they need to be executed in reverse).
int64_t undo_sequence_number_ = leveldb_scopes::kFirstSequenceNumberToWrite;
// The cleanup tasks are written in order, and will be executed in the same
// order.
int64_t cleanup_sequence_number_ = 0ll;
Mode mode_ = Mode::kInMemory;
const std::vector<uint8_t> prefix_;
const size_t write_batch_size_;
const scoped_refptr<LevelDBState> level_db_;
std::vector<PartitionedLock> locks_;
RollbackCallback rollback_callback_;
leveldb::WriteBatch buffer_batch_;
bool buffer_batch_empty_ = true;
bool has_written_to_disk_ = false;
bool committed_ = false;
LevelDBScopesUndoTask undo_task_buffer_;
LevelDBScopesCleanupTask cleanup_task_buffer_;
ScopesEncoder key_encoder_;
std::string value_buffer_;
};
} // namespace content::indexed_db
#endif // COMPONENTS_SERVICES_STORAGE_INDEXED_DB_SCOPES_LEVELDB_SCOPE_H_
|