Skip to content

Commit 245a808

Browse files
authored
feat: Cache state endpoint (#2642)
1 parent 586a211 commit 245a808

File tree

9 files changed

+221
-25
lines changed

9 files changed

+221
-25
lines changed

src/app/ClioApplication.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ ClioApplication::run(bool const useNgWebServer)
189189

190190
httpServer->onGet("/metrics", MetricsHandler{adminVerifier});
191191
httpServer->onGet("/health", HealthCheckHandler{});
192+
httpServer->onGet("/cache_state", CacheStateHandler{cache});
192193
auto requestHandler = RequestHandler{adminVerifier, handler};
193194
httpServer->onPost("/", requestHandler);
194195
httpServer->onWs(std::move(requestHandler));
@@ -214,7 +215,7 @@ ClioApplication::run(bool const useNgWebServer)
214215
// Init the web server
215216
auto handler = std::make_shared<web::RPCServerHandler<RPCEngineType>>(config_, backend, rpcEngine, etl, dosGuard);
216217

217-
auto const httpServer = web::makeHttpServer(config_, ioc, dosGuard, handler);
218+
auto const httpServer = web::makeHttpServer(config_, ioc, dosGuard, handler, cache);
218219

219220
// Blocks until stopped.
220221
// When stopped, shared_ptrs fall out of scope

src/app/WebHandlers.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,4 +120,34 @@ HealthCheckHandler::operator()(
120120
return web::ng::Response{boost::beast::http::status::ok, kHEALTH_CHECK_HTML, request};
121121
}
122122

123+
web::ng::Response
124+
CacheStateHandler::operator()(
125+
web::ng::Request const& request,
126+
web::ng::ConnectionMetadata&,
127+
web::SubscriptionContextPtr,
128+
boost::asio::yield_context
129+
)
130+
{
131+
static constexpr auto kCACHE_CHECK_LOADED_HTML = R"html(
132+
<!DOCTYPE html>
133+
<html>
134+
<head><title>Cache state</title></head>
135+
<body><h1>Cache state</h1><p>Cache is fully loaded</p></body>
136+
</html>
137+
)html";
138+
139+
static constexpr auto kCACHE_CHECK_NOT_LOADED_HTML = R"html(
140+
<!DOCTYPE html>
141+
<html>
142+
<head><title>Cache state</title></head>
143+
<body><h1>Cache state</h1><p>Cache is not yet loaded</p></body>
144+
</html>
145+
)html";
146+
147+
if (cache_.get().isFull())
148+
return web::ng::Response{boost::beast::http::status::ok, kCACHE_CHECK_LOADED_HTML, request};
149+
150+
return web::ng::Response{boost::beast::http::status::service_unavailable, kCACHE_CHECK_NOT_LOADED_HTML, request};
151+
}
152+
123153
} // namespace app

src/app/WebHandlers.hpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#pragma once
2121

22+
#include "data/LedgerCacheInterface.hpp"
2223
#include "rpc/Errors.hpp"
2324
#include "util/log/Logger.hpp"
2425
#include "web/AdminVerificationStrategy.hpp"
@@ -163,6 +164,37 @@ class HealthCheckHandler {
163164
);
164165
};
165166

