xrpld
Loading...
Searching...
No Matches
PathRequestManager.cpp
1#include <xrpld/rpc/detail/PathRequestManager.h>
2
3#include <xrpld/app/ledger/LedgerMaster.h>
4#include <xrpld/app/main/Application.h>
5#include <xrpld/rpc/detail/AssetCache.h>
6#include <xrpld/rpc/detail/PathRequest.h>
7
8#include <xrpl/basics/Log.h>
9#include <xrpl/core/Job.h>
10#include <xrpl/core/JobQueue.h>
11#include <xrpl/json/json_value.h>
12#include <xrpl/ledger/ReadView.h>
13#include <xrpl/protocol/ErrorCodes.h>
14#include <xrpl/protocol/RPCErr.h>
15#include <xrpl/protocol/jss.h>
16#include <xrpl/resource/Consumer.h>
17#include <xrpl/server/InfoSub.h>
18
19#include <algorithm>
20#include <cstdint>
21#include <functional>
22#include <memory>
23#include <mutex>
24#include <utility>
25#include <vector>
26
27namespace xrpl {
28
33std::shared_ptr<AssetCache>
35{
36 std::scoped_lock const sl(lock_);
37
38 auto assetCache = assetCache_.lock();
39
40 std::uint32_t const lineSeq = assetCache ? assetCache->getLedger()->seq() : 0;
41 std::uint32_t const lgrSeq = ledger->seq();
42 JLOG(journal_.debug()) << "getLineCache has cache for " << lineSeq << ", considering "
43 << lgrSeq;
44
45 if ((lineSeq == 0) || // no ledger
46 (authoritative && (lgrSeq > lineSeq)) || // newer authoritative ledger
47 (authoritative && ((lgrSeq + 8) < lineSeq)) || // we jumped way back for some reason
48 (lgrSeq > (lineSeq + 8))) // we jumped way forward for some reason
49 {
50 JLOG(journal_.debug()) << "getLineCache creating new cache for " << lgrSeq;
51 // Assign to the local before the member, because the member is a
52 // weak_ptr, and will immediately discard it if there are no other
53 // references.
54 assetCache_ = assetCache =
55 std::make_shared<AssetCache>(ledger, app_.getJournal("AssetCache"));
56 }
57 return assetCache;
58}
59
60void
62{
63 auto event = app_.getJobQueue().makeLoadEvent(JtPathFind, "PathRequest::updateAll");
64
67
68 // Get the ledger and cache we should be using
69 {
70 std::scoped_lock const sl(lock_);
71 requests = requests_;
72 cache = getAssetCache(inLedger, true);
73 }
74
75 bool newRequests = app_.getLedgerMaster().isNewPathRequest();
76 bool mustBreak = false;
77
78 JLOG(journal_.trace()) << "updateAll seq=" << cache->getLedger()->seq() << ", "
79 << requests.size() << " requests";
80
81 int processed = 0, removed = 0;
82
83 auto getSubscriber = [](PathRequest::pointer const& request) -> InfoSub::pointer {
84 if (auto ipSub = request->getSubscriber(); ipSub && ipSub->getRequest() == request)
85 {
86 return ipSub;
87 }
88 request->doAborting();
89 return nullptr;
90 };
91
92 do
93 {
94 JLOG(journal_.trace()) << "updateAll looping";
95 for (auto const& wr : requests)
96 {
97 if (app_.getJobQueue().isStopping())
98 break;
99
100 auto request = wr.lock();
101 bool remove = true;
102 JLOG(journal_.trace()) << "updateAll request " << (request ? "" : "not ") << "found";
103
104 if (request)
105 {
106 auto continueCallback = [&getSubscriber, &request]() {
107 // This callback is used by doUpdate to determine whether to
108 // continue working. If getSubscriber returns null, that
109 // indicates that this request is no longer relevant.
110 return (bool)getSubscriber(request);
111 };
112 if (!request->needsUpdate(newRequests, cache->getLedger()->seq()))
113 {
114 remove = false;
115 }
116 else
117 {
118 if (auto ipSub = getSubscriber(request))
119 {
120 if (!ipSub->getConsumer().warn())
121 {
122 // Release the shared ptr to the subscriber so that
123 // it can be freed if the client disconnects, and
124 // thus fail to lock later.
125 ipSub.reset();
126 json::Value update = request->doUpdate(cache, false, continueCallback);
127 request->updateComplete();
128 update[jss::type] = "path_find";
129 ipSub = getSubscriber(request);
130 if (ipSub)
131 {
132 ipSub->send(update, false);
133 remove = false;
134 ++processed;
135 }
136 }
137 }
138 else if (request->hasCompletion())
139 {
140 // One-shot request with completion function
141 request->doUpdate(cache, false);
142 request->updateComplete();
143 ++processed;
144 }
145 }
146 }
147
148 if (remove)
149 {
150 std::scoped_lock const sl(lock_);
151
152 // Remove any dangling weak pointers or weak
153 // pointers that refer to this path request.
154 auto ret = std::ranges::remove_if(requests_, [&removed, &request](auto const& wl) {
155 auto r = wl.lock();
156
157 if (r && r != request)
158 return false;
159 ++removed;
160 return true;
161 });
162
163 requests_.erase(ret.begin(), ret.end());
164 }
165
166 mustBreak = !newRequests && app_.getLedgerMaster().isNewPathRequest();
167
168 // We weren't handling new requests and then
169 // there was a new request
170 if (mustBreak)
171 break;
172 }
173
174 if (mustBreak)
175 { // a new request came in while we were working
176 newRequests = true;
177 }
178 else if (newRequests)
179 { // we only did new requests, so we always need a last pass
180 newRequests = app_.getLedgerMaster().isNewPathRequest();
181 }
182 else
183 { // if there are no new requests, we are done
184 newRequests = app_.getLedgerMaster().isNewPathRequest();
185 if (!newRequests)
186 break;
187 }
188
189 // Hold on to the line cache until after the lock is released, so it can
190 // be destroyed outside of the lock
192 {
193 // Get the latest requests, cache, and ledger for next pass
194 std::scoped_lock const sl(lock_);
195
196 if (requests_.empty())
197 break;
198 requests = requests_;
199 lastCache = cache;
200 cache = getAssetCache(cache->getLedger(), false);
201 }
202 } while (!app_.getJobQueue().isStopping());
203
204 JLOG(journal_.debug()) << "updateAll complete: " << processed << " processed and " << removed
205 << " removed";
206}
207
208bool
210{
211 std::scoped_lock const sl(lock_);
212 return !requests_.empty();
213}
214
215void
217{
218 std::scoped_lock const sl(lock_);
219
220 // Insert after any older unserviced requests but before
221 // any serviced requests
222 auto ret = std::ranges::find_if(requests_, [](auto const& wl) {
223 auto r = wl.lock();
224
225 // We come before handled requests
226 return r && !r->isNew();
227 });
228
229 requests_.emplace(ret, req);
230}
231
232// Make a new-style path_find request
235 std::shared_ptr<InfoSub> const& subscriber,
236 std::shared_ptr<ReadView const> const& inLedger,
237 json::Value const& requestJson)
238{
239 auto req = std::make_shared<PathRequest>(app_, subscriber, ++lastIdentifier_, *this, journal_);
240
241 auto [valid, jvRes] = req->doCreate(getAssetCache(inLedger, false), requestJson);
242
243 if (valid)
244 {
245 subscriber->setRequest(req);
247 app_.getLedgerMaster().newPathRequest();
248 }
249 return std::move(jvRes);
250}
251
252// Make an old-style ripple_path_find request
256 std::function<void(void)> completion,
257 resource::Consumer& consumer,
258 std::shared_ptr<ReadView const> const& inLedger,
259 json::Value const& request)
260{
261 // This assignment must take place before the
262 // completion function is called
264 app_, completion, consumer, ++lastIdentifier_, *this, journal_);
265
266 auto [valid, jvRes] = req->doCreate(getAssetCache(inLedger, false), request);
267
268 if (!valid)
269 {
270 req.reset();
271 }
272 else
273 {
275 if (!app_.getLedgerMaster().newPathRequest())
276 {
277 // The newPathRequest failed. Tell the caller.
278 jvRes = rpcError(RpcTooBusy);
279 req.reset();
280 }
281 }
282
283 return std::move(jvRes);
284}
285
288 resource::Consumer& consumer,
289 std::shared_ptr<ReadView const> const& inLedger,
290 json::Value const& request)
291{
292 auto cache = std::make_shared<AssetCache>(inLedger, app_.getJournal("AssetCache"));
293
294 auto req =
295 std::make_shared<PathRequest>(app_, [] {}, consumer, ++lastIdentifier_, *this, journal_);
296
297 auto [valid, jvRes] = req->doCreate(cache, request);
298 if (valid)
299 jvRes = req->doUpdate(cache, false);
300 return std::move(jvRes);
301}
302
303} // namespace xrpl
Represents a JSON value.
Definition json_value.h:117
std::shared_ptr< InfoSub > pointer
Definition InfoSub.h:91
std::atomic< int > lastIdentifier_
std::vector< PathRequest::wptr > requests_
std::shared_ptr< AssetCache > getAssetCache(std::shared_ptr< ReadView const > const &ledger, bool authoritative)
Get the current AssetCache, updating it if necessary.
std::weak_ptr< AssetCache > assetCache_
json::Value makePathRequest(std::shared_ptr< InfoSub > const &subscriber, std::shared_ptr< ReadView const > const &ledger, json::Value const &request)
json::Value makeLegacyPathRequest(PathRequest::pointer &req, std::function< void(void)> completion, resource::Consumer &consumer, std::shared_ptr< ReadView const > const &inLedger, json::Value const &request)
void updateAll(std::shared_ptr< ReadView const > const &ledger)
Update all of the contained PathRequest instances.
void insertPathRequest(PathRequest::pointer const &)
std::recursive_mutex lock_
json::Value doLegacyPathRequest(resource::Consumer &consumer, std::shared_ptr< ReadView const > const &inLedger, json::Value const &request)
std::shared_ptr< PathRequest > pointer
Definition PathRequest.h:48
An endpoint that consumes resources.
Definition Consumer.h:20
T find_if(T... args)
T make_shared(T... args)
TER valid(STTx const &tx, ReadView const &view, AccountID const &src, beast::Journal j)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ RpcTooBusy
Definition ErrorCodes.h:39
json::Value rpcError(ErrorCodeI iError)
Definition RPCErr.cpp:13
@ JtPathFind
Definition Job.h:70
T remove_if(T... args)
T reset(T... args)
T size(T... args)