mirror of https://github.com/procxx/kepka.git
Write date/time inside Store cache record.
This commit is contained in:
parent
e5dda6dd49
commit
fbfa7e7be3
|
@ -100,7 +100,7 @@ QString PlaceFromId(PlaceId place) {
|
|||
}
|
||||
|
||||
int32 GetUnixtime() {
|
||||
return int32(time(nullptr));
|
||||
return std::max(int32(time(nullptr)), 1);
|
||||
}
|
||||
|
||||
enum class Format : uint32 {
|
||||
|
@ -110,9 +110,11 @@ enum class Format : uint32 {
|
|||
struct BasicHeader {
|
||||
BasicHeader();
|
||||
|
||||
static constexpr auto kTrackEstimatedTime = 0x01U;
|
||||
|
||||
Format format : 8;
|
||||
uint32 flags : 24;
|
||||
uint32 date = 0;
|
||||
uint32 systemTime = 0;
|
||||
uint32 reserved1 = 0;
|
||||
uint32 reserved2 = 0;
|
||||
};
|
||||
|
@ -123,6 +125,11 @@ BasicHeader::BasicHeader()
|
|||
, flags(0) {
|
||||
}
|
||||
|
||||
struct EstimatedTimePoint {
|
||||
uint32 system = 0;
|
||||
uint32 relativeAdvancement = 0;
|
||||
};
|
||||
|
||||
struct Store {
|
||||
static constexpr auto kType = RecordType(0x01);
|
||||
|
||||
|
@ -135,6 +142,13 @@ struct Store {
|
|||
};
|
||||
static_assert(GoodForEncryption<Store>);
|
||||
|
||||
struct StoreWithTime : Store {
|
||||
EstimatedTimePoint time;
|
||||
uint32 reserved1 = 0;
|
||||
uint32 reserved2 = 0;
|
||||
};
|
||||
static_assert(GoodForEncryption<StoreWithTime>);
|
||||
|
||||
struct MultiStoreHeader {
|
||||
static constexpr auto kType = RecordType(0x02);
|
||||
|
||||
|
@ -146,16 +160,9 @@ struct MultiStoreHeader {
|
|||
uint32 reserved2 = 0;
|
||||
uint32 reserved3 = 0;
|
||||
};
|
||||
struct MultiStorePart {
|
||||
uint8 reserved = 0;
|
||||
uint8 tag = 0;
|
||||
EntrySize size = { { 0 } };
|
||||
PlaceId place = { { 0 } };
|
||||
uint32 checksum = 0;
|
||||
Key key;
|
||||
};
|
||||
using MultiStorePart = Store;
|
||||
using MultiStoreWithTimePart = StoreWithTime;
|
||||
static_assert(GoodForEncryption<MultiStoreHeader>);
|
||||
static_assert(GoodForEncryption<MultiStorePart>);
|
||||
|
||||
MultiStoreHeader::MultiStoreHeader(size_type count)
|
||||
: type(kType)
|
||||
|
@ -186,34 +193,30 @@ MultiRemoveHeader::MultiRemoveHeader(size_type count)
|
|||
Expects(count >= 0 && count < kBundledRecordsLimit);
|
||||
}
|
||||
|
||||
struct MultiTouchedHeader {
|
||||
struct MultiAccessHeader {
|
||||
static constexpr auto kType = RecordType(0x04);
|
||||
|
||||
MultiTouchedHeader(
|
||||
uint32 time,
|
||||
uint32 advancement,
|
||||
explicit MultiAccessHeader(
|
||||
EstimatedTimePoint time,
|
||||
size_type count = 0);
|
||||
|
||||
RecordType type = kType;
|
||||
RecordsCount count = { { 0 } };
|
||||
uint32 timeAdvancement = 0;
|
||||
uint32 systemTime = 0;
|
||||
EstimatedTimePoint time;
|
||||
uint32 reserved = 0;
|
||||
};
|
||||
struct MultiTouchedPart {
|
||||
struct MultiAccessPart {
|
||||
Key key;
|
||||
};
|
||||
static_assert(GoodForEncryption<MultiTouchedHeader>);
|
||||
static_assert(GoodForEncryption<MultiTouchedPart>);
|
||||
static_assert(GoodForEncryption<MultiAccessHeader>);
|
||||
static_assert(GoodForEncryption<MultiAccessPart>);
|
||||
|
||||
MultiTouchedHeader::MultiTouchedHeader(
|
||||
uint32 time,
|
||||
uint32 advancement,
|
||||
MultiAccessHeader::MultiAccessHeader(
|
||||
EstimatedTimePoint time,
|
||||
size_type count)
|
||||
: type(kType)
|
||||
, count(ReadTo<RecordsCount>(count))
|
||||
, timeAdvancement(advancement)
|
||||
, systemTime(time) {
|
||||
, time(time) {
|
||||
Expects(count >= 0 && count < kBundledRecordsLimit);
|
||||
}
|
||||
|
||||
|
@ -280,14 +283,28 @@ private:
|
|||
size_type readBinlogRecords(bytes::const_span data);
|
||||
size_type readBinlogRecordSize(bytes::const_span data) const;
|
||||
bool readBinlogRecord(bytes::const_span data);
|
||||
template <typename RecordStore>
|
||||
bool readRecordStoreGeneric(bytes::const_span data);
|
||||
bool readRecordStore(bytes::const_span data);
|
||||
template <typename StorePart>
|
||||
bool readRecordMultiStoreGeneric(bytes::const_span data);
|
||||
bool readRecordMultiStore(bytes::const_span data);
|
||||
bool readRecordMultiRemove(bytes::const_span data);
|
||||
bool readRecordMultiTouched(bytes::const_span data);
|
||||
bool readRecordMultiAccess(bytes::const_span data);
|
||||
template <typename RecordStore, typename Postprocess>
|
||||
bool processRecordStoreGeneric(
|
||||
const RecordStore *record,
|
||||
Postprocess &&postprocess);
|
||||
bool processRecordStore(const Store *record, std::is_class<Store>);
|
||||
bool processRecordStore(
|
||||
const StoreWithTime *record,
|
||||
std::is_class<StoreWithTime>);
|
||||
|
||||
void adjustRelativeTime();
|
||||
void startDelayedPruning();
|
||||
int64 countRelativeTime() const;
|
||||
EstimatedTimePoint countTimePoint() const;
|
||||
void applyTimePoint(EstimatedTimePoint time);
|
||||
int64 pruneBeforeTime() const;
|
||||
void prune();
|
||||
void collectTimePrune(
|
||||
|
@ -307,12 +324,18 @@ private:
|
|||
|
||||
QString placePath(PlaceId place) const;
|
||||
bool isFreePlace(PlaceId place) const;
|
||||
template <typename StoreRecord>
|
||||
QString writeKeyPlaceGeneric(
|
||||
StoreRecord &&record,
|
||||
const Key &key,
|
||||
size_type size,
|
||||
uint32 checksum);
|
||||
QString writeKeyPlace(const Key &key, size_type size, uint32 checksum);
|
||||
void writeMultiRemoveLazy();
|
||||
void writeMultiRemove();
|
||||
void writeMultiTouchedLazy();
|
||||
void writeMultiTouched();
|
||||
void writeMultiTouchedBlock();
|
||||
void writeMultiAccessLazy();
|
||||
void writeMultiAccess();
|
||||
void writeMultiAccessBlock();
|
||||
void writeBundlesLazy();
|
||||
void writeBundles();
|
||||
|
||||
|
@ -321,12 +344,12 @@ private:
|
|||
|
||||
crl::weak_on_queue<Database> _weak;
|
||||
QString _base, _path;
|
||||
Settings _settings;
|
||||
const Settings _settings;
|
||||
EncryptionKey _key;
|
||||
File _binlog;
|
||||
Map _map;
|
||||
std::set<Key> _removing;
|
||||
std::set<Key> _touched;
|
||||
std::set<Key> _accessed;
|
||||
|
||||
int64 _relativeTime = 0;
|
||||
int64 _timeCorrection = 0;
|
||||
|
@ -461,15 +484,21 @@ bool Database::readHeader() {
|
|||
return false;
|
||||
} else if (header.format != Format::Format_0) {
|
||||
return false;
|
||||
} else if (_settings.trackEstimatedTime
|
||||
!= !!(header.flags & header.kTrackEstimatedTime)) {
|
||||
return false;
|
||||
}
|
||||
_relativeTime = _latestSystemTime = header.date;
|
||||
_relativeTime = _latestSystemTime = header.systemTime;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Database::writeHeader() {
|
||||
auto header = BasicHeader();
|
||||
const auto now = std::max(GetUnixtime(), 1);
|
||||
_relativeTime = _latestSystemTime = header.date = now;
|
||||
const auto now = _settings.trackEstimatedTime ? GetUnixtime() : 0;
|
||||
_relativeTime = _latestSystemTime = header.systemTime = now;
|
||||
if (_settings.trackEstimatedTime) {
|
||||
header.flags |= header.kTrackEstimatedTime;
|
||||
}
|
||||
return _binlog.write(bytes::object_as_span(&header));
|
||||
}
|
||||
|
||||
|
@ -502,7 +531,7 @@ void Database::readBinlog() {
|
|||
}
|
||||
|
||||
int64 Database::countRelativeTime() const {
|
||||
const auto now = std::max(GetUnixtime(), 1);
|
||||
const auto now = GetUnixtime();
|
||||
const auto delta = std::max(int64(now) - int64(_latestSystemTime), 0LL);
|
||||
return _relativeTime + delta;
|
||||
}
|
||||
|
@ -514,7 +543,7 @@ int64 Database::pruneBeforeTime() const {
|
|||
}
|
||||
|
||||
void Database::startDelayedPruning() {
|
||||
if (_map.empty()) {
|
||||
if (!_settings.trackEstimatedTime || _map.empty()) {
|
||||
return;
|
||||
}
|
||||
const auto pruning = [&] {
|
||||
|
@ -630,9 +659,12 @@ void Database::collectSizePrune(
|
|||
}
|
||||
|
||||
void Database::adjustRelativeTime() {
|
||||
const auto now = std::max(GetUnixtime(), 1);
|
||||
if (!_settings.trackEstimatedTime) {
|
||||
return;
|
||||
}
|
||||
const auto now = GetUnixtime();
|
||||
if (now < _latestSystemTime) {
|
||||
writeMultiTouchedBlock();
|
||||
writeMultiAccessBlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -658,16 +690,20 @@ size_type Database::readBinlogRecordSize(bytes::const_span data) const {
|
|||
|
||||
switch (static_cast<RecordType>(data[0])) {
|
||||
case Store::kType:
|
||||
return sizeof(Store);
|
||||
return _settings.trackEstimatedTime
|
||||
? sizeof(StoreWithTime)
|
||||
: sizeof(Store);
|
||||
|
||||
case MultiStoreHeader::kType:
|
||||
if (data.size() >= sizeof(MultiStoreHeader)) {
|
||||
const auto header = reinterpret_cast<const MultiStoreHeader*>(
|
||||
data.data());
|
||||
const auto count = ReadFrom(header->count);
|
||||
const auto size = _settings.trackEstimatedTime
|
||||
? sizeof(MultiStoreWithTimePart)
|
||||
: sizeof(MultiStorePart);
|
||||
return (count > 0 && count < _settings.maxBundledRecords)
|
||||
? (sizeof(MultiStoreHeader)
|
||||
+ count * sizeof(MultiStorePart))
|
||||
? (sizeof(MultiStoreHeader) + count * size)
|
||||
: kRecordSizeInvalid;
|
||||
}
|
||||
return kRecordSizeUnknown;
|
||||
|
@ -684,14 +720,16 @@ size_type Database::readBinlogRecordSize(bytes::const_span data) const {
|
|||
}
|
||||
return kRecordSizeUnknown;
|
||||
|
||||
case MultiTouchedHeader::kType:
|
||||
if (data.size() >= sizeof(MultiTouchedHeader)) {
|
||||
const auto header = reinterpret_cast<const MultiTouchedHeader*>(
|
||||
case MultiAccessHeader::kType:
|
||||
if (!_settings.trackEstimatedTime) {
|
||||
return kRecordSizeInvalid;
|
||||
} else if (data.size() >= sizeof(MultiAccessHeader)) {
|
||||
const auto header = reinterpret_cast<const MultiAccessHeader*>(
|
||||
data.data());
|
||||
const auto count = ReadFrom(header->count);
|
||||
return (count > 0 && count < _settings.maxBundledRecords)
|
||||
? (sizeof(MultiTouchedHeader)
|
||||
+ count * sizeof(MultiTouchedPart))
|
||||
return (count >= 0 && count < _settings.maxBundledRecords)
|
||||
? (sizeof(MultiAccessHeader)
|
||||
+ count * sizeof(MultiAccessPart))
|
||||
: kRecordSizeInvalid;
|
||||
}
|
||||
return kRecordSizeUnknown;
|
||||
|
@ -713,61 +751,98 @@ bool Database::readBinlogRecord(bytes::const_span data) {
|
|||
case MultiRemoveHeader::kType:
|
||||
return readRecordMultiRemove(data);
|
||||
|
||||
case MultiTouchedHeader::kType:
|
||||
return readRecordMultiTouched(data);
|
||||
case MultiAccessHeader::kType:
|
||||
return readRecordMultiAccess(data);
|
||||
|
||||
}
|
||||
Unexpected("Bad type in Database::readBinlogRecord.");
|
||||
}
|
||||
|
||||
bool Database::readRecordStore(bytes::const_span data) {
|
||||
Expects(data.size() >= sizeof(Store));
|
||||
template <typename RecordStore>
|
||||
bool Database::readRecordStoreGeneric(bytes::const_span data) {
|
||||
Expects(data.size() >= sizeof(RecordStore));
|
||||
|
||||
const auto record = reinterpret_cast<const Store*>(data.data());
|
||||
return processRecordStore(
|
||||
reinterpret_cast<const RecordStore*>(data.data()),
|
||||
std::is_class<RecordStore>{});
|
||||
}
|
||||
|
||||
template <typename RecordStore, typename Postprocess>
|
||||
bool Database::processRecordStoreGeneric(
|
||||
const RecordStore *record,
|
||||
Postprocess &&postprocess) {
|
||||
const auto size = ReadFrom(record->size);
|
||||
if (size > _settings.maxDataSize) {
|
||||
if (size <= 0 || size > _settings.maxDataSize) {
|
||||
return false;
|
||||
}
|
||||
setMapEntry(
|
||||
record->key,
|
||||
Entry(
|
||||
record->place,
|
||||
record->tag,
|
||||
record->checksum,
|
||||
size,
|
||||
_relativeTime));
|
||||
auto entry = Entry(
|
||||
record->place,
|
||||
record->tag,
|
||||
record->checksum,
|
||||
size,
|
||||
_relativeTime);
|
||||
if (!postprocess(entry, record)) {
|
||||
return false;
|
||||
}
|
||||
setMapEntry(record->key, std::move(entry));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Database::readRecordMultiStore(bytes::const_span data) {
|
||||
bool Database::processRecordStore(
|
||||
const Store *record,
|
||||
std::is_class<Store>) {
|
||||
const auto postprocess = [](auto&&...) { return true; };
|
||||
return processRecordStoreGeneric(record, postprocess);
|
||||
}
|
||||
|
||||
bool Database::processRecordStore(
|
||||
const StoreWithTime *record,
|
||||
std::is_class<StoreWithTime>) {
|
||||
const auto postprocess = [&](
|
||||
Entry &entry,
|
||||
not_null<const StoreWithTime*> record) {
|
||||
applyTimePoint(record->time);
|
||||
entry.useTime = _relativeTime;
|
||||
return true;
|
||||
};
|
||||
return processRecordStoreGeneric(record, postprocess);
|
||||
}
|
||||
|
||||
bool Database::readRecordStore(bytes::const_span data) {
|
||||
if (!_settings.trackEstimatedTime) {
|
||||
return readRecordStoreGeneric<Store>(data);
|
||||
}
|
||||
return readRecordStoreGeneric<StoreWithTime>(data);
|
||||
}
|
||||
|
||||
template <typename StorePart>
|
||||
bool Database::readRecordMultiStoreGeneric(bytes::const_span data) {
|
||||
Expects(data.size() >= sizeof(MultiStoreHeader));
|
||||
|
||||
const auto bytes = data.data();
|
||||
const auto record = reinterpret_cast<const MultiStoreHeader*>(bytes);
|
||||
const auto count = ReadFrom(record->count);
|
||||
Assert(data.size() >= sizeof(MultiStoreHeader)
|
||||
+ count * sizeof(MultiStorePart));
|
||||
+ count * sizeof(StorePart));
|
||||
const auto parts = gsl::make_span(
|
||||
reinterpret_cast<const MultiStorePart*>(
|
||||
reinterpret_cast<const StorePart*>(
|
||||
bytes + sizeof(MultiStoreHeader)),
|
||||
count);
|
||||
for (const auto &part : parts) {
|
||||
const auto size = ReadFrom(part.size);
|
||||
if (size > _settings.maxDataSize) {
|
||||
if (!processRecordStore(&part, std::is_class<StorePart>{})) {
|
||||
return false;
|
||||
}
|
||||
setMapEntry(
|
||||
part.key,
|
||||
Entry(
|
||||
part.place,
|
||||
part.tag,
|
||||
part.checksum,
|
||||
size,
|
||||
_relativeTime));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Database::readRecordMultiStore(bytes::const_span data) {
|
||||
if (!_settings.trackEstimatedTime) {
|
||||
return readRecordMultiStoreGeneric<MultiStorePart>(data);
|
||||
}
|
||||
return readRecordMultiStoreGeneric<MultiStoreWithTimePart>(data);
|
||||
}
|
||||
|
||||
void Database::setMapEntry(const Key &key, Entry &&entry) {
|
||||
auto &already = _map[key];
|
||||
_totalSize += entry.size - already.size;
|
||||
|
@ -817,22 +892,38 @@ bool Database::readRecordMultiRemove(bytes::const_span data) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Database::readRecordMultiTouched(bytes::const_span data) {
|
||||
Expects(data.size() >= sizeof(MultiTouchedHeader));
|
||||
EstimatedTimePoint Database::countTimePoint() const {
|
||||
const auto now = std::max(GetUnixtime(), 1);
|
||||
const auto delta = std::max(int64(now) - int64(_latestSystemTime), 0LL);
|
||||
auto result = EstimatedTimePoint();
|
||||
result.system = now;
|
||||
result.relativeAdvancement = std::min(
|
||||
delta,
|
||||
int64(_settings.maxTimeAdvancement));
|
||||
return result;
|
||||
}
|
||||
|
||||
void Database::applyTimePoint(EstimatedTimePoint time) {
|
||||
_relativeTime += time.relativeAdvancement;
|
||||
_latestSystemTime = time.system;
|
||||
}
|
||||
|
||||
bool Database::readRecordMultiAccess(bytes::const_span data) {
|
||||
Expects(data.size() >= sizeof(MultiAccessHeader));
|
||||
Expects(_settings.trackEstimatedTime);
|
||||
|
||||
const auto bytes = data.data();
|
||||
const auto record = reinterpret_cast<const MultiTouchedHeader*>(bytes);
|
||||
if (record->timeAdvancement > _settings.maxTimeAdvancement) {
|
||||
const auto record = reinterpret_cast<const MultiAccessHeader*>(bytes);
|
||||
if (record->time.relativeAdvancement > _settings.maxTimeAdvancement) {
|
||||
return false;
|
||||
}
|
||||
_relativeTime += record->timeAdvancement;
|
||||
_latestSystemTime = record->systemTime;
|
||||
applyTimePoint(record->time);
|
||||
const auto count = ReadFrom(record->count);
|
||||
Assert(data.size() >= sizeof(MultiTouchedHeader)
|
||||
+ count * sizeof(MultiTouchedPart));
|
||||
Assert(data.size() >= sizeof(MultiAccessHeader)
|
||||
+ count * sizeof(MultiAccessPart));
|
||||
const auto parts = gsl::make_span(
|
||||
reinterpret_cast<const MultiTouchedPart*>(
|
||||
bytes + sizeof(MultiTouchedHeader)),
|
||||
reinterpret_cast<const MultiAccessPart*>(
|
||||
bytes + sizeof(MultiAccessHeader)),
|
||||
count);
|
||||
for (const auto &part : parts) {
|
||||
if (const auto i = _map.find(part.key); i != end(_map)) {
|
||||
|
@ -853,6 +944,12 @@ void Database::put(
|
|||
const Key &key,
|
||||
QByteArray value,
|
||||
FnMut<void(Error)> done) {
|
||||
if (value.isEmpty()) {
|
||||
remove(key, [done = std::move(done)]() mutable {
|
||||
done(Error::NoError());
|
||||
});
|
||||
return;
|
||||
}
|
||||
_removing.erase(key);
|
||||
|
||||
const auto checksum = CountChecksum(bytes::make_span(value));
|
||||
|
@ -881,9 +978,6 @@ void Database::put(
|
|||
} else {
|
||||
data.flush();
|
||||
invokeCallback(done, Error::NoError());
|
||||
|
||||
_touched.emplace(key);
|
||||
writeMultiTouchedLazy();
|
||||
startDelayedPruning();
|
||||
}
|
||||
} break;
|
||||
|
@ -892,13 +986,14 @@ void Database::put(
|
|||
}
|
||||
}
|
||||
|
||||
QString Database::writeKeyPlace(
|
||||
template <typename StoreRecord>
|
||||
QString Database::writeKeyPlaceGeneric(
|
||||
StoreRecord &&record,
|
||||
const Key &key,
|
||||
size_type size,
|
||||
uint32 checksum) {
|
||||
Expects(size <= _settings.maxDataSize);
|
||||
|
||||
auto record = Store();
|
||||
record.key = key;
|
||||
record.size = ReadTo<EntrySize>(size);
|
||||
record.checksum = checksum;
|
||||
|
@ -920,6 +1015,25 @@ QString Database::writeKeyPlace(
|
|||
return result;
|
||||
}
|
||||
|
||||
QString Database::writeKeyPlace(
|
||||
const Key &key,
|
||||
size_type size,
|
||||
uint32 checksum) {
|
||||
if (!_settings.trackEstimatedTime) {
|
||||
return writeKeyPlaceGeneric(Store(), key, size, checksum);
|
||||
}
|
||||
auto record = StoreWithTime();
|
||||
record.time = countTimePoint();
|
||||
if (record.time.relativeAdvancement * crl::time_type(1000)
|
||||
< _settings.writeBundleDelay) {
|
||||
// We don't want to produce a lot of unique relativeTime values.
|
||||
// So if change in it is not large we stick to the old value.
|
||||
record.time.system = _latestSystemTime;
|
||||
record.time.relativeAdvancement = 0;
|
||||
}
|
||||
return writeKeyPlaceGeneric(std::move(record), key, size, checksum);
|
||||
}
|
||||
|
||||
void Database::get(const Key &key, FnMut<void(QByteArray)> done) {
|
||||
if (_removing.find(key) != end(_removing)) {
|
||||
invokeCallback(done, QByteArray());
|
||||
|
@ -952,9 +1066,10 @@ void Database::get(const Key &key, FnMut<void(QByteArray)> done) {
|
|||
invokeCallback(done, QByteArray());
|
||||
} else {
|
||||
invokeCallback(done, std::move(result));
|
||||
|
||||
_touched.emplace(key);
|
||||
writeMultiTouchedLazy();
|
||||
if (_settings.trackEstimatedTime) {
|
||||
_accessed.emplace(key);
|
||||
writeMultiAccessLazy();
|
||||
}
|
||||
startDelayedPruning();
|
||||
}
|
||||
} break;
|
||||
|
@ -1009,44 +1124,38 @@ void Database::writeMultiRemove() {
|
|||
}
|
||||
}
|
||||
|
||||
void Database::writeMultiTouchedLazy() {
|
||||
if (_touched.size() == _settings.maxBundledRecords) {
|
||||
writeMultiTouched();
|
||||
void Database::writeMultiAccessLazy() {
|
||||
if (_accessed.size() == _settings.maxBundledRecords) {
|
||||
writeMultiAccess();
|
||||
} else {
|
||||
writeBundlesLazy();
|
||||
}
|
||||
}
|
||||
|
||||
void Database::writeMultiTouched() {
|
||||
if (!_touched.empty()) {
|
||||
writeMultiTouchedBlock();
|
||||
void Database::writeMultiAccess() {
|
||||
if (!_accessed.empty()) {
|
||||
writeMultiAccessBlock();
|
||||
}
|
||||
}
|
||||
|
||||
void Database::writeMultiTouchedBlock() {
|
||||
Expects(_touched.size() <= _settings.maxBundledRecords);
|
||||
void Database::writeMultiAccessBlock() {
|
||||
Expects(_settings.trackEstimatedTime);
|
||||
Expects(_accessed.size() <= _settings.maxBundledRecords);
|
||||
|
||||
const auto now = std::max(GetUnixtime(), 1);
|
||||
const auto delta = std::max(int64(now) - int64(_latestSystemTime), 0LL);
|
||||
const auto advancement = std::min(
|
||||
delta,
|
||||
int64(_settings.maxTimeAdvancement));
|
||||
const auto size = _touched.size();
|
||||
auto header = MultiTouchedHeader(now, advancement, size);
|
||||
auto list = std::vector<MultiTouchedPart>();
|
||||
const auto time = countTimePoint();
|
||||
const auto size = _accessed.size();
|
||||
auto header = MultiAccessHeader(time, size);
|
||||
auto list = std::vector<MultiAccessPart>();
|
||||
if (size > 0) {
|
||||
list.reserve(size);
|
||||
for (const auto &key : base::take(_touched)) {
|
||||
for (const auto &key : base::take(_accessed)) {
|
||||
list.push_back({ key });
|
||||
}
|
||||
}
|
||||
_latestSystemTime = now;
|
||||
if (advancement > 0) {
|
||||
_relativeTime += advancement;
|
||||
for (const auto &entry : list) {
|
||||
if (const auto i = _map.find(entry.key); i != end(_map)) {
|
||||
i->second.useTime = _relativeTime;
|
||||
}
|
||||
applyTimePoint(time);
|
||||
for (const auto &entry : list) {
|
||||
if (const auto i = _map.find(entry.key); i != end(_map)) {
|
||||
i->second.useTime = _relativeTime;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1060,7 +1169,9 @@ void Database::writeMultiTouchedBlock() {
|
|||
|
||||
void Database::writeBundles() {
|
||||
writeMultiRemove();
|
||||
writeMultiTouched();
|
||||
if (_settings.trackEstimatedTime) {
|
||||
writeMultiAccess();
|
||||
}
|
||||
}
|
||||
|
||||
void Database::createCleaner() {
|
||||
|
|
|
@ -40,15 +40,17 @@ inline bool operator<(const Key &a, const Key &b) {
|
|||
class Database {
|
||||
public:
|
||||
struct Settings {
|
||||
int64 totalSizeLimit = 1024 * 1024 * 1024;
|
||||
size_type totalTimeLimit = 30 * 86400; // One month in seconds.
|
||||
size_type maxBundledRecords = 16 * 1024;
|
||||
size_type readBlockSize = 8 * 1024 * 1024;
|
||||
size_type maxDataSize = 10 * 1024 * 1024;
|
||||
crl::time_type writeBundleDelay = 15 * 60 * crl::time_type(1000);
|
||||
|
||||
bool trackEstimatedTime = true;
|
||||
int64 totalSizeLimit = 1024 * 1024 * 1024;
|
||||
size_type totalTimeLimit = 30 * 86400; // One month in seconds.
|
||||
size_type maxTimeAdvancement = 365 * 86400; // One year in seconds.
|
||||
crl::time_type pruneTimeout = 5 * crl::time_type(1000);
|
||||
crl::time_type maxPruneCheckTimeout = 60 * 60 * crl::time_type(1000);
|
||||
crl::time_type maxPruneCheckTimeout = 3600 * crl::time_type(1000);
|
||||
};
|
||||
Database(const QString &path, const Settings &settings);
|
||||
|
||||
|
|
|
@ -460,7 +460,7 @@ TEST_CASE("cache db limits", "[storage_cache_database]") {
|
|||
AdvanceTime(1);
|
||||
db.get(Key{ 1, 0 }, nullptr);
|
||||
db.get(Key{ 0, 1 }, nullptr);
|
||||
AdvanceTime(2);
|
||||
AdvanceTime(3);
|
||||
db.get(Key{ 2, 0 }, GetValue);
|
||||
Semaphore.acquire();
|
||||
REQUIRE(Value.isEmpty());
|
||||
|
|
Loading…
Reference in New Issue