167+
/**
168+
* @brief A function object that handles the cache state check endpoint.
169+
*/
170+
class CacheStateHandler {
171+
std::reference_wrapper<data::LedgerCacheInterface const> cache_;
172+
173+
public:
174+
/**
175+
* @brief Construct a new CacheStateHandler object.
176+
*
177+
* @param cache The ledger cache to use.
178+
*/
179+
CacheStateHandler(data::LedgerCacheInterface const& cache) : cache_{cache}
180+
{
181+
}
182+
183+
/**
184+
* @brief The call of the function object.
185+
*
186+
* @param request The request to handle.
187+
* @return The response to the request
188+
*/
189+
web::ng::Response
190+
operator()(
191+
web::ng::Request const& request,
192+
web::ng::ConnectionMetadata&,
193+
web::SubscriptionContextPtr,
194+
boost::asio::yield_context
195+
);
196+
};
197+
166198
/**
167199
* @brief A function object that handles the websocket endpoint.
168200
*

src/web/HttpSession.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#pragma once
2121

22+
#include "data/LedgerCacheInterface.hpp"
2223
#include "util/Taggable.hpp"
2324
#include "web/AdminVerificationStrategy.hpp"
2425
#include "web/PlainWsSession.hpp"
@@ -69,6 +70,7 @@ class HttpSession : public impl::HttpBase<HttpSession, HandlerType>,
6970
* @param tagFactory A factory that is used to generate tags to track requests and sessions
7071
* @param dosGuard The denial of service guard to use
7172
* @param handler The server handler to use
73+
* @param cache The ledger cache to use
7274
* @param buffer Buffer with initial data received from the peer
7375
* @param maxWsSendingQueueSize The maximum size of the sending queue for websocket
7476
*/
@@ -80,6 +82,7 @@ class HttpSession : public impl::HttpBase<HttpSession, HandlerType>,
8082
std::reference_wrapper<util::TagDecoratorFactory const> tagFactory,
8183
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard,
8284
std::shared_ptr<HandlerType> const& handler,
85+
std::reference_wrapper<data::LedgerCacheInterface const> cache,
8386
boost::beast::flat_buffer buffer,
8487
std::uint32_t maxWsSendingQueueSize
8588
)
@@ -90,6 +93,7 @@ class HttpSession : public impl::HttpBase<HttpSession, HandlerType>,
9093
std::move(proxyIpResolver),
9194
dosGuard,
9295
handler,
96+
cache,
9397
std::move(buffer)
9498
)
9599
, stream_(std::move(socket))

src/web/Server.hpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#pragma once
2121

