@@ -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
3036struct 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-
185214void 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 ¶ms)
222+ bool ACache::clear (const QString & query, const QVariantList ¶ms)
194223{
195224 Q_D (ACache);
196225 auto it = d->cache .constFind (query);
@@ -205,7 +234,7 @@ bool ACache::clear(QStringView query, const QVariantList ¶ms)
205234 return false ;
206235}
207236
208- bool ACache::expire (std::chrono::milliseconds maxAge, QStringView query, const QVariantList ¶ms)
237+ bool ACache::expire (std::chrono::milliseconds maxAge, const QString & query, const QVariantList ¶ms)
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
267297AExpectedResult
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-
315315void ACache::exec (const QString &query, QObject *receiver, AResultFn cb)
316316{
317317 execExpiring (query, -1ms, {}, receiver, cb);
0 commit comments