File: leveldb_scope.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (190 lines) | stat: -rw-r--r-- 8,302 bytes parent folder | download | duplicates (5)
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_