Cache media search results until empty query.

This commit is contained in:
John Preston 2017-10-31 11:03:18 +04:00
parent eb2719fad1
commit c9152b0b3a
5 changed files with 281 additions and 67 deletions

View File

@ -0,0 +1,155 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
namespace base {
namespace details {
template <
typename Type,
typename Operator,
typename = decltype(Operator{}(
std::declval<Type>(),
std::declval<Type>()))>
char test_operator(const Type &, const Operator &);
int test_operator(...);
template <typename Type, template <typename> typename Operator>
struct has_operator
: std::bool_constant<
sizeof(test_operator(
std::declval<Type>(),
std::declval<Operator<Type>>()
)) == sizeof(char)> {
};
template <
typename Type,
template <typename> typename Operator>
constexpr bool has_operator_v
= has_operator<Type, Operator>::value;
template <typename Type>
constexpr bool has_less_v = has_operator_v<Type, std::less>;
template <typename Type>
constexpr bool has_greater_v = has_operator_v<Type, std::greater>;
template <typename Type>
constexpr bool has_less_equal_v = has_operator_v<Type, std::less_equal>;
template <typename Type>
constexpr bool has_greater_equal_v = has_operator_v<Type, std::greater_equal>;
template <typename Type>
constexpr bool has_equal_to_v = has_operator_v<Type, std::equal_to>;
template <typename Type>
constexpr bool has_not_equal_to_v = has_operator_v<Type, std::not_equal_to>;
} // namespace details
} // namespace base
template <
typename ValueType,
typename Helper = decltype(
value_ordering_helper(std::declval<ValueType>()))>
inline auto operator<(const ValueType &a, const ValueType &b)
-> std::enable_if_t<base::details::has_less_v<Helper>, bool> {
return value_ordering_helper(a) < value_ordering_helper(b);
}
template <
typename ValueType,
typename Helper = decltype(
value_ordering_helper(std::declval<ValueType>()))>
inline auto operator>(const ValueType &a, const ValueType &b)
-> std::enable_if_t<
base::details::has_greater_v<Helper>
|| base::details::has_less_v<Helper>,
bool
> {
if constexpr (base::details::has_greater_v<Helper>) {
return value_ordering_helper(a) > value_ordering_helper(b);
} else {
return value_ordering_helper(b) < value_ordering_helper(a);
}
}
template <
typename ValueType,
typename Helper = decltype(
value_ordering_helper(std::declval<ValueType>()))>
inline auto operator<=(const ValueType &a, const ValueType &b)
-> std::enable_if_t<
base::details::has_less_equal_v<Helper>
|| base::details::has_less_v<Helper>,
bool
> {
if constexpr (base::details::has_less_equal_v<Helper>) {
return value_ordering_helper(a) <= value_ordering_helper(b);
} else {
return !(value_ordering_helper(b) < value_ordering_helper(a));
}
}
template <
typename ValueType,
typename Helper = decltype(
value_ordering_helper(std::declval<ValueType>()))>
inline auto operator>=(const ValueType &a, const ValueType &b)
-> std::enable_if_t<
base::details::has_greater_equal_v<Helper>
|| base::details::has_less_v<Helper>,
bool
> {
if constexpr (base::details::has_greater_equal_v<Helper>) {
return value_ordering_helper(a) >= value_ordering_helper(b);
} else {
return !(value_ordering_helper(a) < value_ordering_helper(b));
}
}
template <
typename ValueType,
typename Helper = decltype(
value_ordering_helper(std::declval<ValueType>()))>
inline auto operator==(const ValueType &a, const ValueType &b)
-> std::enable_if_t<base::details::has_equal_to_v<Helper>, bool> {
return value_ordering_helper(a) == value_ordering_helper(b);
}
template <
typename ValueType,
typename Helper = decltype(
value_ordering_helper(std::declval<ValueType>()))>
inline auto operator!=(const ValueType &a, const ValueType &b)
-> std::enable_if_t<
base::details::has_not_equal_to_v<Helper>
|| base::details::has_equal_to_v<Helper>,
bool
> {
if constexpr (base::details::has_not_equal_to_v<Helper>) {
return value_ordering_helper(a) != value_ordering_helper(b);
} else {
return !(value_ordering_helper(a) == value_ordering_helper(b));
}
}

View File