22+
#include "data/LedgerCacheInterface.hpp"
2223
#include "util/Taggable.hpp"
2324
#include "util/log/Logger.hpp"
2425
#include "web/AdminVerificationStrategy.hpp"
@@ -85,6 +86,7 @@ class Detector : public std::enable_shared_from_this<Detector<PlainSessionType,
8586
std::reference_wrapper<util::TagDecoratorFactory const> tagFactory_;
8687
std::reference_wrapper<dosguard::DOSGuardInterface> const dosGuard_;
8788
std::shared_ptr<HandlerType> const handler_;
89+
std::reference_wrapper<data::LedgerCacheInterface const> cache_;
8890
boost::beast::flat_buffer buffer_;
8991
std::shared_ptr<AdminVerificationStrategy> const adminVerification_;
9092
std::uint32_t maxWsSendingQueueSize_;
@@ -99,6 +101,7 @@ class Detector : public std::enable_shared_from_this<Detector<PlainSessionType,
99101
* @param tagFactory A factory that is used to generate tags to track requests and sessions
100102
* @param dosGuard The denial of service guard to use
101103
* @param handler The server handler to use
104+
* @param cache The ledger cache to use
102105
* @param adminVerification The admin verification strategy to use
103106
* @param maxWsSendingQueueSize The maximum size of the sending queue for websocket
104107
* @param proxyIpResolver The client ip resolver if a request was forwarded by a proxy
@@ -109,6 +112,7 @@ class Detector : public std::enable_shared_from_this<Detector<PlainSessionType,
109112
std::reference_wrapper<util::TagDecoratorFactory const> tagFactory,
110113
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard,
111114
std::shared_ptr<HandlerType> handler,
115+
std::reference_wrapper<data::LedgerCacheInterface const> cache,
112116
std::shared_ptr<AdminVerificationStrategy> adminVerification,
113117
std::uint32_t maxWsSendingQueueSize,
114118
std::shared_ptr<ProxyIpResolver> proxyIpResolver
@@ -118,6 +122,7 @@ class Detector : public std::enable_shared_from_this<Detector<PlainSessionType,
118122
, tagFactory_(std::cref(tagFactory))
119123
, dosGuard_(dosGuard)
120124
, handler_(std::move(handler))
125+
, cache_(cache)
121126
, adminVerification_(std::move(adminVerification))
122127
, maxWsSendingQueueSize_(maxWsSendingQueueSize)
123128
, proxyIpResolver_(std::move(proxyIpResolver))
@@ -179,6 +184,7 @@ class Detector : public std::enable_shared_from_this<Detector<PlainSessionType,
179184
tagFactory_,
180185
dosGuard_,
181186
handler_,
187+
cache_,
182188
std::move(buffer_),
183189
maxWsSendingQueueSize_
184190
)
@@ -194,6 +200,7 @@ class Detector : public std::enable_shared_from_this<Detector<PlainSessionType,
194200
tagFactory_,
195201
dosGuard_,
196202
handler_,
203+
cache_,
197204
std::move(buffer_),
198205
maxWsSendingQueueSize_
199206
)
@@ -223,6 +230,7 @@ class Server : public std::enable_shared_from_this<Server<PlainSessionType, SslS
223230
util::TagDecoratorFactory tagFactory_;
224231
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard_;
225232
std::shared_ptr<HandlerType> handler_;
233+
std::reference_wrapper<data::LedgerCacheInterface const> cache_;
226234
tcp::acceptor acceptor_;
227235
std::shared_ptr<AdminVerificationStrategy> adminVerification_;
228236
std::uint32_t maxWsSendingQueueSize_;
@@ -238,6 +246,7 @@ class Server : public std::enable_shared_from_this<Server<PlainSessionType, SslS
238246
* @param tagFactory A factory that is used to generate tags to track requests and sessions
239247
* @param dosGuard The denial of service guard to use
240248
* @param handler The server handler to use
249+
* @param cache The ledger cache to use
241250
* @param adminVerification The admin verification strategy to use
242251
* @param maxWsSendingQueueSize The maximum size of the sending queue for websocket
243252
* @param proxyIpResolver The client ip resolver if a request was forwarded by a proxy
@@ -249,6 +258,7 @@ class Server : public std::enable_shared_from_this<Server<PlainSessionType, SslS
249258
util::TagDecoratorFactory tagFactory,
250259
dosguard::DOSGuardInterface& dosGuard,
251260
std::shared_ptr<HandlerType> handler,
261+
std::reference_wrapper<data::LedgerCacheInterface const> cache,
252262
std::shared_ptr<AdminVerificationStrategy> adminVerification,
253263
std::uint32_t maxWsSendingQueueSize,
254264
ProxyIpResolver proxyIpResolver
@@ -258,6 +268,7 @@ class Server : public std::enable_shared_from_this<Server<PlainSessionType, SslS
258268
, tagFactory_(tagFactory)
259269
, dosGuard_(std::ref(dosGuard))
260270
, handler_(std::move(handler))
271+
, cache_(cache)
261272
, acceptor_(boost::asio::make_strand(ioc))
262273
, adminVerification_(std::move(adminVerification))
263274
, maxWsSendingQueueSize_(maxWsSendingQueueSize)
@@ -320,6 +331,7 @@ class Server : public std::enable_shared_from_this<Server<PlainSessionType, SslS
320331
std::cref(tagFactory_),
321332
dosGuard_,
322333
handler_,
334+
cache_,
323335
adminVerification_,
324336
maxWsSendingQueueSize_,
325337
proxyIpResolver_
@@ -343,6 +355,7 @@ using HttpServer = Server<HttpSession, SslHttpSession, HandlerType>;
343355
* @param ioc The server will run under this io_context
344356
* @param dosGuard The dos guard to protect the server
345357
* @param handler The handler to process the request
358+
* @param cache The ledger cache to use
346359
* @return The server instance
347360
*/
348361
template <typename HandlerType>
@@ -351,7 +364,8 @@ makeHttpServer(
351364
util::config::ClioConfigDefinition const& config,
352365
boost::asio::io_context& ioc,
353366
dosguard::DOSGuardInterface& dosGuard,
354-
std::shared_ptr<HandlerType> const& handler
367+
std::shared_ptr<HandlerType> const& handler,
368+
std::reference_wrapper<data::LedgerCacheInterface const> cache
355369
)
356370
{
357371
static util::Logger const log{"WebServer"}; // NOLINT(readability-identifier-naming)
@@ -385,6 +399,7 @@ makeHttpServer(
385399
util::TagDecoratorFactory(config),
386400
dosGuard,
387401
handler,
402+
cache,
388403
std::move(expectedAdminVerification).value(),
389404
maxWsSendingQueueSize,
390405
std::move(proxyIpResolver)

src/web/SslHttpSession.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#pragma once
2121

22+
#include "data/LedgerCacheInterface.hpp"
2223
#include "util/Taggable.hpp"
2324
#include "web/AdminVerificationStrategy.hpp"
2425
#include "web/ProxyIpResolver.hpp"
@@ -76,6 +77,7 @@ class SslHttpSession : public impl::HttpBase<SslHttpSession, HandlerType>,
7677
* @param tagFactory A factory that is used to generate tags to track requests and sessions
7778
* @param dosGuard The denial of service guard to use
7879
* @param handler The server handler to use
80+
* @param cache The ledger cache to use
7981
* @param buffer Buffer with initial data received from the peer
8082
* @param maxWsSendingQueueSize The maximum size of the sending queue for websocket
8183
*/
@@ -88,6 +90,7 @@ class SslHttpSession : public impl::HttpBase<SslHttpSession, HandlerType>,
8890
std::reference_wrapper<util::TagDecoratorFactory const> tagFactory,
8991
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard,
9092
std::shared_ptr<HandlerType> const& handler,
93+
std::reference_wrapper<data::LedgerCacheInterface const> cache,
9194
boost::beast::flat_buffer buffer,
9295
std::uint32_t maxWsSendingQueueSize
9396
)
@@ -98,6 +101,7 @@ class SslHttpSession : public impl::HttpBase<SslHttpSession, HandlerType>,
98101
std::move(proxyIpResolver),
99102
dosGuard,
100103
handler,
104+
cache,
101105
std::move(buffer)
102106
)
103107
, stream_(std::move(socket), ctx)

