Skip to content

Commit ad36957

Browse files
authored
Ignore errors enumerating files during GC (#5892)
1 parent 87a1b7d commit ad36957

12 files changed

+83
-27
lines changed

Firestore/CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
# Unreleased
22
- [fixed] Removed a delay that may have prevented Firestore from immediately
33
establishing a network connection if a connectivity change occurred while
4-
the app was in the background.
4+
the app was in the background (#5783).
5+
- [fixed] Fixed a rare crash that could happen if the garbage collection
6+
process for old documents in the cache happened to run during a LevelDB
7+
compaction (#5881).
58

69
# v1.16.0
710
- [fixed] Fixed an issue that may have prevented the client from connecting

Firestore/core/src/local/leveldb_lru_reference_delegate.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "Firestore/core/src/local/target_data.h"
2828
#include "Firestore/core/src/model/resource_path.h"
2929
#include "Firestore/core/src/model/types.h"
30+
#include "Firestore/core/src/util/statusor.h"
3031
#include "absl/memory/memory.h"
3132
#include "absl/strings/match.h"
3233

@@ -37,6 +38,7 @@ namespace local {
3738
using model::DocumentKey;
3839
using model::ListenSequenceNumber;
3940
using model::ResourcePath;
41+
using util::StatusOr;
4042

4143
LevelDbLruReferenceDelegate::LevelDbLruReferenceDelegate(
4244
LevelDbPersistence* persistence, LruParams lru_params)
@@ -104,7 +106,7 @@ LruGarbageCollector* LevelDbLruReferenceDelegate::garbage_collector() {
104106
return gc_.get();
105107
}
106108

107-
int64_t LevelDbLruReferenceDelegate::CalculateByteSize() {
109+
StatusOr<int64_t> LevelDbLruReferenceDelegate::CalculateByteSize() {
108110
return db_->CalculateByteSize();
109111
}
110112

Firestore/core/src/local/leveldb_lru_reference_delegate.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class LevelDbLruReferenceDelegate : public LruDelegate {
5757

5858
LruGarbageCollector* garbage_collector() override;
5959

60-
int64_t CalculateByteSize() override;
60+
util::StatusOr<int64_t> CalculateByteSize() override;
6161
size_t GetSequenceNumberCount() override;
6262

6363
void EnumerateTargets(const TargetCallback& callback) override;

Firestore/core/src/local/leveldb_persistence.cc

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ std::set<std::string> CollectUserSet(LevelDbTransaction* transaction) {
7676

7777
} // namespace
7878

79-
util::StatusOr<std::unique_ptr<LevelDbPersistence>> LevelDbPersistence::Create(
79+
StatusOr<std::unique_ptr<LevelDbPersistence>> LevelDbPersistence::Create(
8080
util::Path dir, LocalSerializer serializer, const LruParams& lru_params) {
8181
auto* fs = Filesystem::Default();
8282
Status status = EnsureDirectory(dir);
@@ -178,21 +178,35 @@ util::Status LevelDbPersistence::ClearPersistence(
178178
return fs->RecursivelyRemove(leveldb_dir);
179179
}
180180

181-
int64_t LevelDbPersistence::CalculateByteSize() {
181+
StatusOr<int64_t> LevelDbPersistence::CalculateByteSize() {
182182
auto* fs = Filesystem::Default();
183183

184-
int64_t count = 0;
184+
// Accumulate the total size in an unsigned integer to avoid undefined
185+
// behavior on overflow.
186+
uint64_t count = 0;
185187
auto iter = util::DirectoryIterator::Create(directory_);
186188
for (; iter->Valid(); iter->Next()) {
187-
int64_t file_size = fs->FileSize(iter->file()).ValueOrDie();
189+
StatusOr<int64_t> maybe_size = fs->FileSize(iter->file());
190+
if (!maybe_size.ok()) {
191+
return Status::FromCause("Failed to size LevelDB directory",
192+
maybe_size.status());
193+
}
194+
195+
uint64_t old_count = count;
196+
int64_t file_size = maybe_size.ValueOrDie();
188197
count += file_size;
198+
199+
if (count < old_count || count > std::numeric_limits<int64_t>::max()) {
200+
return Status(Error::kErrorOutOfRange,
201+
"Failed to size LevelDB: count overflowed");
202+
}
189203
}
190204

191-
HARD_ASSERT(iter->status().ok(), "Failed to iterate LevelDB directory: %s",
192-
iter->status().error_message().c_str());
193-
HARD_ASSERT(count >= 0 && count <= std::numeric_limits<int64_t>::max(),
194-
"Overflowed counting bytes cached");
195-
return count;
205+
if (!iter->status().ok()) {
206+
return Status::FromCause("Failed to iterate over LevelDB files",
207+
iter->status());
208+
}
209+
return static_cast<int64_t>(count);
196210
}
197211

198212
// MARK: - Persistence

Firestore/core/src/local/leveldb_persistence.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ class LevelDbPersistence : public Persistence {
6969

7070
static util::Status ClearPersistence(const core::DatabaseInfo& database_info);
7171

72-
int64_t CalculateByteSize();
72+
util::StatusOr<int64_t> CalculateByteSize();
7373

7474
// MARK: Persistence overrides
7575

Firestore/core/src/local/lru_garbage_collector.cc

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,18 @@
2626
#include "Firestore/core/src/local/target_data.h"
2727
#include "Firestore/core/src/model/document_key.h"
2828
#include "Firestore/core/src/util/log.h"
29+
#include "Firestore/core/src/util/statusor.h"
2930

3031
namespace firebase {
3132
namespace firestore {
3233
namespace local {
3334
namespace {
3435

35-
using firebase::firestore::api::Settings;
36-
using firebase::firestore::model::DocumentKey;
37-
using firebase::firestore::model::ListenSequenceNumber;
38-
using firebase::firestore::model::TargetId;
36+
using api::Settings;
37+
using model::DocumentKey;
38+
using model::ListenSequenceNumber;
39+
using model::TargetId;
40+
using util::StatusOr;
3941

4042
using Millis = std::chrono::milliseconds;
4143

@@ -108,13 +110,26 @@ LruGarbageCollector::LruGarbageCollector(LruDelegate* delegate,
108110
: delegate_(delegate), params_(std::move(params)) {
109111
}
110112

113+
StatusOr<int64_t> LruGarbageCollector::CalculateByteSize() const {
114+
return delegate_->CalculateByteSize();
115+
}
116+
111117
LruResults LruGarbageCollector::Collect(const LiveQueryMap& live_targets) {
112118
if (params_.min_bytes_threshold == Settings::CacheSizeUnlimited) {
113119
LOG_DEBUG("Garbage collection skipped; disabled");
114120
return LruResults::DidNotRun();
115121
}
116122

117-
int64_t current_size = CalculateByteSize();
123+
StatusOr<int64_t> maybe_current_size = CalculateByteSize();
124+
if (!maybe_current_size.ok()) {
125+
LOG_ERROR(
126+
"Garbage collection skipped; failed to estimate the size of the "
127+
"cache: %s",
128+
maybe_current_size.status().ToString());
129+
return LruResults::DidNotRun();
130+
}
131+
132+
int64_t current_size = maybe_current_size.ValueOrDie();
118133
if (current_size < params_.min_bytes_threshold) {
119134
// Not enough on disk to warrant collection. Wait another timeout cycle.
120135
LOG_DEBUG(

Firestore/core/src/local/lru_garbage_collector.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "Firestore/core/src/local/target_cache.h"
2424
#include "Firestore/core/src/local/target_data.h"
2525
#include "Firestore/core/src/model/types.h"
26+
#include "Firestore/core/src/util/status_fwd.h"
2627

2728
namespace firebase {
2829
namespace firestore {
@@ -71,7 +72,7 @@ class LruDelegate : public ReferenceDelegate {
7172
/** Access to the underlying LRU Garbage collector instance. */
7273
virtual LruGarbageCollector* garbage_collector() = 0;
7374

74-
virtual int64_t CalculateByteSize() = 0;
75+
virtual util::StatusOr<int64_t> CalculateByteSize() = 0;
7576

7677
/** Returns the number of targets and orphaned documents cached. */
7778
virtual size_t GetSequenceNumberCount() = 0;
@@ -114,9 +115,7 @@ class LruGarbageCollector {
114115
public:
115116
LruGarbageCollector(LruDelegate* delegate, LruParams params);
116117

117-
int64_t CalculateByteSize() const {
118-
return delegate_->CalculateByteSize();
119-
}
118+
util::StatusOr<int64_t> CalculateByteSize() const;
120119

121120
/**
122121
* Given a target percentile, return the number of queries that make up that

Firestore/core/src/local/memory_lru_reference_delegate.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "Firestore/core/src/local/remote_document_cache.h"
2828
#include "Firestore/core/src/local/sizer.h"
2929
#include "Firestore/core/src/local/target_data.h"
30+
#include "Firestore/core/src/util/statusor.h"
3031
#include "absl/memory/memory.h"
3132

3233
namespace firebase {
@@ -35,6 +36,7 @@ namespace local {
3536

3637
using model::DocumentKey;
3738
using model::ListenSequenceNumber;
39+
using util::StatusOr;
3840

3941
MemoryLruReferenceDelegate::MemoryLruReferenceDelegate(
4042
MemoryPersistence* persistence,
@@ -172,7 +174,7 @@ bool MemoryLruReferenceDelegate::IsPinnedAtSequenceNumber(
172174
return false;
173175
}
174176

175-
int64_t MemoryLruReferenceDelegate::CalculateByteSize() {
177+
StatusOr<int64_t> MemoryLruReferenceDelegate::CalculateByteSize() {
176178
// Note that this method is only used for testing because this delegate is
177179
// only used for testing. The algorithm here (loop through everything,
178180
// serialize it and count bytes) is inefficient and inexact, but won't run in

Firestore/core/src/local/memory_lru_reference_delegate.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ class MemoryLruReferenceDelegate : public LruDelegate {
6565

6666
LruGarbageCollector* garbage_collector() override;
6767

68-
int64_t CalculateByteSize() override;
68+
util::StatusOr<int64_t> CalculateByteSize() override;
6969
size_t GetSequenceNumberCount() override;
7070

7171
void EnumerateTargets(const TargetCallback& callback) override;

Firestore/core/src/util/status.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ Status::Status(Error code, std::string msg) {
3232
state_ = State::MakePtr(code, std::move(msg));
3333
}
3434

35+
Status Status::FromCause(std::string message, const Status& cause) {
36+
if (cause.ok()) {
37+
return cause;
38+
}
39+
40+
return Status(cause.code(), std::move(message)).CausedBy(cause);
41+
}
42+
3543
void Status::Update(const Status& new_status) {
3644
if (ok()) {
3745
*this = new_status;

0 commit comments

Comments
 (0)