Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4)

Side by Side Diff: storage/browser/quota/quota_manager.cc

Issue 1782053004: Change how the quota system computes the total poolsize for temporary storage (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "storage/browser/quota/quota_manager.h" 5 #include "storage/browser/quota/quota_manager.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 #include <stdint.h> 8 #include <stdint.h>
9 #include <algorithm> 9 #include <algorithm>
10 #include <functional> 10 #include <functional>
11 #include <limits> 11 #include <limits>
12 #include <utility> 12 #include <utility>
13 13
14 #include "base/bind.h" 14 #include "base/bind.h"
15 #include "base/bind_helpers.h" 15 #include "base/bind_helpers.h"
16 #include "base/command_line.h" 16 #include "base/command_line.h"
17 #include "base/files/file_util.h" 17 #include "base/files/file_util.h"
18 #include "base/macros.h" 18 #include "base/macros.h"
19 #include "base/metrics/histogram.h" 19 #include "base/metrics/histogram.h"
20 #include "base/profiler/scoped_tracker.h" 20 #include "base/profiler/scoped_tracker.h"
21 #include "base/sequenced_task_runner.h" 21 #include "base/sequenced_task_runner.h"
22 #include "base/single_thread_task_runner.h" 22 #include "base/single_thread_task_runner.h"
23 #include "base/strings/string_number_conversions.h" 23 #include "base/strings/string_number_conversions.h"
24 #include "base/sys_info.h" 24 #include "base/sys_info.h"
25 #include "base/task_runner_util.h" 25 #include "base/task_runner_util.h"
26 #include "base/thread_task_runner_handle.h"
26 #include "base/time/time.h" 27 #include "base/time/time.h"
27 #include "base/trace_event/trace_event.h" 28 #include "base/trace_event/trace_event.h"
28 #include "net/base/url_util.h" 29 #include "net/base/url_util.h"
29 #include "storage/browser/quota/client_usage_tracker.h" 30 #include "storage/browser/quota/client_usage_tracker.h"
30 #include "storage/browser/quota/quota_manager_proxy.h" 31 #include "storage/browser/quota/quota_manager_proxy.h"
31 #include "storage/browser/quota/quota_temporary_storage_evictor.h" 32 #include "storage/browser/quota/quota_temporary_storage_evictor.h"
32 #include "storage/browser/quota/storage_monitor.h" 33 #include "storage/browser/quota/storage_monitor.h"
33 #include "storage/browser/quota/usage_tracker.h" 34 #include "storage/browser/quota/usage_tracker.h"
34 #include "storage/common/quota/quota_types.h" 35 #include "storage/common/quota/quota_types.h"
35 36
(...skipping 15 matching lines...) Expand all
51 1, 10 * 1024 * 1024 /* 10TB */, 100) 52 1, 10 * 1024 * 1024 /* 10TB */, 100)
52 53
53 namespace storage { 54 namespace storage {
54 55
55 namespace { 56 namespace {
56 57
57 const int64_t kMBytes = 1024 * 1024; 58 const int64_t kMBytes = 1024 * 1024;
58 const int kMinutesInMilliSeconds = 60 * 1000; 59 const int kMinutesInMilliSeconds = 60 * 1000;
59 60
60 const int64_t kReportHistogramInterval = 60 * 60 * 1000; // 1 hour 61 const int64_t kReportHistogramInterval = 60 * 60 * 1000; // 1 hour
61 const double kTemporaryQuotaRatioToAvail = 1.0 / 3.0; // 33% 62 const double kTemporaryQuotaRatio = 1.0 / 3.0; // 33%
jsbell 2016/03/23 18:08:28 While you're here, can you comment on what these a
michaeln 2016/04/07 00:32:34 Done, also moved these values to where they're use
63
64 #if defined(OS_ANDROID)
65 const uint64_t kOsAccomodation = 250 * kMBytes;
michaeln 2016/03/22 22:23:55 these specific numbers are just placeholders, this
66 #elif defined(OS_CHROMEOS)
67 const uint64_t kOsAccomodation = 1000 * kMBytes;
68 #else
69 const uint64_t kOsAccomodation = 10000 * kMBytes;
70 #endif
62 71
63 } // namespace 72 } // namespace
64 73
65 // Arbitrary for now, but must be reasonably small so that 74 // Arbitrary for now, but must be reasonably small so that
66 // in-memory databases can fit. 75 // in-memory databases can fit.
67 // TODO(kinuko): Refer SysInfo::AmountOfPhysicalMemory() to determine this. 76 // TODO(kinuko): Refer SysInfo::AmountOfPhysicalMemory() to determine this.
68 const int64_t QuotaManager::kIncognitoDefaultQuotaLimit = 100 * kMBytes; 77 const int64_t QuotaManager::kIncognitoDefaultQuotaLimit = 100 * kMBytes;
69 78
70 const int64_t QuotaManager::kNoLimit = INT64_MAX; 79 const int64_t QuotaManager::kNoLimit = INT64_MAX;
71 80
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after
236 } 245 }
237 246
238 bool UpdateModifiedTimeOnDBThread(const GURL& origin, 247 bool UpdateModifiedTimeOnDBThread(const GURL& origin,
239 StorageType type, 248 StorageType type,
240 base::Time modified_time, 249 base::Time modified_time,
241 QuotaDatabase* database) { 250 QuotaDatabase* database) {
242 DCHECK(database); 251 DCHECK(database);
243 return database->SetOriginLastModifiedTime(origin, type, modified_time); 252 return database->SetOriginLastModifiedTime(origin, type, modified_time);
244 } 253 }
245 254
246 int64_t CalculateTemporaryGlobalQuota(int64_t global_limited_usage,
247 int64_t available_space) {
248 DCHECK_GE(global_limited_usage, 0);
249 int64_t avail_space = available_space;
250 if (avail_space <
251 std::numeric_limits<int64_t>::max() - global_limited_usage) {
252 // We basically calculate the temporary quota by
253 // [available_space + space_used_for_temp] * kTempQuotaRatio,
254 // but make sure we'll have no overflow.
255 avail_space += global_limited_usage;
256 }
257 int64_t pool_size = avail_space * kTemporaryQuotaRatioToAvail;
258 UMA_HISTOGRAM_MBYTES("Quota.GlobalTemporaryPoolSize", pool_size);
259 return pool_size;
260 }
261
262 void DispatchTemporaryGlobalQuotaCallback(
263 const QuotaCallback& callback,
264 QuotaStatusCode status,
265 const UsageAndQuota& usage_and_quota) {
266 if (status != kQuotaStatusOk) {
267 callback.Run(status, 0);
268 return;
269 }
270
271 callback.Run(status, CalculateTemporaryGlobalQuota(
272 usage_and_quota.global_limited_usage,
273 usage_and_quota.available_disk_space));
274 }
275
276 int64_t CalculateQuotaWithDiskSpace(int64_t available_disk_space, 255 int64_t CalculateQuotaWithDiskSpace(int64_t available_disk_space,
277 int64_t usage, 256 int64_t usage,
278 int64_t quota) { 257 int64_t quota) {
279 if (available_disk_space < QuotaManager::kMinimumPreserveForSystem) { 258 if (available_disk_space < QuotaManager::kMinimumPreserveForSystem) {
280 LOG(WARNING) 259 LOG(WARNING)
281 << "Running out of disk space for profile." 260 << "Running out of disk space for profile."
282 << " QuotaManager starts forbidding further quota consumption."; 261 << " QuotaManager starts forbidding further quota consumption.";
283 return usage; 262 return usage;
284 } 263 }
285 264
286 if (quota < usage) { 265 if (quota < usage) {
287 // No more space; cap the quota to the current usage. 266 // No more space; cap the quota to the current usage.
288 return usage; 267 return usage;
289 } 268 }
290 269
291 available_disk_space -= QuotaManager::kMinimumPreserveForSystem; 270 available_disk_space -= QuotaManager::kMinimumPreserveForSystem;
292 if (available_disk_space < quota - usage) 271 if (available_disk_space < quota - usage)
293 return available_disk_space + usage; 272 return available_disk_space + usage;
294 273
295 return quota; 274 return quota;
296 } 275 }
297 276
298 int64_t CalculateTemporaryHostQuota(int64_t host_usage, 277 int64_t CalculateTemporaryHostQuota(int64_t host_usage,
299 int64_t global_quota, 278 int64_t global_quota,
300 int64_t global_limited_usage) { 279 int64_t available_disk_space) {
301 DCHECK_GE(global_limited_usage, 0); 280 const int64_t desired_quota = global_quota /
302 int64_t host_quota = global_quota / QuotaManager::kPerHostTemporaryPortion; 281 QuotaManager::kPerHostTemporaryPortion;
303 if (global_limited_usage > global_quota) 282
michaeln 2016/03/22 21:40:15 This used to defend against filling the device.
304 host_quota = std::min(host_quota, host_usage); 283 // We define "too low" as not enough room for another hosts
305 return host_quota; 284 // nominal desired amount of data. (TODO: what should this be)
michaeln 2016/03/22 21:40:15 ~66 MBytes for small andoid devcices and ~66GB for
jsbell 2016/03/23 18:08:29 Would the "save a bit of room for non-Chrome usage
michaeln 2016/04/07 00:32:34 This is one of the two places where logic to "save
285 const int64_t too_low_threshold = desired_quota;
286
287 // If disk space is too low, cap usage at current levels.
288 if (available_disk_space < too_low_threshold)
michaeln 2016/03/22 21:40:15 That defense is replaced with this.
289 return std::min(desired_quota, host_usage);
290
291 // If its close to being too low, cap growth to avoid it getting too low.
292 int64_t remaining_quota = std::min(INT64_C(0), desired_quota - host_usage);
jsbell 2016/03/23 18:08:28 I think this might be clearer as: return std::min
michaeln 2016/04/07 00:32:34 I think your right. I had thought a variable named
293 if (available_disk_space < remaining_quota + too_low_threshold) {
294 remaining_quota = available_disk_space - too_low_threshold;
295 return host_usage + remaining_quota;
296 }
297
298 return desired_quota;
306 } 299 }
307 300
308 void DispatchUsageAndQuotaForWebApps( 301 void DispatchUsageAndQuotaForWebApps(
309 StorageType type, 302 StorageType type,
310 bool is_incognito, 303 bool is_incognito,
311 bool is_unlimited, 304 bool is_unlimited,
312 bool can_query_disk_size, 305 bool can_query_disk_size,
313 const QuotaManager::GetUsageAndQuotaCallback& callback, 306 const QuotaManager::GetUsageAndQuotaCallback& callback,
314 QuotaStatusCode status, 307 QuotaStatusCode status,
315 const UsageAndQuota& usage_and_quota) { 308 const UsageAndQuota& usage_and_quota) {
316 if (status != kQuotaStatusOk) { 309 if (status != kQuotaStatusOk) {
317 callback.Run(status, 0, 0); 310 callback.Run(status, 0, 0);
318 return; 311 return;
319 } 312 }
320 313
321 int64_t usage = usage_and_quota.usage; 314 int64_t host_usage = usage_and_quota.usage;
322 int64_t quota = usage_and_quota.quota; 315 int64_t global_quota = usage_and_quota.quota;
316 int64_t host_quota = global_quota;
323 317
324 if (type == kStorageTypeTemporary && !is_unlimited) { 318 if (type == kStorageTypeTemporary && !is_unlimited) {
325 quota = CalculateTemporaryHostQuota( 319 host_quota = CalculateTemporaryHostQuota(
326 usage, quota, usage_and_quota.global_limited_usage); 320 host_usage, global_quota, usage_and_quota.available_disk_space);
327 } 321 }
328 322
329 if (is_incognito) { 323 if (is_incognito) {
330 quota = std::min(quota, QuotaManager::kIncognitoDefaultQuotaLimit); 324 host_quota = std::min(host_quota,
331 callback.Run(status, usage, quota); 325 QuotaManager::kIncognitoDefaultQuotaLimit);
326 callback.Run(status, host_usage, host_quota);
332 return; 327 return;
333 } 328 }
334 329
335 // For apps with unlimited permission or can_query_disk_size is true (and not 330 // For apps with unlimited permission or can_query_disk_size is true (and not
336 // in incognito mode). 331 // in incognito mode).
337 // We assume we can expose the actual disk size for them and cap the quota by 332 // We assume we can expose the actual disk size for them and cap the quota by
338 // the available disk space. 333 // the available disk space.
339 if (is_unlimited || can_query_disk_size) { 334 if (is_unlimited || can_query_disk_size) {
340 callback.Run( 335 callback.Run(
341 status, usage, 336 status, host_usage,
342 CalculateQuotaWithDiskSpace( 337 CalculateQuotaWithDiskSpace(
343 usage_and_quota.available_disk_space, 338 usage_and_quota.available_disk_space,
344 usage, quota)); 339 host_usage, host_quota));
345 return; 340 return;
346 } 341 }
347 342
348 callback.Run(status, usage, quota); 343 callback.Run(status, host_usage, host_quota);
349 } 344 }
350 345
351 } // namespace 346 } // namespace
352 347
353 UsageAndQuota::UsageAndQuota() 348 UsageAndQuota::UsageAndQuota()
354 : usage(0), 349 : usage(0),
355 global_limited_usage(0), 350 global_limited_usage(0),
356 quota(0), 351 quota(0),
357 available_disk_space(0) { 352 available_disk_space(0) {
358 } 353 }
(...skipping 559 matching lines...) Expand 10 before | Expand all | Expand 10 after
918 bool unlimited = IsStorageUnlimited(origin, type); 913 bool unlimited = IsStorageUnlimited(origin, type);
919 bool can_query_disk_size = CanQueryDiskSize(origin); 914 bool can_query_disk_size = CanQueryDiskSize(origin);
920 915
921 UsageAndQuotaCallbackDispatcher* dispatcher = 916 UsageAndQuotaCallbackDispatcher* dispatcher =
922 new UsageAndQuotaCallbackDispatcher(this); 917 new UsageAndQuotaCallbackDispatcher(this);
923 918
924 if (unlimited) { 919 if (unlimited) {
925 dispatcher->set_quota(kNoLimit); 920 dispatcher->set_quota(kNoLimit);
926 } else { 921 } else {
927 if (type == kStorageTypeTemporary) { 922 if (type == kStorageTypeTemporary) {
928 GetUsageTracker(type)->GetGlobalLimitedUsage(
929 dispatcher->GetGlobalLimitedUsageCallback());
930 GetTemporaryGlobalQuota(dispatcher->GetQuotaCallback()); 923 GetTemporaryGlobalQuota(dispatcher->GetQuotaCallback());
931 } else if (type == kStorageTypePersistent) { 924 } else if (type == kStorageTypePersistent) {
932 GetPersistentHostQuota(net::GetHostOrSpecFromURL(origin), 925 GetPersistentHostQuota(net::GetHostOrSpecFromURL(origin),
933 dispatcher->GetQuotaCallback()); 926 dispatcher->GetQuotaCallback());
934 } else { 927 } else {
935 dispatcher->set_quota(kSyncableStorageDefaultHostQuota); 928 dispatcher->set_quota(kSyncableStorageDefaultHostQuota);
936 } 929 }
937 } 930 }
938 931
939 DCHECK(GetUsageTracker(type)); 932 DCHECK(GetUsageTracker(type));
940 GetUsageTracker(type)->GetHostUsage(net::GetHostOrSpecFromURL(origin), 933 GetUsageTracker(type)->GetHostUsage(net::GetHostOrSpecFromURL(origin),
941 dispatcher->GetHostUsageCallback()); 934 dispatcher->GetHostUsageCallback());
942 935
943 if (!is_incognito_ && (unlimited || can_query_disk_size)) 936 GetAvailableSpace(dispatcher->GetAvailableSpaceCallback());
jsbell 2016/03/23 18:08:29 Can we avoid this if is_incognito_ like we used to
michaeln 2016/04/07 00:32:34 that's a good question, the system is currently us
944 GetAvailableSpace(dispatcher->GetAvailableSpaceCallback());
945 937
946 dispatcher->WaitForResults(base::Bind( 938 dispatcher->WaitForResults(base::Bind(
947 &DispatchUsageAndQuotaForWebApps, 939 &DispatchUsageAndQuotaForWebApps,
948 type, is_incognito_, unlimited, can_query_disk_size, 940 type, is_incognito_, unlimited, can_query_disk_size,
949 callback)); 941 callback));
950 } 942 }
951 943
952 void QuotaManager::GetUsageAndQuota( 944 void QuotaManager::GetUsageAndQuota(
953 const GURL& origin, StorageType type, 945 const GURL& origin, StorageType type,
954 const GetUsageAndQuotaCallback& callback) { 946 const GetUsageAndQuotaCallback& callback) {
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
1050 &QuotaManager::GetTemporaryGlobalQuota, 1042 &QuotaManager::GetTemporaryGlobalQuota,
1051 weak_factory_.GetWeakPtr(), callback)); 1043 weak_factory_.GetWeakPtr(), callback));
1052 return; 1044 return;
1053 } 1045 }
1054 1046
1055 if (temporary_quota_override_ > 0) { 1047 if (temporary_quota_override_ > 0) {
1056 callback.Run(kQuotaStatusOk, temporary_quota_override_); 1048 callback.Run(kQuotaStatusOk, temporary_quota_override_);
1057 return; 1049 return;
1058 } 1050 }
1059 1051
1060 UsageAndQuotaCallbackDispatcher* dispatcher = 1052 db_thread_->PostTask(
1061 new UsageAndQuotaCallbackDispatcher(this); 1053 FROM_HERE,
1062 GetUsageTracker(kStorageTypeTemporary)-> 1054 base::Bind(&QuotaManager::CalculateTemporaryPoolSize,
1063 GetGlobalLimitedUsage(dispatcher->GetGlobalLimitedUsageCallback()); 1055 get_volume_info_fn_, profile_path_, callback,
1064 GetAvailableSpace(dispatcher->GetAvailableSpaceCallback()); 1056 base::ThreadTaskRunnerHandle::Get()));
1065 dispatcher->WaitForResults(
1066 base::Bind(&DispatchTemporaryGlobalQuotaCallback, callback));
1067 } 1057 }
1068 1058
1069 void QuotaManager::SetTemporaryGlobalOverrideQuota( 1059 void QuotaManager::SetTemporaryGlobalOverrideQuota(
1070 int64_t new_quota, 1060 int64_t new_quota,
1071 const QuotaCallback& callback) { 1061 const QuotaCallback& callback) {
1072 LazyInitialize(); 1062 LazyInitialize();
1073 1063
1074 if (new_quota < 0) { 1064 if (new_quota < 0) {
1075 if (!callback.is_null()) 1065 if (!callback.is_null())
1076 callback.Run(kQuotaErrorInvalidModification, -1); 1066 callback.Run(kQuotaErrorInvalidModification, -1);
(...skipping 712 matching lines...) Expand 10 before | Expand all | Expand 10 after
1789 // |database_|, therefore we can be sure that database_ is alive when this 1779 // |database_|, therefore we can be sure that database_ is alive when this
1790 // task runs. 1780 // task runs.
1791 base::PostTaskAndReplyWithResult( 1781 base::PostTaskAndReplyWithResult(
1792 db_thread_.get(), 1782 db_thread_.get(),
1793 from_here, 1783 from_here,
1794 base::Bind(task, base::Unretained(database_.get())), 1784 base::Bind(task, base::Unretained(database_.get())),
1795 reply); 1785 reply);
1796 } 1786 }
1797 1787
1798 // static 1788 // static
1789 void QuotaManager::CalculateTemporaryPoolSize(
1790 GetVolumeInfoFn get_volume_info_fn,
1791 const base::FilePath& profile_path,
1792 const QuotaCallback& callback,
1793 const scoped_refptr<base::TaskRunner>& reply_runner) {
1794 QuotaStatusCode status_code = kQuotaStatusUnknown;
1795 int64_t pool_size = 0;
1796 uint64_t available, total;
1797 if (get_volume_info_fn(profile_path, &available, &total)) {
1798 status_code = kQuotaStatusOk;
1799 if (total > kOsAccomodation)
1800 total -= kOsAccomodation; // todo: what if total is super small???
jsbell 2016/03/23 18:08:28 Maybe we want a threshold, e.g. os_reserve = min(k
michaeln 2016/04/07 00:32:34 Done
1801 pool_size = static_cast<int64_t>(total * kTemporaryQuotaRatio);
1802 UMA_HISTOGRAM_MBYTES("Quota.GlobalTemporaryPoolSize", pool_size);
1803 }
1804 reply_runner->PostTask(
1805 FROM_HERE, base::Bind(callback, status_code, pool_size));
1806 }
1807
1808 // static
1799 int64_t QuotaManager::CallGetAmountOfFreeDiskSpace( 1809 int64_t QuotaManager::CallGetAmountOfFreeDiskSpace(
1800 GetVolumeInfoFn get_volume_info_fn, 1810 GetVolumeInfoFn get_volume_info_fn,
1801 const base::FilePath& profile_path) { 1811 const base::FilePath& profile_path) {
1802 // crbug.com/349708 1812 // crbug.com/349708
1803 TRACE_EVENT0("io", "CallSystemGetAmountOfFreeDiskSpace"); 1813 TRACE_EVENT0("io", "CallSystemGetAmountOfFreeDiskSpace");
1804 if (!base::CreateDirectory(profile_path)) { 1814 if (!base::CreateDirectory(profile_path)) {
1805 LOG(WARNING) << "Create directory failed for path" << profile_path.value(); 1815 LOG(WARNING) << "Create directory failed for path" << profile_path.value();
1806 return 0; 1816 return 0;
1807 } 1817 }
1808 uint64_t available, total; 1818 uint64_t available, total;
(...skipping 23 matching lines...) Expand all
1832 return false; 1842 return false;
1833 *available_space = static_cast<uint64_t>(stats.f_bavail) * stats.f_frsize; 1843 *available_space = static_cast<uint64_t>(stats.f_bavail) * stats.f_frsize;
1834 *total_size = static_cast<uint64_t>(stats.f_blocks) * stats.f_frsize; 1844 *total_size = static_cast<uint64_t>(stats.f_blocks) * stats.f_frsize;
1835 #else 1845 #else
1836 #error Not implemented 1846 #error Not implemented
1837 #endif 1847 #endif
1838 return true; 1848 return true;
1839 } 1849 }
1840 1850
1841 } // namespace storage 1851 } // namespace storage
OLDNEW
« storage/browser/quota/quota_manager.h ('K') | « storage/browser/quota/quota_manager.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698