src/web/impl/HttpBase.hpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#pragma once
2121

22+
#include "data/LedgerCacheInterface.hpp"
2223
#include "rpc/Errors.hpp"
2324
#include "util/Assert.hpp"
2425
#include "util/Taggable.hpp"
@@ -71,6 +72,22 @@ static constexpr auto kHEALTH_CHECK_HTML = R"html(
7172
</html>
7273
)html";
7374

75+
static constexpr auto kCACHE_CHECK_LOADED_HTML = R"html(
76+
<!DOCTYPE html>
77+
<html>
78+
<head><title>Cache state</title></head>
79+
<body><h1>Cache state</h1><p>Cache is fully loaded</p></body>
80+
</html>
81+
)html";
82+
83+
static constexpr auto kCACHE_CHECK_NOT_LOADED_HTML = R"html(
84+
<!DOCTYPE html>
85+
<html>
86+
<head><title>Cache state</title></head>
87+
<body><h1>Cache state</h1><p>Cache is not yet loaded</p></body>
88+
</html>
89+
)html";
90+
7491
using tcp = boost::asio::ip::tcp;
7592

7693
/**
@@ -128,6 +145,7 @@ class HttpBase : public ConnectionBase {
128145
http::request<http::string_body> req_;
129146
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard_;
130147
std::shared_ptr<HandlerType> const handler_;
148+
std::reference_wrapper<data::LedgerCacheInterface const> cache_;
131149
util::Logger log_{"WebServer"};
132150
util::Logger perfLog_{"Performance"};
133151

@@ -169,6 +187,7 @@ class HttpBase : public ConnectionBase {
169187
std::shared_ptr<ProxyIpResolver> proxyIpResolver,
170188
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard,
171189
std::shared_ptr<HandlerType> handler,
190+
std::reference_wrapper<data::LedgerCacheInterface const> cache,
172191
boost::beast::flat_buffer buffer
173192
)
174193
: ConnectionBase(tagFactory, ip)
@@ -178,6 +197,7 @@ class HttpBase : public ConnectionBase {
178197
, buffer_(std::move(buffer))
179198
, dosGuard_(dosGuard)
180199
, handler_(std::move(handler))
200+
, cache_(cache)
181201
{
182202
LOG(perfLog_.debug()) << tag() << "http session created";
183203
dosGuard_.get().increment(ip);
@@ -222,6 +242,13 @@ class HttpBase : public ConnectionBase {
222242
if (req_.method() == http::verb::get and req_.target() == "/health")
223243
return sender_(httpResponse(http::status::ok, "text/html", kHEALTH_CHECK_HTML));
224244

245+
if (req_.method() == http::verb::get and req_.target() == "/cache_state") {
246+
if (cache_.get().isFull())
247+
return sender_(httpResponse(http::status::ok, "text/html", kCACHE_CHECK_LOADED_HTML));
248+
249+
return sender_(httpResponse(http::status::service_unavailable, "text/html", kCACHE_CHECK_NOT_LOADED_HTML));
250+
}
251+
225252
if (auto resolvedIp = proxyIpResolver_->resolveClientIp(clientIp_, req_); resolvedIp != clientIp_) {
226253
LOG(log_.info()) << tag() << "Detected a forwarded request from proxy. Proxy ip: " << clientIp_
227254
<< ". Resolved client ip: " << resolvedIp;

tests/unit/app/WebHandlersTests.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "app/WebHandlers.hpp"
2121
#include "rpc/Errors.hpp"
2222
#include "util/AsioContextTestFixture.hpp"
23+
#include "util/MockLedgerCache.hpp"
2324
#include "util/MockPrometheus.hpp"
2425
#include "util/Taggable.hpp"
2526
#include "util/config/ConfigDefinition.hpp"
@@ -149,6 +150,32 @@ TEST_F(HealthCheckHandlerTests, Call)
149150
});
150151
}
151152

153+
struct CacheStateHandlerTests : SyncAsioContextTest, WebHandlersTest {
154+
web::ng::Request request{http::request<http::string_body>{http::verb::get, "/", 11}};
155+
MockLedgerCache cache;
156+
CacheStateHandler cacheStateHandler{cache};
157+
};
158+
159+
TEST_F(CacheStateHandlerTests, CallWithCacheLoaded)
160+
{
161+
EXPECT_CALL(cache, isFull()).WillRepeatedly(testing::Return(true));
162+
runSpawn([&](boost::asio::yield_context yield) {
163+
auto response = cacheStateHandler(request, connectionMock, nullptr, yield);
164+
auto const httpResponse = std::move(response).intoHttpResponse();
165+
EXPECT_EQ(httpResponse.result(), boost::beast::http::status::ok);
166+
});
167+
}
168+
169+
TEST_F(CacheStateHandlerTests, CallWithoutCacheLoaded)
170+
{
171+
EXPECT_CALL(cache, isFull()).WillRepeatedly(testing::Return(false));
172+
runSpawn([&](boost::asio::yield_context yield) {
173+
auto response = cacheStateHandler(request, connectionMock, nullptr, yield);
174+
auto const httpResponse = std::move(response).intoHttpResponse();
175+
EXPECT_EQ(httpResponse.result(), boost::beast::http::status::service_unavailable);
176+
});
177+
}
178+
152179
struct RequestHandlerTest : SyncAsioContextTest, WebHandlersTest {
153180
AdminVerificationStrategyStrictMockPtr adminVerifier{
154181
std::make_shared<testing::StrictMock<AdminVerificationStrategyMock>>()

0 commit comments

Comments
 (0)