Add copy() and move() to Cache::Database.
This commit is contained in:
parent
adcc11c474
commit
5824afa941
|
@ -55,7 +55,7 @@ void Database::get(const Key &key, FnMut<void(QByteArray)> done) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::remove(const Key &key, FnMut<void()> done) {
|
void Database::remove(const Key &key, FnMut<void(Error)> done) {
|
||||||
_wrapped.with([
|
_wrapped.with([
|
||||||
key,
|
key,
|
||||||
done = std::move(done)
|
done = std::move(done)
|
||||||
|
@ -64,6 +64,32 @@ void Database::remove(const Key &key, FnMut<void()> done) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Database::copy(
|
||||||
|
const Key &from,
|
||||||
|
const Key &to,
|
||||||
|
FnMut<void(Error)> done) {
|
||||||
|
_wrapped.with([
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
done = std::move(done)
|
||||||
|
](Implementation &unwrapped) mutable {
|
||||||
|
unwrapped.copy(from, to, std::move(done));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Database::move(
|
||||||
|
const Key &from,
|
||||||
|
const Key &to,
|
||||||
|
FnMut<void(Error)> done) {
|
||||||
|
_wrapped.with([
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
done = std::move(done)
|
||||||
|
](Implementation &unwrapped) mutable {
|
||||||
|
unwrapped.move(from, to, std::move(done));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void Database::clear(FnMut<void(Error)> done) {
|
void Database::clear(FnMut<void(Error)> done) {
|
||||||
_wrapped.with([
|
_wrapped.with([
|
||||||
done = std::move(done)
|
done = std::move(done)
|
||||||
|
|
|
@ -20,22 +20,31 @@ namespace details {
|
||||||
class DatabaseObject;
|
class DatabaseObject;
|
||||||
} // namespace details
|
} // namespace details
|
||||||
|
|
||||||
using Key = details::Key;
|
|
||||||
using Error = details::Error;
|
|
||||||
|
|
||||||
class Database {
|
class Database {
|
||||||
public:
|
public:
|
||||||
using Settings = details::Settings;
|
using Settings = details::Settings;
|
||||||
Database(const QString &path, const Settings &settings);
|
Database(const QString &path, const Settings &settings);
|
||||||
|
|
||||||
void open(EncryptionKey key, FnMut<void(Error)> done);
|
void open(EncryptionKey key, FnMut<void(Error)> done = nullptr);
|
||||||
void close(FnMut<void()> done);
|
void close(FnMut<void()> done = nullptr);
|
||||||
|
|
||||||
void put(const Key &key, QByteArray value, FnMut<void(Error)> done);
|
void put(
|
||||||
|
const Key &key,
|
||||||
|
QByteArray value,
|
||||||
|
FnMut<void(Error)> done = nullptr);
|
||||||
void get(const Key &key, FnMut<void(QByteArray)> done);
|
void get(const Key &key, FnMut<void(QByteArray)> done);
|
||||||
void remove(const Key &key, FnMut<void()> done);
|
void remove(const Key &key, FnMut<void(Error)> done = nullptr);
|
||||||
|
|
||||||
void clear(FnMut<void(Error)> done);
|
void copy(
|
||||||
|
const Key &from,
|
||||||
|
const Key &to,
|
||||||
|
FnMut<void(Error)> done = nullptr);
|
||||||
|
void move(
|
||||||
|
const Key &from,
|
||||||
|
const Key &to,
|
||||||
|
FnMut<void(Error)> done = nullptr);
|
||||||
|
|
||||||
|
void clear(FnMut<void(Error)> done = nullptr);
|
||||||
|
|
||||||
~Database();
|
~Database();
|
||||||
|
|
||||||
|
|
|
@ -320,7 +320,7 @@ void DatabaseObject::prune() {
|
||||||
collectTimePrune(stale, staleTotalSize);
|
collectTimePrune(stale, staleTotalSize);
|
||||||
collectSizePrune(stale, staleTotalSize);
|
collectSizePrune(stale, staleTotalSize);
|
||||||
for (const auto &key : stale) {
|
for (const auto &key : stale) {
|
||||||
remove(key);
|
remove(key, nullptr);
|
||||||
}
|
}
|
||||||
optimize();
|
optimize();
|
||||||
}
|
}
|
||||||
|
@ -622,8 +622,8 @@ void DatabaseObject::put(
|
||||||
QByteArray value,
|
QByteArray value,
|
||||||
FnMut<void(Error)> done) {
|
FnMut<void(Error)> done) {
|
||||||
if (value.isEmpty()) {
|
if (value.isEmpty()) {
|
||||||
remove(key, [done = std::move(done)]() mutable {
|
remove(key, [done = std::move(done)](Error error) mutable {
|
||||||
done(Error::NoError());
|
done(error);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -732,6 +732,60 @@ base::optional<QString> DatabaseObject::writeKeyPlace(
|
||||||
return writeKeyPlaceGeneric(std::move(record), key, data, checksum);
|
return writeKeyPlaceGeneric(std::move(record), key, data, checksum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename StoreRecord>
|
||||||
|
Error DatabaseObject::writeExistingPlaceGeneric(
|
||||||
|
StoreRecord &&record,
|
||||||
|
const Key &key,
|
||||||
|
const Entry &entry) {
|
||||||
|
record.key = key;
|
||||||
|
record.size = ReadTo<EntrySize>(entry.size);
|
||||||
|
record.checksum = entry.checksum;
|
||||||
|
if (const auto i = _map.find(key); i != end(_map)) {
|
||||||
|
const auto &already = i->second;
|
||||||
|
if (already.tag == record.tag
|
||||||
|
&& already.size == entry.size
|
||||||
|
&& already.checksum == entry.checksum
|
||||||
|
&& (readValueData(already.place, already.size)
|
||||||
|
== readValueData(entry.place, entry.size))) {
|
||||||
|
return Error::NoError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
record.place = entry.place;
|
||||||
|
auto writeable = record;
|
||||||
|
const auto success = _binlog.write(bytes::object_as_span(&writeable));
|
||||||
|
if (!success) {
|
||||||
|
_binlog.close();
|
||||||
|
return ioError(binlogPath());
|
||||||
|
}
|
||||||
|
_binlog.flush();
|
||||||
|
|
||||||
|
const auto applied = processRecordStore(
|
||||||
|
&record,
|
||||||
|
std::is_class<StoreRecord>{});
|
||||||
|
Assert(applied);
|
||||||
|
return Error::NoError();
|
||||||
|
}
|
||||||
|
|
||||||
|
Error DatabaseObject::writeExistingPlace(
|
||||||
|
const Key &key,
|
||||||
|
const Entry &entry) {
|
||||||
|
if (!_settings.trackEstimatedTime) {
|
||||||
|
return writeExistingPlaceGeneric(Store(), key, entry);
|
||||||
|
}
|
||||||
|
auto record = StoreWithTime();
|
||||||
|
record.time = countTimePoint();
|
||||||
|
const auto writing = record.time.getRelative();
|
||||||
|
const auto current = _time.getRelative();
|
||||||
|
Assert(writing >= current);
|
||||||
|
if ((writing - current) * crl::time_type(1000)
|
||||||
|
< _settings.writeBundleDelay) {
|
||||||
|
// We don't want to produce a lot of unique _time.relative values.
|
||||||
|
// So if change in it is not large we stick to the old value.
|
||||||
|
record.time = _time;
|
||||||
|
}
|
||||||
|
return writeExistingPlaceGeneric(std::move(record), key, entry);
|
||||||
|
}
|
||||||
|
|
||||||
void DatabaseObject::get(const Key &key, FnMut<void(QByteArray)> done) {
|
void DatabaseObject::get(const Key &key, FnMut<void(QByteArray)> done) {
|
||||||
if (_removing.find(key) != end(_removing)) {
|
if (_removing.find(key) != end(_removing)) {
|
||||||
invokeCallback(done, QByteArray());
|
invokeCallback(done, QByteArray());
|
||||||
|
@ -784,7 +838,7 @@ void DatabaseObject::recordEntryAccess(const Key &key) {
|
||||||
optimize();
|
optimize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseObject::remove(const Key &key, FnMut<void()> done) {
|
void DatabaseObject::remove(const Key &key, FnMut<void(Error)> done) {
|
||||||
const auto i = _map.find(key);
|
const auto i = _map.find(key);
|
||||||
if (i != _map.end()) {
|
if (i != _map.end()) {
|
||||||
_removing.emplace(key);
|
_removing.emplace(key);
|
||||||
|
@ -792,9 +846,45 @@ void DatabaseObject::remove(const Key &key, FnMut<void()> done) {
|
||||||
|
|
||||||
const auto path = placePath(i->second.place);
|
const auto path = placePath(i->second.place);
|
||||||
eraseMapEntry(i);
|
eraseMapEntry(i);
|
||||||
QFile(path).remove();
|
if (QFile(path).remove() || !QFile(path).exists()) {
|
||||||
|
invokeCallback(done, Error::NoError());
|
||||||
|
} else {
|
||||||
|
invokeCallback(done, ioError(path));
|
||||||
}
|
}
|
||||||
invokeCallback(done);
|
} else {
|
||||||
|
invokeCallback(done, Error::NoError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseObject::copy(
|
||||||
|
const Key &from,
|
||||||
|
const Key &to,
|
||||||
|
FnMut<void(Error)> done) {
|
||||||
|
get(from, [&](QByteArray value) {
|
||||||
|
put(to, value, std::move(done));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseObject::move(
|
||||||
|
const Key &from,
|
||||||
|
const Key &to,
|
||||||
|
FnMut<void(Error)> done) {
|
||||||
|
const auto i = _map.find(from);
|
||||||
|
if (i == _map.end()) {
|
||||||
|
put(to, QByteArray(), std::move(done));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_removing.emplace(from);
|
||||||
|
|
||||||
|
const auto entry = i->second;
|
||||||
|
eraseMapEntry(i);
|
||||||
|
|
||||||
|
const auto result = writeMultiRemove();
|
||||||
|
if (result.type != Error::Type::None) {
|
||||||
|
invokeCallback(done, result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
invokeCallback(done, writeExistingPlace(to, entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseObject::writeBundlesLazy() {
|
void DatabaseObject::writeBundlesLazy() {
|
||||||
|
@ -811,11 +901,11 @@ void DatabaseObject::writeMultiRemoveLazy() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseObject::writeMultiRemove() {
|
Error DatabaseObject::writeMultiRemove() {
|
||||||
Expects(_removing.size() <= _settings.maxBundledRecords);
|
Expects(_removing.size() <= _settings.maxBundledRecords);
|
||||||
|
|
||||||
if (_removing.empty()) {
|
if (_removing.empty()) {
|
||||||
return;
|
return Error::NoError();
|
||||||
}
|
}
|
||||||
const auto size = _removing.size();
|
const auto size = _removing.size();
|
||||||
auto header = MultiRemove(size);
|
auto header = MultiRemove(size);
|
||||||
|
@ -824,13 +914,15 @@ void DatabaseObject::writeMultiRemove() {
|
||||||
for (const auto &key : base::take(_removing)) {
|
for (const auto &key : base::take(_removing)) {
|
||||||
list.push_back(key);
|
list.push_back(key);
|
||||||
}
|
}
|
||||||
if (_binlog.write(bytes::object_as_span(&header))) {
|
if (_binlog.write(bytes::object_as_span(&header))
|
||||||
_binlog.write(bytes::make_span(list));
|
&& _binlog.write(bytes::make_span(list))) {
|
||||||
_binlog.flush();
|
_binlog.flush();
|
||||||
|
|
||||||
_binlogExcessLength += bytes::object_as_span(&header).size()
|
_binlogExcessLength += bytes::object_as_span(&header).size()
|
||||||
+ bytes::make_span(list).size();
|
+ bytes::make_span(list).size();
|
||||||
|
return Error::NoError();
|
||||||
}
|
}
|
||||||
|
_binlog.close();
|
||||||
|
return ioError(binlogPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseObject::writeMultiAccessLazy() {
|
void DatabaseObject::writeMultiAccessLazy() {
|
||||||
|
@ -841,13 +933,14 @@ void DatabaseObject::writeMultiAccessLazy() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseObject::writeMultiAccess() {
|
Error DatabaseObject::writeMultiAccess() {
|
||||||
if (!_accessed.empty()) {
|
if (_accessed.empty()) {
|
||||||
writeMultiAccessBlock();
|
return Error::NoError();
|
||||||
}
|
}
|
||||||
|
return writeMultiAccessBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseObject::writeMultiAccessBlock() {
|
Error DatabaseObject::writeMultiAccessBlock() {
|
||||||
Expects(_settings.trackEstimatedTime);
|
Expects(_settings.trackEstimatedTime);
|
||||||
Expects(_accessed.size() <= _settings.maxBundledRecords);
|
Expects(_accessed.size() <= _settings.maxBundledRecords);
|
||||||
|
|
||||||
|
@ -868,15 +961,15 @@ void DatabaseObject::writeMultiAccessBlock() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_binlog.write(bytes::object_as_span(&header))) {
|
if (_binlog.write(bytes::object_as_span(&header))
|
||||||
if (size > 0) {
|
&& (!size || _binlog.write(bytes::make_span(list)))) {
|
||||||
_binlog.write(bytes::make_span(list));
|
|
||||||
}
|
|
||||||
_binlog.flush();
|
_binlog.flush();
|
||||||
|
|
||||||
_binlogExcessLength += bytes::object_as_span(&header).size()
|
_binlogExcessLength += bytes::object_as_span(&header).size()
|
||||||
+ bytes::make_span(list).size();
|
+ bytes::make_span(list).size();
|
||||||
|
return Error::NoError();
|
||||||
}
|
}
|
||||||
|
_binlog.close();
|
||||||
|
return ioError(binlogPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseObject::writeBundles() {
|
void DatabaseObject::writeBundles() {
|
||||||
|
|
|
@ -35,7 +35,10 @@ public:
|
||||||
|
|
||||||
void put(const Key &key, QByteArray value, FnMut<void(Error)> done);
|
void put(const Key &key, QByteArray value, FnMut<void(Error)> done);
|
||||||
void get(const Key &key, FnMut<void(QByteArray)> done);
|
void get(const Key &key, FnMut<void(QByteArray)> done);
|
||||||
void remove(const Key &key, FnMut<void()> done = nullptr);
|
void remove(const Key &key, FnMut<void(Error)> done);
|
||||||
|
|
||||||
|
void copy(const Key &from, const Key &to, FnMut<void(Error)> done);
|
||||||
|
void move(const Key &from, const Key &to, FnMut<void(Error)> done);
|
||||||
|
|
||||||
void clear(FnMut<void(Error)> done);
|
void clear(FnMut<void(Error)> done);
|
||||||
|
|
||||||
|
@ -160,11 +163,19 @@ private:
|
||||||
const Key &key,
|
const Key &key,
|
||||||
const QByteArray &value,
|
const QByteArray &value,
|
||||||
uint32 checksum);
|
uint32 checksum);
|
||||||
|
template <typename StoreRecord>
|
||||||
|
Error writeExistingPlaceGeneric(
|
||||||
|
StoreRecord &&record,
|
||||||
|
const Key &key,
|
||||||
|
const Entry &entry);
|
||||||
|
Error writeExistingPlace(
|
||||||
|
const Key &key,
|
||||||
|
const Entry &entry);
|
||||||
void writeMultiRemoveLazy();
|
void writeMultiRemoveLazy();
|
||||||
void writeMultiRemove();
|
Error writeMultiRemove();
|
||||||
void writeMultiAccessLazy();
|
void writeMultiAccessLazy();
|
||||||
void writeMultiAccess();
|
Error writeMultiAccess();
|
||||||
void writeMultiAccessBlock();
|
Error writeMultiAccessBlock();
|
||||||
void writeBundlesLazy();
|
void writeBundlesLazy();
|
||||||
void writeBundles();
|
void writeBundles();
|
||||||
|
|
||||||
|
|
|
@ -106,7 +106,7 @@ Error Put(Database &db, const Key &key, const QByteArray &value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Remove(Database &db, const Key &key) {
|
void Remove(Database &db, const Key &key) {
|
||||||
db.remove(key, [&] { Semaphore.release(); });
|
db.remove(key, [&](Error) { Semaphore.release(); });
|
||||||
Semaphore.acquire();
|
Semaphore.acquire();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
namespace Storage {
|
namespace Storage {
|
||||||
namespace Cache {
|
namespace Cache {
|
||||||
namespace details {
|
|
||||||
|
|
||||||
struct Key {
|
struct Key {
|
||||||
uint64 high = 0;
|
uint64 high = 0;
|
||||||
|
@ -32,6 +31,25 @@ inline bool operator<(const Key &a, const Key &b) {
|
||||||
return std::tie(a.high, a.low) < std::tie(b.high, b.low);
|
return std::tie(a.high, a.low) < std::tie(b.high, b.low);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Error {
|
||||||
|
enum class Type {
|
||||||
|
None,
|
||||||
|
IO,
|
||||||
|
WrongKey,
|
||||||
|
LockFailed,
|
||||||
|
};
|
||||||
|
Type type = Type::None;
|
||||||
|
QString path;
|
||||||
|
|
||||||
|
static Error NoError();
|
||||||
|
};
|
||||||
|
|
||||||
|
inline Error Error::NoError() {
|
||||||
|
return Error();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
|
||||||
struct Settings {
|
struct Settings {
|
||||||
size_type maxBundledRecords = 16 * 1024;
|
size_type maxBundledRecords = 16 * 1024;
|
||||||
size_type readBlockSize = 8 * 1024 * 1024;
|
size_type readBlockSize = 8 * 1024 * 1024;
|
||||||
|
@ -56,23 +74,6 @@ QString VersionFilePath(const QString &base);
|
||||||
base::optional<Version> ReadVersionValue(const QString &base);
|
base::optional<Version> ReadVersionValue(const QString &base);
|
||||||
bool WriteVersionValue(const QString &base, Version value);
|
bool WriteVersionValue(const QString &base, Version value);
|
||||||
|
|
||||||
struct Error {
|
|
||||||
enum class Type {
|
|
||||||
None,
|
|
||||||
IO,
|
|
||||||
WrongKey,
|
|
||||||
LockFailed,
|
|
||||||
};
|
|
||||||
Type type = Type::None;
|
|
||||||
QString path;
|
|
||||||
|
|
||||||
static Error NoError();
|
|
||||||
};
|
|
||||||
|
|
||||||
inline Error Error::NoError() {
|
|
||||||
return Error();
|
|
||||||
}
|
|
||||||
|
|
||||||
using RecordType = uint8;
|
using RecordType = uint8;
|
||||||
using PlaceId = std::array<uint8, 7>;
|
using PlaceId = std::array<uint8, 7>;
|
||||||
using EntrySize = std::array<uint8, 3>;
|
using EntrySize = std::array<uint8, 3>;
|
||||||
|
@ -217,8 +218,8 @@ struct MultiAccess {
|
||||||
namespace std {
|
namespace std {
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct hash<Storage::Cache::details::Key> {
|
struct hash<Storage::Cache::Key> {
|
||||||
size_t operator()(const Storage::Cache::details::Key &key) const {
|
size_t operator()(const Storage::Cache::Key &key) const {
|
||||||
return (hash<uint64>()(key.high) ^ hash<uint64>()(key.low));
|
return (hash<uint64>()(key.high) ^ hash<uint64>()(key.low));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue