1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/extras/sqlite/sqlite_persistent_cookie_store.h"
6
7 #include <vector>
8
9 #include "base/compiler_specific.h"
10 #include "base/files/scoped_temp_dir.h"
11 #include "base/functional/bind.h"
12 #include "base/rand_util.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/synchronization/waitable_event.h"
15 #include "base/task/sequenced_task_runner.h"
16 #include "base/task/thread_pool.h"
17 #include "base/test/task_environment.h"
18 #include "base/time/time.h"
19 #include "base/timer/elapsed_timer.h"
20 #include "net/base/test_completion_callback.h"
21 #include "net/cookies/canonical_cookie.h"
22 #include "net/cookies/cookie_constants.h"
23 #include "net/extras/sqlite/cookie_crypto_delegate.h"
24 #include "net/log/net_log_with_source.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 #include "testing/perf/perf_result_reporter.h"
27 #include "url/gurl.h"
28
29 namespace net {
30
31 namespace {
32
33 const base::FilePath::CharType cookie_filename[] = FILE_PATH_LITERAL("Cookies");
34
35 static const int kNumDomains = 300;
36 static const int kCookiesPerDomain = 50;
37
38 // Prime number noticeably larger than kNumDomains or kCookiesPerDomain
39 // so that multiplying this number by an incrementing index and moduloing
40 // with those values will return semi-random results.
41 static const int kRandomSeed = 13093;
42 static_assert(kRandomSeed > 10 * kNumDomains,
43 "kRandomSeed not high enough for number of domains");
44 static_assert(kRandomSeed > 10 * kCookiesPerDomain,
45 "kRandomSeed not high enough for number of cookies per domain");
46
47 static constexpr char kMetricPrefixSQLPCS[] = "SQLitePersistentCookieStore.";
48 static constexpr char kMetricOperationDurationMs[] = "operation_duration";
49
SetUpSQLPCSReporter(const std::string & story)50 perf_test::PerfResultReporter SetUpSQLPCSReporter(const std::string& story) {
51 perf_test::PerfResultReporter reporter(kMetricPrefixSQLPCS, story);
52 reporter.RegisterImportantMetric(kMetricOperationDurationMs, "ms");
53 return reporter;
54 }
55
56 } // namespace
57
58 class SQLitePersistentCookieStorePerfTest : public testing::Test {
59 public:
SQLitePersistentCookieStorePerfTest()60 SQLitePersistentCookieStorePerfTest()
61 : test_start_(base::Time::Now()),
62 loaded_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
63 base::WaitableEvent::InitialState::NOT_SIGNALED),
64 key_loaded_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
65 base::WaitableEvent::InitialState::NOT_SIGNALED) {}
66
OnLoaded(std::vector<std::unique_ptr<CanonicalCookie>> cookies)67 void OnLoaded(std::vector<std::unique_ptr<CanonicalCookie>> cookies) {
68 cookies_.swap(cookies);
69 loaded_event_.Signal();
70 }
71
OnKeyLoaded(std::vector<std::unique_ptr<CanonicalCookie>> cookies)72 void OnKeyLoaded(std::vector<std::unique_ptr<CanonicalCookie>> cookies) {
73 cookies_.swap(cookies);
74 key_loaded_event_.Signal();
75 }
76
Load()77 void Load() {
78 store_->Load(base::BindOnce(&SQLitePersistentCookieStorePerfTest::OnLoaded,
79 base::Unretained(this)),
80 NetLogWithSource());
81 loaded_event_.Wait();
82 }
83
CookieFromIndices(int domain_num,int cookie_num)84 CanonicalCookie CookieFromIndices(int domain_num, int cookie_num) {
85 base::Time t(
86 test_start_ +
87 base::Microseconds(domain_num * kCookiesPerDomain + cookie_num));
88 std::string domain_name(base::StringPrintf(".domain_%d.com", domain_num));
89 return *CanonicalCookie::CreateUnsafeCookieForTesting(
90 base::StringPrintf("Cookie_%d", cookie_num), "1", domain_name, "/", t,
91 t, t, t, false, false, CookieSameSite::NO_RESTRICTION,
92 COOKIE_PRIORITY_DEFAULT);
93 }
94
SetUp()95 void SetUp() override {
96 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
97 store_ = base::MakeRefCounted<SQLitePersistentCookieStore>(
98 temp_dir_.GetPath().Append(cookie_filename), client_task_runner_,
99 background_task_runner_, /*restore_old_session_cookies=*/true,
100 /*crypto_delegate=*/nullptr, /*enable_exclusive_access=*/false);
101 std::vector<CanonicalCookie*> cookies;
102 Load();
103 ASSERT_EQ(0u, cookies_.size());
104 // Creates kNumDomains*kCookiesPerDomain cookies from kNumDomains eTLD+1s.
105 for (int domain_num = 0; domain_num < kNumDomains; domain_num++) {
106 for (int cookie_num = 0; cookie_num < kCookiesPerDomain; ++cookie_num) {
107 store_->AddCookie(CookieFromIndices(domain_num, cookie_num));
108 }
109 }
110 // Replace the store effectively destroying the current one and forcing it
111 // to write its data to disk.
112 store_ = nullptr;
113
114 // Flush ThreadPool tasks, causing pending commits to run.
115 task_environment_.RunUntilIdle();
116
117 store_ = base::MakeRefCounted<SQLitePersistentCookieStore>(
118 temp_dir_.GetPath().Append(cookie_filename), client_task_runner_,
119 background_task_runner_, /*restore_old_session_cookies=*/true,
120 /*crypto_delegate=*/nullptr, /*enable_exclusive_access=*/false);
121 }
122
123 // Pick a random cookie out of the 15000 in the store and return it.
124 // Note that this distribution is intended to be random for purposes of
125 // probing, but will be the same each time the test is run for
126 // reproducibility of performance.
RandomCookie()127 CanonicalCookie RandomCookie() {
128 int consistent_random_value = ++seed_multiple_ * kRandomSeed;
129 int domain = consistent_random_value % kNumDomains;
130 int cookie_num = consistent_random_value % kCookiesPerDomain;
131 return CookieFromIndices(domain, cookie_num);
132 }
133
TearDown()134 void TearDown() override { store_ = nullptr; }
135
StartPerfMeasurement()136 void StartPerfMeasurement() {
137 DCHECK(perf_measurement_start_.is_null());
138 perf_measurement_start_ = base::Time::Now();
139 }
140
EndPerfMeasurement(const std::string & story)141 void EndPerfMeasurement(const std::string& story) {
142 DCHECK(!perf_measurement_start_.is_null());
143 base::TimeDelta elapsed = base::Time::Now() - perf_measurement_start_;
144 perf_measurement_start_ = base::Time();
145 auto reporter = SetUpSQLPCSReporter(story);
146 reporter.AddResult(kMetricOperationDurationMs, elapsed.InMillisecondsF());
147 }
148
149 protected:
150 int seed_multiple_ = 1;
151 base::Time test_start_;
152 base::test::TaskEnvironment task_environment_;
153 const scoped_refptr<base::SequencedTaskRunner> background_task_runner_ =
154 base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()});
155 const scoped_refptr<base::SequencedTaskRunner> client_task_runner_ =
156 base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()});
157 base::WaitableEvent loaded_event_;
158 base::WaitableEvent key_loaded_event_;
159 std::vector<std::unique_ptr<CanonicalCookie>> cookies_;
160 base::ScopedTempDir temp_dir_;
161 scoped_refptr<SQLitePersistentCookieStore> store_;
162 base::Time perf_measurement_start_;
163 };
164
165 // Test the performance of priority load of cookies for a specific domain key
TEST_F(SQLitePersistentCookieStorePerfTest,TestLoadForKeyPerformance)166 TEST_F(SQLitePersistentCookieStorePerfTest, TestLoadForKeyPerformance) {
167 ASSERT_LT(3, kNumDomains);
168 for (int domain_num = 0; domain_num < 3; ++domain_num) {
169 std::string domain_name(base::StringPrintf("domain_%d.com", domain_num));
170 StartPerfMeasurement();
171 store_->LoadCookiesForKey(
172 domain_name,
173 base::BindOnce(&SQLitePersistentCookieStorePerfTest::OnKeyLoaded,
174 base::Unretained(this)));
175 key_loaded_event_.Wait();
176 EndPerfMeasurement("load_for_key");
177
178 ASSERT_EQ(50U, cookies_.size());
179 }
180 }
181
182 // Test the performance of load
TEST_F(SQLitePersistentCookieStorePerfTest,TestLoadPerformance)183 TEST_F(SQLitePersistentCookieStorePerfTest, TestLoadPerformance) {
184 StartPerfMeasurement();
185 Load();
186 EndPerfMeasurement("load");
187
188 ASSERT_EQ(kNumDomains * kCookiesPerDomain, static_cast<int>(cookies_.size()));
189 }
190
191 // Test deletion performance.
TEST_F(SQLitePersistentCookieStorePerfTest,TestDeletePerformance)192 TEST_F(SQLitePersistentCookieStorePerfTest, TestDeletePerformance) {
193 const int kNumToDelete = 50;
194 const int kNumIterations = 400;
195
196 // Figure out the kNumToDelete cookies.
197 std::vector<CanonicalCookie> cookies;
198 cookies.reserve(kNumToDelete);
199 for (int cookie = 0; cookie < kNumToDelete; ++cookie) {
200 cookies.push_back(RandomCookie());
201 }
202 ASSERT_EQ(static_cast<size_t>(kNumToDelete), cookies.size());
203
204 StartPerfMeasurement();
205 for (int i = 0; i < kNumIterations; ++i) {
206 // Delete and flush
207 for (int cookie = 0; cookie < kNumToDelete; ++cookie) {
208 store_->DeleteCookie(cookies[cookie]);
209 }
210 {
211 TestClosure test_closure;
212 store_->Flush(test_closure.closure());
213 test_closure.WaitForResult();
214 }
215
216 // Add and flush
217 for (int cookie = 0; cookie < kNumToDelete; ++cookie) {
218 store_->AddCookie(cookies[cookie]);
219 }
220
221 TestClosure test_closure;
222 store_->Flush(test_closure.closure());
223 test_closure.WaitForResult();
224 }
225 EndPerfMeasurement("delete");
226 }
227
228 // Test update performance.
TEST_F(SQLitePersistentCookieStorePerfTest,TestUpdatePerformance)229 TEST_F(SQLitePersistentCookieStorePerfTest, TestUpdatePerformance) {
230 const int kNumToUpdate = 50;
231 const int kNumIterations = 400;
232
233 // Figure out the kNumToUpdate cookies.
234 std::vector<CanonicalCookie> cookies;
235 cookies.reserve(kNumToUpdate);
236 for (int cookie = 0; cookie < kNumToUpdate; ++cookie) {
237 cookies.push_back(RandomCookie());
238 }
239 ASSERT_EQ(static_cast<size_t>(kNumToUpdate), cookies.size());
240
241 StartPerfMeasurement();
242 for (int i = 0; i < kNumIterations; ++i) {
243 // Update and flush
244 for (int cookie = 0; cookie < kNumToUpdate; ++cookie) {
245 store_->UpdateCookieAccessTime(cookies[cookie]);
246 }
247
248 TestClosure test_closure;
249 store_->Flush(test_closure.closure());
250 test_closure.WaitForResult();
251 }
252 EndPerfMeasurement("update");
253 }
254
255 } // namespace net
256