Skip to content

Commit 347cd82

Browse files
committed
Remove usage of QStringView in ACache
For some unknown reason sometimes when QMultiHash<QStringView is used and we try to find(query) an entry the iterator returned is not the latest inserted, and our while loop to find the matching ACacheValue.args will not find it and silently ignore the return causing the caller to "leak" as it's stuck untill the cache is cleaned. With QString that does not happen, and eventually in Qt 6.8 we can use the view to do lookups.
1 parent a219588 commit 347cd82

2 files changed

Lines changed: 112 additions & 125 deletions

File tree

src/acache.cpp

Lines changed: 106 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,15 @@ struct ACacheReceiverCb {
2525
AResultFn cb;
2626
QPointer<QObject> receiver;
2727
QObject *checkReceiver = nullptr;
28+
29+
void emitResult(AResult result) const {
30+
if (cb && (checkReceiver == nullptr || !receiver.isNull())) {
31+
cb(result);
32+
}
33+
}
2834
};
2935

3036
struct ACacheValue {
31-
QString query;
3237
QVariantList args;
3338
std::vector<ACacheReceiverCb> receivers;
3439
AResult result;
@@ -44,53 +49,58 @@ class ACachePrivate
4449
Pool,
4550
};
4651

47-
bool searchOrQueue(QStringView query,
52+
bool searchOrQueue(const QString &query,
4853
std::chrono::milliseconds maxAge,
4954
const QVariantList &args,
5055
QObject *receiver,
5156
AResultFn cb);
52-
void requestData(const QString &query,
53-
const QVariantList &args,
57+
ACoroTerminator requestData(QString query,
58+
QVariantList args,
5459
QObject *receiver,
5560
AResultFn cb);
5661

5762
QObject *q_ptr;
5863
QString poolName;
5964
ADatabase db;
60-
QMultiHash<QStringView, ACacheValue> cache;
65+
66+
// For some unknown reason sometimes when QMultiHash<QStringView
67+
// is used and we try to find(query) an entry the iterator returned
68+
// is not the latest inserted, and our while loop to find the
69+
// matching ACacheValue.args will not find it and silently ignore
70+
// the return causing the caller to "leak" as it's stuck untill
71+
// the cache is cleaned.
72+
// With QString that does not happen, and eventually in Qt 6.8 we
73+
// can use the view to do lookups.
74+
QMultiHash<QString, ACacheValue> cache;
6175
DbSource dbSource = DbSource::Unset;
6276
};
6377

64-
bool ACachePrivate::searchOrQueue(QStringView query,
78+
bool ACachePrivate::searchOrQueue(const QString &query,
6579
std::chrono::milliseconds maxAge,
6680
const QVariantList &args,
6781
QObject *receiver,
6882
AResultFn cb)
6983
{
70-
auto it = cache.find(query);
71-
while (it != cache.end() && it.key() == query) {
84+
auto it = cache.constFind(query);
85+
while (it != cache.constEnd() && it.key() == query) {
7286
auto &value = *it;
7387
if (value.args == args) {
74-
if (value.hasResultTP) {
75-
if (maxAge != -1ms) {
88+
if (value.hasResultTP.has_value()) {
89+
if (maxAge >= 0ms) {
7690
const auto cutAge = steady_clock::now() - maxAge;
77-
if (value.hasResultTP < cutAge) {
91+
if (value.hasResultTP.value() < cutAge) {
92+
qDebug(ASQL_CACHE) << "Expiring cache" << query.left(15) << args;
7893
cache.erase(it);
7994
break;
80-
} else {
81-
qDebug(ASQL_CACHE) << "cached data ready" << query;
82-
if (cb) {
83-
cb(value.result);
84-
}
85-
}
86-
} else {
87-
qDebug(ASQL_CACHE) << "cached data ready" << query;
88-
if (cb) {
89-
cb(value.result);
9095
}
9196
}
97+
98+
qDebug(ASQL_CACHE) << "Cached query ready" << query.left(15) << args;
99+
if (cb) {
100+
cb(value.result);
101+
}
92102
} else {
93-
qDebug(ASQL_CACHE) << "queuing request" << query;
103+
qDebug(ASQL_CACHE) << "Queuing request" << query.left(15) << args;
94104
// queue another request
95105
value.receivers.emplace_back(ACacheReceiverCb{cb, receiver, receiver});
96106
}
@@ -103,56 +113,80 @@ bool ACachePrivate::searchOrQueue(QStringView query,
103113
return false;
104114
}
105115

106-
void ACachePrivate::requestData(const QString &query,
107-
const QVariantList &args,
116+
ACoroTerminator ACachePrivate::requestData(QString query,
117+
QVariantList args,
108118
QObject *receiver,
109119
AResultFn cb)
110120
{
111-
qCDebug(ASQL_CACHE) << "requesting data" << query << int(dbSource);
112-
113-
ACacheValue cacheValue;
114-
cacheValue.query = query;
115-
cacheValue.args = args;
116-
cacheValue.receivers.emplace_back(ACacheReceiverCb{cb, receiver, receiver});
117-
118-
cache.emplace(query, std::move(cacheValue));
121+
qCDebug(ASQL_CACHE) << "Requesting data" << query.left(15) << args << int(dbSource);
122+
co_yield q_ptr;
119123

120-
auto performQuery = [this, query, args](ADatabase db) {
121-
db.exec(query, args, q_ptr, [query, args, this](AResult &result) {
122-
auto it = cache.find(query);
123-
while (it != cache.end() && it.key() == query) {
124-
ACacheValue &value = it.value();
125-
if (value.args == args) {
126-
value.result = result;
127-
value.hasResultTP = steady_clock::now();
128-
qInfo(ASQL_CACHE) << "got request data, dispatching to"
129-
<< value.receivers.size() << "receivers" << query;
130-
for (const ACacheReceiverCb &receiverObj : value.receivers) {
131-
if (receiverObj.checkReceiver == nullptr ||
132-
!receiverObj.receiver.isNull()) {
133-
qDebug(ASQL_CACHE)
134-
<< "dispatching to receiver" << receiverObj.checkReceiver << query;
135-
receiverObj.cb(result);
136-
}
137-
}
138-
value.receivers.clear();
139-
}
140-
++it;
141-
}
142-
});
143-
};
124+
ACacheReceiverCb cacheReceiver{cb, receiver, receiver};
144125

126+
ADatabase localDb;
145127
switch (dbSource) {
146128
case ACachePrivate::DbSource::Database:
147-
performQuery(db);
129+
localDb = db;
148130
break;
149131
case ACachePrivate::DbSource::Pool:
150-
APool::database(q_ptr, performQuery, poolName);
132+
{
133+
auto dbFromPool = co_await APool::coDatabase(q_ptr, poolName);
134+
if (!dbFromPool) {
135+
qCritical(ASQL_CACHE) << "Failed to get connection from pool" << dbFromPool.error();
136+
if (cb) {
137+
AResult result;
138+
cb(result);
139+
}
140+
co_return;
141+
}
142+
localDb = *dbFromPool;
151143
break;
144+
}
152145
default:
153-
qCCritical(ASQL_CACHE) << "Pool database was not set" << int(dbSource);
146+
qCCritical(ASQL_CACHE) << "Cache database source was not set";
147+
if (cb) {
148+
AResult result;
149+
cb(result);
150+
}
151+
co_return;
152+
}
153+
154+
ACacheValue cacheValue;
155+
cacheValue.args = args;
156+
cacheValue.receivers.emplace_back(cacheReceiver);
157+
158+
cache.emplace(query, std::move(cacheValue));
159+
160+
auto result = co_await localDb.coExec(query, args, q_ptr);
161+
bool found = false;
162+
auto it = cache.constFind(query);
163+
while (it != cache.constEnd() && it.key() == query) {
164+
ACacheValue &value = it.value();
165+
if (value.args == args) {
166+
value.result = *result;
167+
value.hasResultTP = steady_clock::now();
168+
169+
// Copy the receivers as the callback call might invalidade the cache
170+
std::vector<ACacheReceiverCb> receivers = std::move(value.receivers);
171+
value.receivers.clear();
172+
173+
qDebug(ASQL_CACHE) << "Got request data, dispatching to"
174+
<< receivers.size() << "receivers" << query.left(15) << args;
175+
for (const ACacheReceiverCb &receiverObj : receivers) {
176+
qDebug(ASQL_CACHE) << "Dispatching to receiver" << receiverObj.checkReceiver << query.left(15) << args;
177+
receiverObj.emitResult(*result);
178+
}
179+
found = true;
180+
181+
break;
182+
}
183+
++it;
184+
}
185+
186+
if (!found) {
187+
qWarning(ASQL_CACHE) << "Queued request not found" << query.left(15) << args;
154188
AResult result;
155-
cb(result);
189+
cacheReceiver.emitResult(result);
156190
}
157191
}
158192

@@ -177,11 +211,6 @@ void ACache::setDatabasePool(const QString &poolName)
177211
d->dbSource = ACachePrivate::DbSource::Pool;
178212
}
179213

180-
void ACache::setDatabasePool(QStringView poolName)
181-
{
182-
ACache::setDatabasePool(poolName.toString());
183-
}
184-
185214
void ACache::setDatabase(const ADatabase &db)
186215
{
187216
Q_D(ACache);
@@ -190,7 +219,7 @@ void ACache::setDatabase(const ADatabase &db)
190219
d->dbSource = ACachePrivate::DbSource::Database;
191220
}
192221

193-
bool ACache::clear(QStringView query, const QVariantList &params)
222+
bool ACache::clear(const QString &query, const QVariantList &params)
194223
{
195224
Q_D(ACache);
196225
auto it = d->cache.constFind(query);
@@ -205,7 +234,7 @@ bool ACache::clear(QStringView query, const QVariantList &params)
205234
return false;
206235
}
207236

208-
bool ACache::expire(std::chrono::milliseconds maxAge, QStringView query, const QVariantList &params)
237+
bool ACache::expire(std::chrono::milliseconds maxAge, const QString &query, const QVariantList &params)
209238
{
210239
Q_D(ACache);
211240
int ret = false;
@@ -214,9 +243,9 @@ bool ACache::expire(std::chrono::milliseconds maxAge, QStringView query, const Q
214243
while (it != d->cache.constEnd() && it.key() == query) {
215244
const ACacheValue &value = *it;
216245
if (value.args == params) {
217-
if (value.hasResultTP && value.hasResultTP < cutAge) {
246+
if (value.hasResultTP.has_value() && value.hasResultTP.value() < cutAge) {
218247
ret = true;
219-
qDebug(ASQL_CACHE) << "clearing cache" << query << params;
248+
// qDebug(ASQL_CACHE) << "clearing cache" << query << params;
220249
d->cache.erase(it);
221250
}
222251
break;
@@ -231,16 +260,17 @@ int ACache::expireAll(std::chrono::milliseconds maxAge)
231260
Q_D(ACache);
232261
int ret = 0;
233262
const auto cutAge = steady_clock::now() - maxAge;
234-
auto it = d->cache.begin();
235-
while (it != d->cache.end()) {
263+
auto it = d->cache.constBegin();
264+
while (it != d->cache.constEnd()) {
236265
const ACacheValue &value = *it;
237-
if (value.hasResultTP && value.hasResultTP < cutAge) {
266+
if (value.hasResultTP.has_value() && value.hasResultTP.value() < cutAge) {
238267
it = d->cache.erase(it);
239268
++ret;
240269
} else {
241270
++it;
242271
}
243272
}
273+
// qDebug(ASQL_CACHE) << "cleared cache" << ret;
244274
return ret;
245275
}
246276

@@ -250,29 +280,29 @@ int ACache::size() const
250280
return d->cache.size();
251281
}
252282

253-
AExpectedResult ACache::coExec(QStringView query, QObject *receiver)
283+
AExpectedResult ACache::coExec(const QString &query, QObject *receiver)
254284
{
255285
AExpectedResult coro(receiver);
256286
execExpiring(query, -1ms, {}, receiver, coro.callback);
257287
return coro;
258288
}
259289

260-
AExpectedResult ACache::coExec(QStringView query, const QVariantList &args, QObject *receiver)
290+
AExpectedResult ACache::coExec(const QString &query, const QVariantList &args, QObject *receiver)
261291
{
262292
AExpectedResult coro(receiver);
263293
execExpiring(query, -1ms, args, receiver, coro.callback);
264294
return coro;
265295
}
266296

267297
AExpectedResult
268-
ACache::coExecExpiring(QStringView query, std::chrono::milliseconds maxAge, QObject *receiver)
298+
ACache::coExecExpiring(const QString &query, std::chrono::milliseconds maxAge, QObject *receiver)
269299
{
270300
AExpectedResult coro(receiver);
271301
execExpiring(query, maxAge, {}, receiver, coro.callback);
272302
return coro;
273303
}
274304

275-
AExpectedResult ACache::coExecExpiring(QStringView query,
305+
AExpectedResult ACache::coExecExpiring(const QString &query,
276306
std::chrono::milliseconds maxAge,
277307
const QVariantList &args,
278308
QObject *receiver)
@@ -282,36 +312,6 @@ AExpectedResult ACache::coExecExpiring(QStringView query,
282312
return coro;
283313
}
284314

285-
void ACache::exec(QStringView query, QObject *receiver, AResultFn cb)
286-
{
287-
execExpiring(query, -1ms, {}, receiver, cb);
288-
}
289-
290-
void ACache::exec(QStringView query, const QVariantList &args, QObject *receiver, AResultFn cb)
291-
{
292-
execExpiring(query, -1ms, args, receiver, cb);
293-
}
294-
295-
void ACache::execExpiring(QStringView query,
296-
std::chrono::milliseconds maxAge,
297-
QObject *receiver,
298-
AResultFn cb)
299-
{
300-
execExpiring(query, maxAge, {}, receiver, cb);
301-
}
302-
303-
void ACache::execExpiring(QStringView query,
304-
std::chrono::milliseconds maxAge,
305-
const QVariantList &args,
306-
QObject *receiver,
307-
AResultFn cb)
308-
{
309-
Q_D(ACache);
310-
if (!d->searchOrQueue(query, maxAge, args, receiver, cb)) {
311-
d->requestData(query.toString(), args, receiver, cb);
312-
}
313-
}
314-
315315
void ACache::exec(const QString &query, QObject *receiver, AResultFn cb)
316316
{
317317
execExpiring(query, -1ms, {}, receiver, cb);

0 commit comments

Comments
 (0)