@ -169,19 +169,39 @@ SearchResult ParseSearchResult(
return result; return result;
} }
SingleSearchController::SingleSearchController(const Query &query) SearchController::CacheEntry::CacheEntry(const Query &query)
: _query(query) : peerData(App::peer(query.peerId))
, _peerData(App::peer(query.peerId)) , migratedData(query.migratedPeerId
, _migratedData(query.migratedPeerId
? base::make_optional(Data(App::peer(query.migratedPeerId))) ? base::make_optional(Data(App::peer(query.migratedPeerId)))
: base::none) { : base::none) {
} }
rpl::producer<SparseIdsMergedSlice> SingleSearchController::idsSlice( bool SearchController::hasInCache(const Query &query) const {
return query.query.isEmpty() || _cache.contains(query);
}
void SearchController::setQuery(const Query &query) {
if (query.query.isEmpty()) {
_cache.clear();
_current = _cache.end();
} else {
_current = _cache.find(query);
}
if (_current == _cache.end()) {
_current = _cache.emplace(
query,
std::make_unique<CacheEntry>(query)).first;
}
}
rpl::producer<SparseIdsMergedSlice> SearchController::idsSlice(
SparseIdsMergedSlice::UniversalMsgId aroundId, SparseIdsMergedSlice::UniversalMsgId aroundId,
int limitBefore, int limitBefore,
int limitAfter) { int limitAfter) {
auto createSimpleViewer = [this]( Expects(_current != _cache.cend());
auto query = (const Query&)_current->first;
auto createSimpleViewer = [=](
PeerId peerId, PeerId peerId,
SparseIdsSlice::Key simpleKey, SparseIdsSlice::Key simpleKey,
int limitBefore, int limitBefore,
@ -189,34 +209,41 @@ rpl::producer<SparseIdsMergedSlice> SingleSearchController::idsSlice(
return simpleIdsSlice( return simpleIdsSlice(
peerId, peerId,
simpleKey, simpleKey,
query,
limitBefore, limitBefore,
limitAfter); limitAfter);
}; };
return SparseIdsMergedSlice::CreateViewer( return SparseIdsMergedSlice::CreateViewer(
SparseIdsMergedSlice::Key( SparseIdsMergedSlice::Key(
_query.peerId, query.peerId,
_query.migratedPeerId, query.migratedPeerId,
aroundId), aroundId),
limitBefore, limitBefore,
limitAfter, limitAfter,
std::move(createSimpleViewer)); std::move(createSimpleViewer));
} }
rpl::producer<SparseIdsSlice> SingleSearchController::simpleIdsSlice( rpl::producer<SparseIdsSlice> SearchController::simpleIdsSlice(
PeerId peerId, PeerId peerId,
MsgId aroundId, MsgId aroundId,
const Query &query,
int limitBefore, int limitBefore,
int limitAfter) { int limitAfter) {
Expects(peerId != 0); Expects(peerId != 0);
Expects(IsServerMsgId(aroundId) || (aroundId == 0)); Expects(IsServerMsgId(aroundId) || (aroundId == 0));
Expects((aroundId != 0) Expects((aroundId != 0)
|| (limitBefore == 0 && limitAfter == 0)); || (limitBefore == 0 && limitAfter == 0));
Expects((_query.peerId == peerId) Expects((query.peerId == peerId)
|| (_query.migratedPeerId == peerId)); || (query.migratedPeerId == peerId));
auto listData = (peerId == _query.peerId) auto it = _cache.find(query);
? &_peerData if (it == _cache.end()) {
: &*_migratedData; return [=](auto) { return rpl::lifetime(); };
}
auto listData = (peerId == query.peerId)
? &it->second->peerData
: &*it->second->migratedData;
return [=](auto consumer) { return [=](auto consumer) {
auto lifetime = rpl::lifetime(); auto lifetime = rpl::lifetime();
auto builder = lifetime.make_state<SparseIdsSliceBuilder>( auto builder = lifetime.make_state<SparseIdsSliceBuilder>(
@ -226,7 +253,7 @@ rpl::producer<SparseIdsSlice> SingleSearchController::simpleIdsSlice(
builder->insufficientAround() builder->insufficientAround()
| rpl::start_with_next([=]( | rpl::start_with_next([=](
const SparseIdsSliceBuilder::AroundData &data) { const SparseIdsSliceBuilder::AroundData &data) {
requestMore(data, listData); requestMore(data, query, listData);
}, lifetime); }, lifetime);
auto pushNextSnapshot = [=] { auto pushNextSnapshot = [=] {
@ -255,28 +282,41 @@ rpl::producer<SparseIdsSlice> SingleSearchController::simpleIdsSlice(
| rpl::filter([=] { return builder->removeAll(); }) | rpl::filter([=] { return builder->removeAll(); })
| rpl::start_with_next(pushNextSnapshot, lifetime); | rpl::start_with_next(pushNextSnapshot, lifetime);
builder->checkInsufficient(); using Result = Storage::SparseIdsListResult;
listData->list.query(Storage::SparseIdsListQuery(
aroundId,
limitBefore,
limitAfter))
| rpl::filter([=](const Result &result) {
return builder->applyInitial(result);
})
| rpl::start_with_next_done(
pushNextSnapshot,
[=] { builder->checkInsufficient(); },
lifetime);
return lifetime; return lifetime;
}; };
} }
void SingleSearchController::requestMore( void SearchController::requestMore(
const SparseIdsSliceBuilder::AroundData &key, const SparseIdsSliceBuilder::AroundData &key,
const Query &query,
Data *listData) { Data *listData) {
if (listData->requests.contains(key)) { if (listData->requests.contains(key)) {
return; return;
} }
auto requestId = request(PrepareSearchRequest( auto requestId = request(PrepareSearchRequest(
listData->peer, listData->peer,
_query.type, query.type,
_query.query, query.query,
key.aroundId, key.aroundId,
key.direction) key.direction)
).done([=](const MTPmessages_Messages &result) { ).done([=](const MTPmessages_Messages &result) {
listData->requests.remove(key);
auto parsed = ParseSearchResult( auto parsed = ParseSearchResult(
listData->peer, listData->peer,
_query.type, query.type,
key.aroundId, key.aroundId,
key.direction, key.direction,
result); result);
@ -285,8 +325,9 @@ void SingleSearchController::requestMore(
parsed.noSkipRange, parsed.noSkipRange,
parsed.fullCount); parsed.fullCount);
}).send(); }).send();
listData->requests.emplace(key, [=] {
listData->requests.emplace(key, requestId); request(requestId).cancel();
});
} }
DelayedSearchController::DelayedSearchController() { DelayedSearchController::DelayedSearchController() {
@ -294,9 +335,18 @@ DelayedSearchController::DelayedSearchController() {
} }
void DelayedSearchController::setQuery(const Query &query) { void DelayedSearchController::setQuery(const Query &query) {
setQuery( setQuery(query, kDefaultSearchTimeoutMs);
query, }
query.query.isEmpty() ? 0 : kDefaultSearchTimeoutMs);
void DelayedSearchController::setQuery(
const Query &query,
TimeMs delay) {
if (_controller.hasInCache(query)) {
setQueryFast(query);
} else {
_nextQuery = query;
_timer.callOnce(delay);
}
} }
void DelayedSearchController::setQueryFast(const Query &query) { void DelayedSearchController::setQueryFast(const Query &query) {

View File

@ -24,6 +24,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "history/history_sparse_ids.h" #include "history/history_sparse_ids.h"
#include "storage/storage_sparse_ids_list.h" #include "storage/storage_sparse_ids_list.h"
#include "storage/storage_shared_media.h" #include "storage/storage_shared_media.h"
#include "base/value_ordering.h"
namespace Api { namespace Api {
@ -47,7 +48,7 @@ SearchResult ParseSearchResult(
SparseIdsLoadDirection direction, SparseIdsLoadDirection direction,
const MTPmessages_Messages &data); const MTPmessages_Messages &data);
class SingleSearchController : private MTP::Sender { class SearchController : private MTP::Sender {
public: public:
struct Query { struct Query {
using MediaType = Storage::SharedMediaType; using MediaType = Storage::SharedMediaType;
@ -57,19 +58,30 @@ public:
MediaType type = MediaType::kCount; MediaType type = MediaType::kCount;
QString query; QString query;
// from_id, min_date, max_date // from_id, min_date, max_date
friend inline auto value_ordering_helper(const Query &value) {
return std::tie(
value.peerId,
value.migratedPeerId,
value.type,
value.query);
}
}; };
SingleSearchController(const Query &query); void setQuery(const Query &query);
bool hasInCache(const Query &query) const;
Query query() const {
Expects(_current != _cache.cend());
return _current->first;
}
rpl::producer<SparseIdsMergedSlice> idsSlice( rpl::producer<SparseIdsMergedSlice> idsSlice(
SparseIdsMergedSlice::UniversalMsgId aroundId, SparseIdsMergedSlice::UniversalMsgId aroundId,
int limitBefore, int limitBefore,
int limitAfter); int limitAfter);
Query query() const {
return _query;
}
private: private:
struct Data { struct Data {
explicit Data(not_null<PeerData*> peer) : peer(peer) { explicit Data(not_null<PeerData*> peer) : peer(peer) {
@ -79,49 +91,40 @@ private:
Storage::SparseIdsList list; Storage::SparseIdsList list;
base::flat_map< base::flat_map<
SparseIdsSliceBuilder::AroundData, SparseIdsSliceBuilder::AroundData,
mtpRequestId> requests; rpl::lifetime> requests;
}; };
using SliceUpdate = Storage::SparseIdsSliceUpdate; using SliceUpdate = Storage::SparseIdsSliceUpdate;
struct CacheEntry {
CacheEntry(const Query &query);
Data peerData;
base::optional<Data> migratedData;
};
struct CacheLess {
inline bool operator()(const Query &a, const Query &b) const {
return (a < b);
}
};
using Cache = base::flat_map<
Query,
std::unique_ptr<CacheEntry>,
CacheLess>;
rpl::producer<SparseIdsSlice> simpleIdsSlice( rpl::producer<SparseIdsSlice> simpleIdsSlice(
PeerId peerId, PeerId peerId,
MsgId aroundId, MsgId aroundId,
const Query &query,
int limitBefore, int limitBefore,
int limitAfter); int limitAfter);
void requestMore( void requestMore(
const SparseIdsSliceBuilder::AroundData &key, const SparseIdsSliceBuilder::AroundData &key,
const Query &query,
Data *listData); Data *listData);
Query _query; Cache _cache;
Data _peerData; Cache::iterator _current = _cache.end();
base::optional<Data> _migratedData;
};
class SearchController {
public:
using Query = SingleSearchController::Query;
void setQuery(const Query &query) {
_controller = SingleSearchController(query);
}
Query query() const {
return _controller ? _controller->query() : Query();
}
rpl::producer<SparseIdsMergedSlice> idsSlice(
SparseIdsMergedSlice::UniversalMsgId aroundId,
int limitBefore,
int limitAfter) {
Expects(_controller.has_value());
return _controller->idsSlice(
aroundId,
limitBefore,
limitAfter);
}
private:
base::optional<SingleSearchController> _controller;
}; };
@ -129,12 +132,9 @@ class DelayedSearchController {
public: public:
DelayedSearchController(); DelayedSearchController();
using Query = SingleSearchController::Query; using Query = SearchController::Query;
void setQuery(const Query &query); void setQuery(const Query &query);
void setQuery(const Query &query, TimeMs delay) { void setQuery(const Query &query, TimeMs delay);
_nextQuery = query;
_timer.callOnce(delay);
}
void setQueryFast(const Query &query); void setQueryFast(const Query &query);
Query currentQuery() const { Query currentQuery() const {

View File

@ -38,6 +38,7 @@ class event_stream {
public: public:
event_stream(); event_stream();
event_stream(event_stream &&other); event_stream(event_stream &&other);
event_stream &operator=(event_stream &&other);
template <typename OtherValue> template <typename OtherValue>
void fire_forward(OtherValue &&value) const; void fire_forward(OtherValue &&value) const;
@ -90,6 +91,13 @@ inline event_stream<Value>::event_stream(event_stream &&other)
: _consumers(base::take(other._consumers)) { : _consumers(base::take(other._consumers)) {
} }
template <typename Value>
inline event_stream<Value> &event_stream<Value>::operator=(
event_stream &&other) {
_consumers = base::take(other._consumers);
return *this;
}
template <typename Value> template <typename Value>
template <typename OtherValue> template <typename OtherValue>
inline void event_stream<Value>::fire_forward( inline void event_stream<Value>::fire_forward(

View File

@ -29,6 +29,7 @@
<(src_loc)/base/unique_any.h <(src_loc)/base/unique_any.h
<(src_loc)/base/unique_function.h <(src_loc)/base/unique_function.h
<(src_loc)/base/unique_qptr.h <(src_loc)/base/unique_qptr.h
<(src_loc)/base/value_ordering.h
<(src_loc)/base/variant.h <(src_loc)/base/variant.h
<(src_loc)/base/virtual_method.h <(src_loc)/base/virtual_method.h
<(src_loc)/base/weak_unique_ptr.h <(src_loc)/base/weak_unique_ptr.h