fix(amm): evict stale accounts before forced reads

This commit is contained in:
Ricardo Guilherme Schmidt 2026-07-17 19:45:07 -03:00
parent 14dff686d1
commit ba23429fc4
No known key found for this signature in database
GPG Key ID: 1396EA17DE132FFE
2 changed files with 58 additions and 2 deletions

View File

@ -155,6 +155,8 @@ void SequencerClient::readAccount(const QString& accountId,
}); });
return; return;
} }
if (forceRefresh)
m_cache.remove(accountId);
if (!forceRefresh && m_cache.contains(accountId)) { if (!forceRefresh && m_cache.contains(accountId)) {
const WalletAccountRead cached = m_cache.value(accountId); const WalletAccountRead cached = m_cache.value(accountId);
QTimer::singleShot(0, this, QTimer::singleShot(0, this,

View File

@ -14,6 +14,8 @@
#include <QTemporaryFile> #include <QTemporaryFile>
#include <QTimer> #include <QTimer>
#include <utility>
namespace { namespace {
bool expect(bool condition, const char* message) bool expect(bool condition, const char* message)
{ {
@ -48,6 +50,7 @@ namespace {
} }
int requestCount() const { return m_requestCount; } int requestCount() const { return m_requestCount; }
void failNextRequest() { ++m_failuresRemaining; }
private: private:
void process(QTcpSocket* socket) void process(QTcpSocket* socket)
@ -71,10 +74,16 @@ namespace {
return; return;
++m_requestCount; ++m_requestCount;
const bool fail = m_failuresRemaining > 0;
if (fail)
--m_failuresRemaining;
const QByteArray payload = QByteArrayLiteral( const QByteArray payload = QByteArrayLiteral(
R"({"jsonrpc":"2.0","id":1,"result":null})"); R"({"jsonrpc":"2.0","id":1,"result":null})");
QByteArray response = QByteArrayLiteral( QByteArray response = fail
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: "); ? QByteArrayLiteral(
"HTTP/1.1 500 Internal Server Error\r\nContent-Type: application/json\r\nContent-Length: ")
: QByteArrayLiteral(
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: ");
response += QByteArray::number(payload.size()); response += QByteArray::number(payload.size());
response += QByteArrayLiteral("\r\nConnection: close\r\n\r\n"); response += QByteArrayLiteral("\r\nConnection: close\r\n\r\n");
response += payload; response += payload;
@ -86,6 +95,7 @@ namespace {
QTcpServer m_server; QTcpServer m_server;
QHash<QTcpSocket*, QByteArray> m_requests; QHash<QTcpSocket*, QByteArray> m_requests;
int m_requestCount = 0; int m_requestCount = 0;
int m_failuresRemaining = 0;
}; };
class FakeWallet final : public WalletProvider { class FakeWallet final : public WalletProvider {
@ -262,6 +272,28 @@ namespace {
loop.exec(); loop.exec();
return completed; return completed;
} }
bool waitForAccounts(SequencerClient& sequencer,
const QStringList& accountIds,
bool forceRefresh,
QVector<WalletAccountRead>* reads)
{
bool completed = false;
QEventLoop loop;
QTimer timeout;
timeout.setSingleShot(true);
QObject::connect(&timeout, &QTimer::timeout, &loop, &QEventLoop::quit);
sequencer.readAccounts(accountIds, forceRefresh,
[&](QVector<WalletAccountRead> result) {
*reads = std::move(result);
completed = true;
loop.quit();
});
timeout.start(3000);
if (!completed)
loop.exec();
return completed;
}
} }
int main(int argc, char** argv) int main(int argc, char** argv)
@ -391,5 +423,27 @@ int main(int argc, char** argv)
"forced context should reread config and wallet holding")) "forced context should reread config and wallet holding"))
return 1; return 1;
server.failNextRequest();
QVector<WalletAccountRead> failedRefresh;
if (!expect(waitForAccounts(sequencer, { holding.address }, true,
&failedRefresh),
"failed forced holding refresh should complete"))
return 1;
if (!expect(failedRefresh.size() == 1 && !failedRefresh.constFirst().ok(),
"forced holding refresh should surface the sequencer failure"))
return 1;
const int requestsAfterFailure = server.requestCount();
QVector<WalletAccountRead> recoveredRead;
if (!expect(waitForAccounts(sequencer, { holding.address }, false,
&recoveredRead),
"holding read after failure should complete"))
return 1;
if (!expect(server.requestCount() == requestsAfterFailure + 1,
"failed forced refresh should evict the stale holding cache"))
return 1;
if (!expect(recoveredRead.size() == 1 && recoveredRead.constFirst().ok(),
"holding read should recover from the sequencer"))
return 1;
return 0; return 0;
} }