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) {
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 result = openBinlog(version, File::Mode::ReadAppend, key);
switch (result) {
case File::Result::Success:
invokeCallback(done, Error::NoError());
break;
case File::Result::Success: return Error::NoError();
case File::Result::Failed: return openNewBinlog(key);
case File::Result::LockFailed:
invokeCallback(
done,
Error{ Error::Type::LockFailed, binlogPath(version) });
break;
return Error{ Error::Type::LockFailed, binlogPath(version) };
case File::Result::WrongKey:
invokeCallback(
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.");
return Error{ Error::Type::WrongKey, binlogPath(version) };
}
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 {
@ -177,19 +179,20 @@ File::Result DatabaseObject::openBinlog(
return File::Result::Failed;
}
const auto result = _binlog.open(path, mode, key);
if (result == File::Result::Success) {
const auto headerRequired = (mode == File::Mode::Read)
|| (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;
}
if (result != File::Result::Success) {
return result;
}
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() {
@ -575,13 +578,15 @@ void DatabaseObject::compactorDone(
});
_binlog.close();
if (!File::Move(ready, binlog)) {
// megafail
compactorFail();
return;
}
const auto result = _binlog.open(binlog, File::Mode::ReadAppend, _key);
if (result != File::Result::Success || !_binlog.seek(_binlog.size())) {
// megafail
if (result != File::Result::Success) {
compactorFail();
return;
} else if (!_binlog.seek(_binlog.size())) {
_binlog.close();
compactorFail();
return;
}
@ -600,7 +605,9 @@ void DatabaseObject::compactorFail() {
}
void DatabaseObject::close(FnMut<void()> done) {
writeBundles();
if (_binlog.isOpen()) {
writeBundles();
}
_cleaner = CleanerWrap();
_compactor = CompactorWrap();
_binlog.close();
@ -692,6 +699,7 @@ base::optional<QString> DatabaseObject::writeKeyPlaceGeneric(
auto writeable = record;
const auto success = _binlog.write(bytes::object_as_span(&writeable));
if (!success) {
_binlog.close();
return QString();
}
_binlog.flush();
@ -905,7 +913,7 @@ void DatabaseObject::checkCompactor() {
&& (_binlogExcessLength * _settings.compactAfterFullSize
< _settings.compactAfterExcess * _binlog.size())) {
return;
} else if (crl::time() < _compactor.nextAttempt) {
} else if (crl::time() < _compactor.nextAttempt || !_binlog.isOpen()) {
return;
}
auto info = Compactor::Info();

View File

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

View File

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

View File

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

View File

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