Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / android / history_report / delta_file_backend_leveldb.cc
blobc1903cef2df5b598bdbc1dd63bf13d9104a187bc
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"
7 #include <inttypes.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"
20 #include "url/gurl.h"
22 namespace {
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();
30 int64 seq_no = 0;
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();
37 return 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);
44 entry.set_type(type);
45 entry.set_url(url);
46 leveldb::WriteOptions writeOptions;
47 std::string key;
48 base::SStringPrintf(&key, "%" PRId64, seq_no);
49 leveldb::Status status = db->Put(
50 writeOptions,
51 leveldb::Slice(key),
52 leveldb::Slice(entry.SerializeAsString()));
53 if (!status.ok())
54 LOG(WARNING) << "Save Change failed " << status.ToString();
57 } // namespace
59 namespace history_report {
61 // Comparator used in leveldb.
62 class DeltaFileBackend::DigitsComparator : public leveldb::Comparator {
63 public:
64 int Compare(const leveldb::Slice& a,
65 const leveldb::Slice& b) const override {
66 int64 first;
67 int64 second;
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;
73 return 0;
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);
101 if (status.ok()) {
102 CHECK(db);
103 db_.reset(db);
104 return true;
106 LOG(WARNING) << "Unable to open " << path_.value() << ": "
107 << status.ToString();
108 return false;
111 bool DeltaFileBackend::EnsureInitialized() {
112 if (db_.get()) return true;
113 return Init();
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())
132 return -1;
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()))
136 return -1;
137 int64 min_seq_no = first_entry.seq_no();
138 db_iter->SeekToLast();
139 if (!db_iter->Valid())
140 return -1;
141 history_report::DeltaFileEntry last_entry;
142 value_slice = db_iter->value();
143 if (!last_entry.ParseFromArray(value_slice.data(), value_slice.size()))
144 return -1;
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) {
152 std::string key;
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);
159 if (status.ok())
160 return max_seq_no;
161 LOG(WARNING) << "Trim failed: " << status.ToString();
162 return -1;
165 bool DeltaFileBackend::Recreate(const std::vector<std::string>& urls) {
166 if (!EnsureInitialized()) return false;
167 Clear();
168 int64 seq_no = 1;
169 leveldb::WriteBatch updates;
170 for (std::vector<std::string>::const_iterator it = urls.begin();
171 it != urls.end();
172 ++it) {
173 DeltaFileEntry entry;
174 entry.set_seq_no(seq_no);
175 entry.set_url(*it);
176 entry.set_type("add");
177 std::string key;
178 base::SStringPrintf(&key, "%" PRId64, seq_no);
179 updates.Put(leveldb::Slice(key),
180 leveldb::Slice(entry.SerializeAsString()));
181 ++seq_no;
183 leveldb::WriteOptions options;
184 leveldb::Status status = db_->Write(options, &updates);
185 if (status.ok())
186 return true;
187 LOG(WARNING) << "Recreate failed: " << status.ToString();
188 return false;
191 scoped_ptr<std::vector<DeltaFileEntryWithData> > DeltaFileBackend::Query(
192 int64 last_seq_no,
193 int32 limit) {
194 if (!EnsureInitialized())
195 return make_scoped_ptr(new std::vector<DeltaFileEntryWithData>());
196 std::string start;
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>());
202 int32 count = 0;
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()))
207 continue;
208 result->push_back(DeltaFileEntryWithData(entry));
209 ++count;
211 return result.Pass();
214 void DeltaFileBackend::Clear() {
215 if (!EnsureInitialized()) return;
216 db_.reset();
217 base::DeleteFile(path_, true);
218 Init();
221 std::string DeltaFileBackend::Dump() {
222 std::string dump("\n Delta File [");
223 if (!EnsureInitialized()) {
224 dump.append("not initialized]");
225 return dump;
227 dump.append("num pending entries=");
228 leveldb::ReadOptions options;
229 scoped_ptr<leveldb::Iterator> db_it(db_->NewIterator(options));
230 int num_entries = 0;
231 for (db_it->SeekToFirst(); db_it->Valid(); db_it->Next()) num_entries++;
232 dump.append(base::IntToString(num_entries));
233 dump.append("]");
234 return dump;
237 } // namespace history_report