1 // Copyright 2015 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.
5 #include "chrome/browser/android/history_report/delta_file_backend_leveldb.h"
8 #include "base/files/file_util.h"
9 #include "base/logging.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/stringprintf.h"
12 #include "chrome/browser/android/history_report/delta_file_commons.h"
13 #include "third_party/leveldatabase/src/include/leveldb/comparator.h"
14 #include "third_party/leveldatabase/src/include/leveldb/db.h"
15 #include "third_party/leveldatabase/src/include/leveldb/iterator.h"
16 #include "third_party/leveldatabase/src/include/leveldb/options.h"
17 #include "third_party/leveldatabase/src/include/leveldb/slice.h"
18 #include "third_party/leveldatabase/src/include/leveldb/status.h"
19 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
23 const base::FilePath::CharType kDbFileName
[] =
24 FILE_PATH_LITERAL("DeltaFileLevelDb");
26 int64
GetLastSeqNo(leveldb::DB
* db
) {
27 leveldb::ReadOptions options
;
28 scoped_ptr
<leveldb::Iterator
> db_iter(db
->NewIterator(options
));
29 db_iter
->SeekToLast();
31 if (db_iter
->Valid()) {
32 history_report::DeltaFileEntry last_entry
;
33 leveldb::Slice value_slice
= db_iter
->value();
34 if (last_entry
.ParseFromArray(value_slice
.data(), value_slice
.size()))
35 seq_no
= last_entry
.seq_no();
40 void SaveChange(leveldb::DB
* db
, std::string url
, std::string type
) {
41 int64 seq_no
= GetLastSeqNo(db
) + 1;
42 history_report::DeltaFileEntry entry
;
43 entry
.set_seq_no(seq_no
);
46 leveldb::WriteOptions writeOptions
;
48 base::SStringPrintf(&key
, "%" PRId64
, seq_no
);
49 leveldb::Status status
= db
->Put(
52 leveldb::Slice(entry
.SerializeAsString()));
54 LOG(WARNING
) << "Save Change failed " << status
.ToString();
59 namespace history_report
{
61 // Comparator used in leveldb.
62 class DeltaFileBackend::DigitsComparator
: public leveldb::Comparator
{
64 int Compare(const leveldb::Slice
& a
,
65 const leveldb::Slice
& b
) const override
{
68 // Keys which can't be parsed go to the end.
69 if (!base::StringToInt64(a
.ToString(), &first
)) return 1;
70 if (!base::StringToInt64(b
.ToString(), &second
)) return -1;
71 if (first
< second
) return -1;
72 if (first
> second
) return 1;
75 const char* Name() const override
{ return "DigitsComparator"; }
76 void FindShortestSeparator(std::string
*,
77 const leveldb::Slice
&) const override
{ }
78 void FindShortSuccessor(std::string
*) const override
{ }
81 DeltaFileBackend::DeltaFileBackend(const base::FilePath
& dir
)
82 : path_(dir
.Append(kDbFileName
)),
83 leveldb_cmp_(new DeltaFileBackend::DigitsComparator()) {
86 DeltaFileBackend::~DeltaFileBackend() {}
88 bool DeltaFileBackend::Init() {
89 leveldb::Options options
;
90 options
.create_if_missing
= true;
91 options
.max_open_files
= 0; // Use minimum number of files.
92 options
.comparator
= leveldb_cmp_
.get();
93 std::string path
= path_
.value();
94 leveldb::DB
* db
= NULL
;
95 leveldb::Status status
= leveldb::DB::Open(options
, path
, &db
);
96 if (status
.IsCorruption()) {
97 LOG(WARNING
) << "Deleting possibly-corrupt database";
98 base::DeleteFile(path_
, true);
99 status
= leveldb::DB::Open(options
, path
, &db
);
106 LOG(WARNING
) << "Unable to open " << path_
.value() << ": "
107 << status
.ToString();
111 bool DeltaFileBackend::EnsureInitialized() {
112 if (db_
.get()) return true;
116 void DeltaFileBackend::PageAdded(const GURL
& url
) {
117 if (!EnsureInitialized()) return;
118 SaveChange(db_
.get(), url
.spec().c_str(), "add");
121 void DeltaFileBackend::PageDeleted(const GURL
& url
) {
122 if (!EnsureInitialized()) return;
123 SaveChange(db_
.get(), url
.spec().c_str(), "del");
126 int64
DeltaFileBackend::Trim(int64 lower_bound
) {
127 if (!EnsureInitialized()) return -1;
128 leveldb::ReadOptions read_options
;
129 scoped_ptr
<leveldb::Iterator
> db_iter(db_
->NewIterator(read_options
));
130 db_iter
->SeekToFirst();
131 if (!db_iter
->Valid())
133 history_report::DeltaFileEntry first_entry
;
134 leveldb::Slice value_slice
= db_iter
->value();
135 if (!first_entry
.ParseFromArray(value_slice
.data(), value_slice
.size()))
137 int64 min_seq_no
= first_entry
.seq_no();
138 db_iter
->SeekToLast();
139 if (!db_iter
->Valid())
141 history_report::DeltaFileEntry last_entry
;
142 value_slice
= db_iter
->value();
143 if (!last_entry
.ParseFromArray(value_slice
.data(), value_slice
.size()))
145 int64 max_seq_no
= last_entry
.seq_no();
146 // We want to have at least one entry in delta file left to know
147 // last sequence number in SaveChange.
148 if (max_seq_no
<= lower_bound
)
149 lower_bound
= max_seq_no
- 1;
150 leveldb::WriteBatch updates
;
151 for (int64 seq_no
= min_seq_no
; seq_no
<= lower_bound
; ++seq_no
) {
153 base::SStringPrintf(&key
, "%" PRId64
, seq_no
);
154 updates
.Delete(leveldb::Slice(key
));
157 leveldb::WriteOptions write_options
;
158 leveldb::Status status
= db_
->Write(write_options
, &updates
);
161 LOG(WARNING
) << "Trim failed: " << status
.ToString();
165 bool DeltaFileBackend::Recreate(const std::vector
<std::string
>& urls
) {
166 if (!EnsureInitialized()) return false;
169 leveldb::WriteBatch updates
;
170 for (std::vector
<std::string
>::const_iterator it
= urls
.begin();
173 DeltaFileEntry entry
;
174 entry
.set_seq_no(seq_no
);
176 entry
.set_type("add");
178 base::SStringPrintf(&key
, "%" PRId64
, seq_no
);
179 updates
.Put(leveldb::Slice(key
),
180 leveldb::Slice(entry
.SerializeAsString()));
183 leveldb::WriteOptions options
;
184 leveldb::Status status
= db_
->Write(options
, &updates
);
187 LOG(WARNING
) << "Recreate failed: " << status
.ToString();
191 scoped_ptr
<std::vector
<DeltaFileEntryWithData
> > DeltaFileBackend::Query(
194 if (!EnsureInitialized())
195 return make_scoped_ptr(new std::vector
<DeltaFileEntryWithData
>());
197 base::SStringPrintf(&start
, "%" PRId64
, last_seq_no
+ 1);
198 leveldb::ReadOptions options
;
199 scoped_ptr
<leveldb::Iterator
> db_it(db_
->NewIterator(options
));
200 scoped_ptr
<std::vector
<DeltaFileEntryWithData
> > result(
201 new std::vector
<DeltaFileEntryWithData
>());
203 for (db_it
->Seek(start
); db_it
->Valid() && count
< limit
; db_it
->Next()) {
204 DeltaFileEntry entry
;
205 leveldb::Slice value_slice
= db_it
->value();
206 if (!entry
.ParseFromArray(value_slice
.data(), value_slice
.size()))
208 result
->push_back(DeltaFileEntryWithData(entry
));
211 return result
.Pass();
214 void DeltaFileBackend::Clear() {
215 if (!EnsureInitialized()) return;
217 base::DeleteFile(path_
, true);
221 std::string
DeltaFileBackend::Dump() {
222 std::string
dump("\n Delta File [");
223 if (!EnsureInitialized()) {
224 dump
.append("not initialized]");
227 dump
.append("num pending entries=");
228 leveldb::ReadOptions options
;
229 scoped_ptr
<leveldb::Iterator
> db_it(db_
->NewIterator(options
));
231 for (db_it
->SeekToFirst(); db_it
->Valid(); db_it
->Next()) num_entries
++;
232 dump
.append(base::IntToString(num_entries
));
237 } // namespace history_report