Ignore database actions after IO error.

This commit is contained in:
John Preston 2018-08-26 15:55:12 +03:00
parent 4769a1a49f
commit adcc11c474
5 changed files with 63 additions and 45 deletions

View File

@ -106,37 +106,39 @@ Error DatabaseObject::ioError(const QString &path) const {
} }
void DatabaseObject::open(EncryptionKey key, FnMut<void(Error)> done) { void DatabaseObject::open(EncryptionKey key, FnMut<void(Error)> done) {
close(nullptr);
const auto error = openSomeBinlog(key);
if (error.type != Error::Type::None) {
close(nullptr);
}
invokeCallback(done, error);
}
Error DatabaseObject::openSomeBinlog(EncryptionKey &key) {
const auto version = readVersion(); const auto version = readVersion();
const auto result = openBinlog(version, File::Mode::ReadAppend, key); const auto result = openBinlog(version, File::Mode::ReadAppend, key);
switch (result) { switch (result) {
case File::Result::Success: case File::Result::Success: return Error::NoError();
invokeCallback(done, Error::NoError()); case File::Result::Failed: return openNewBinlog(key);
break;
case File::Result::LockFailed: case File::Result::LockFailed:
invokeCallback( return Error{ Error::Type::LockFailed, binlogPath(version) };
done,
Error{ Error::Type::LockFailed, binlogPath(version) });
break;
case File::Result::WrongKey: case File::Result::WrongKey:
invokeCallback( return Error{ Error::Type::WrongKey, binlogPath(version) };
done,
Error{ Error::Type::WrongKey, binlogPath(version) });
break;
case File::Result::Failed: {
const auto available = findAvailableVersion();
if (writeVersion(available)) {
const auto open = openBinlog(available, File::Mode::Write, key);
if (open == File::Result::Success) {
invokeCallback(done, Error::NoError());
} else {
invokeCallback(done, ioError(binlogPath(available)));
}
} else {
invokeCallback(done, ioError(versionPath()));
}
} break;
default: Unexpected("Result from DatabaseObject::openBinlog.");
} }
Unexpected("Result from DatabaseObject::openBinlog.");
}
Error DatabaseObject::openNewBinlog(EncryptionKey &key) {
const auto available = findAvailableVersion();
if (!writeVersion(available)) {
return ioError(versionPath());
}
const auto open = openBinlog(available, File::Mode::Write, key);
if (open != File::Result::Success) {
return ioError(binlogPath(available));
}
return Error::NoError();
} }
QString DatabaseObject::computePath(Version version) const { QString DatabaseObject::computePath(Version version) const {
@ -177,19 +179,20 @@ File::Result DatabaseObject::openBinlog(
return File::Result::Failed; return File::Result::Failed;
} }
const auto result = _binlog.open(path, mode, key); const auto result = _binlog.open(path, mode, key);
if (result == File::Result::Success) { if (result != File::Result::Success) {
const auto headerRequired = (mode == File::Mode::Read) return result;
|| (mode == File::Mode::ReadAppend && _binlog.size() > 0);
if (headerRequired ? readHeader() : writeHeader()) {
_path = computePath(version);
_key = std::move(key);
createCleaner();
readBinlog();
} else {
return File::Result::Failed;
}
} }
return result; const auto headerRequired = (mode == File::Mode::Read)
|| (mode == File::Mode::ReadAppend && _binlog.size() > 0);
const auto headerResult = headerRequired ? readHeader() : writeHeader();
if (!headerResult) {
return File::Result::Failed;
}
_path = computePath(version);
_key = std::move(key);
createCleaner();
readBinlog();
return File::Result::Success;
} }
bool DatabaseObject::readHeader() { bool DatabaseObject::readHeader() {
@ -575,13 +578,15 @@ void DatabaseObject::compactorDone(
}); });
_binlog.close(); _binlog.close();
if (!File::Move(ready, binlog)) { if (!File::Move(ready, binlog)) {
// megafail
compactorFail(); compactorFail();
return; return;
} }
const auto result = _binlog.open(binlog, File::Mode::ReadAppend, _key); const auto result = _binlog.open(binlog, File::Mode::ReadAppend, _key);
if (result != File::Result::Success || !_binlog.seek(_binlog.size())) { if (result != File::Result::Success) {
// megafail compactorFail();
return;
} else if (!_binlog.seek(_binlog.size())) {
_binlog.close();
compactorFail(); compactorFail();
return; return;
} }
@ -600,7 +605,9 @@ void DatabaseObject::compactorFail() {
} }
void DatabaseObject::close(FnMut<void()> done) { void DatabaseObject::close(FnMut<void()> done) {
writeBundles(); if (_binlog.isOpen()) {
writeBundles();
}
_cleaner = CleanerWrap(); _cleaner = CleanerWrap();
_compactor = CompactorWrap(); _compactor = CompactorWrap();
_binlog.close(); _binlog.close();
@ -692,6 +699,7 @@ base::optional<QString> DatabaseObject::writeKeyPlaceGeneric(
auto writeable = record; auto writeable = record;
const auto success = _binlog.write(bytes::object_as_span(&writeable)); const auto success = _binlog.write(bytes::object_as_span(&writeable));
if (!success) { if (!success) {
_binlog.close();
return QString(); return QString();
} }
_binlog.flush(); _binlog.flush();
@ -905,7 +913,7 @@ void DatabaseObject::checkCompactor() {
&& (_binlogExcessLength * _settings.compactAfterFullSize && (_binlogExcessLength * _settings.compactAfterFullSize
< _settings.compactAfterExcess * _binlog.size())) { < _settings.compactAfterExcess * _binlog.size())) {
return; return;
} else if (crl::time() < _compactor.nextAttempt) { } else if (crl::time() < _compactor.nextAttempt || !_binlog.isOpen()) {
return; return;
} }
auto info = Compactor::Info(); auto info = Compactor::Info();

View File

@ -88,6 +88,8 @@ private:
QString binlogPath() const; QString binlogPath() const;
QString compactReadyPath(Version version) const; QString compactReadyPath(Version version) const;
QString compactReadyPath() const; QString compactReadyPath() const;
Error openSomeBinlog(EncryptionKey &key);
Error openNewBinlog(EncryptionKey &key);
File::Result openBinlog( File::Result openBinlog(
Version version, Version version,
File::Mode mode, File::Mode mode,

View File

@ -18,9 +18,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
using namespace Storage::Cache; using namespace Storage::Cache;
const auto DisableLimitsTests = true; const auto DisableLimitsTests = false;
const auto DisableCompactTests = true; const auto DisableCompactTests = false;
const auto DisableLargeTest = false; const auto DisableLargeTest = true;
const auto key = Storage::EncryptionKey(bytes::make_vector( const auto key = Storage::EncryptionKey(bytes::make_vector(
bytes::make_span("\ bytes::make_span("\

View File

@ -206,6 +206,9 @@ size_type File::read(bytes::span bytes) {
bool File::write(bytes::span bytes) { bool File::write(bytes::span bytes) {
Expects(bytes.size() % kBlockSize == 0); Expects(bytes.size() % kBlockSize == 0);
if (!isOpen()) {
return false;
}
encrypt(bytes); encrypt(bytes);
const auto count = writePlain(bytes); const auto count = writePlain(bytes);
if (count == bytes.size()) { if (count == bytes.size()) {
@ -288,6 +291,10 @@ void File::close() {
_state = base::none; _state = base::none;
} }
bool File::isOpen() const {
return _data.isOpen();
}
int64 File::size() const { int64 File::size() const {
return _dataSize; return _dataSize;
} }

View File

@ -37,6 +37,7 @@ public:
bool flush(); bool flush();
bool isOpen() const;
int64 size() const; int64 size() const;
int64 offset() const; int64 offset() const;
bool seek(int64 offset); bool seek(int64 offset);