mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-14 16:55:51 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4468302852 | ||
|
|
a704cf7cfe | ||
|
|
05d09cc352 | ||
|
|
ae96ac7baf | ||
|
|
4579fa2f26 |
@@ -45,7 +45,6 @@ target_sources(clio PRIVATE
|
|||||||
## Backend
|
## Backend
|
||||||
src/backend/BackendInterface.cpp
|
src/backend/BackendInterface.cpp
|
||||||
src/backend/CassandraBackend.cpp
|
src/backend/CassandraBackend.cpp
|
||||||
src/backend/LayeredCache.cpp
|
|
||||||
src/backend/Pg.cpp
|
src/backend/Pg.cpp
|
||||||
src/backend/PostgresBackend.cpp
|
src/backend/PostgresBackend.cpp
|
||||||
src/backend/SimpleCache.cpp
|
src/backend/SimpleCache.cpp
|
||||||
|
|||||||
@@ -1,9 +1,3 @@
|
|||||||
[](https://github.com/legleux/clio/actions/workflows/build.yml)
|
|
||||||
|
|
||||||
|
|
||||||
**Status:** This software is in beta mode. We encourage anyone to try it out and
|
|
||||||
report any issues they discover. Version 1.0 coming soon.
|
|
||||||
|
|
||||||
# Clio
|
# Clio
|
||||||
Clio is an XRP Ledger API server. Clio is optimized for RPC calls, over WebSocket or JSON-RPC. Validated
|
Clio is an XRP Ledger API server. Clio is optimized for RPC calls, over WebSocket or JSON-RPC. Validated
|
||||||
historical ledger and transaction data are stored in a more space-efficient format,
|
historical ledger and transaction data are stored in a more space-efficient format,
|
||||||
@@ -167,4 +161,4 @@ deleted to free up space.
|
|||||||
rotate the current log file.
|
rotate the current log file.
|
||||||
|
|
||||||
Note, time-based log rotation occurs dependently on size-based log rotation, where if a
|
Note, time-based log rotation occurs dependently on size-based log rotation, where if a
|
||||||
size-based log rotation occurs, the timer for the time-based rotation will reset.
|
size-based log rotation occurs, the timer for the time-based rotation will reset.
|
||||||
|
|||||||
@@ -1,110 +0,0 @@
|
|||||||
#include <backend/LayeredCache.h>
|
|
||||||
namespace Backend {
|
|
||||||
|
|
||||||
void
|
|
||||||
LayeredCache::insert(
|
|
||||||
ripple::uint256 const& key,
|
|
||||||
Blob const& value,
|
|
||||||
uint32_t seq)
|
|
||||||
{
|
|
||||||
auto entry = map_[key];
|
|
||||||
// stale insert, do nothing
|
|
||||||
if (seq <= entry.recent.seq)
|
|
||||||
return;
|
|
||||||
entry.old = entry.recent;
|
|
||||||
entry.recent = {seq, value};
|
|
||||||
if (value.empty())
|
|
||||||
pendingDeletes_.push_back(key);
|
|
||||||
if (!entry.old.blob.empty())
|
|
||||||
pendingSweeps_.push_back(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<Blob>
|
|
||||||
LayeredCache::select(CacheEntry const& entry, uint32_t seq) const
|
|
||||||
{
|
|
||||||
if (seq < entry.old.seq)
|
|
||||||
return {};
|
|
||||||
if (seq < entry.recent.seq && !entry.old.blob.empty())
|
|
||||||
return entry.old.blob;
|
|
||||||
if (!entry.recent.blob.empty())
|
|
||||||
return entry.recent.blob;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
void
|
|
||||||
LayeredCache::update(std::vector<LedgerObject> const& blobs, uint32_t seq)
|
|
||||||
{
|
|
||||||
std::unique_lock lck{mtx_};
|
|
||||||
if (seq > mostRecentSequence_)
|
|
||||||
mostRecentSequence_ = seq;
|
|
||||||
for (auto const& k : pendingSweeps_)
|
|
||||||
{
|
|
||||||
auto e = map_[k];
|
|
||||||
e.old = {};
|
|
||||||
}
|
|
||||||
for (auto const& k : pendingDeletes_)
|
|
||||||
{
|
|
||||||
map_.erase(k);
|
|
||||||
}
|
|
||||||
for (auto const& b : blobs)
|
|
||||||
{
|
|
||||||
insert(b.key, b.blob, seq);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::optional<LedgerObject>
|
|
||||||
LayeredCache::getSuccessor(ripple::uint256 const& key, uint32_t seq) const
|
|
||||||
{
|
|
||||||
ripple::uint256 curKey = key;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
std::shared_lock lck{mtx_};
|
|
||||||
if (seq < mostRecentSequence_ - 1)
|
|
||||||
return {};
|
|
||||||
auto e = map_.upper_bound(curKey);
|
|
||||||
if (e == map_.end())
|
|
||||||
return {};
|
|
||||||
auto const& entry = e->second;
|
|
||||||
auto blob = select(entry, seq);
|
|
||||||
if (!blob)
|
|
||||||
{
|
|
||||||
curKey = e->first;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return {{e->first, *blob}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::optional<LedgerObject>
|
|
||||||
LayeredCache::getPredecessor(ripple::uint256 const& key, uint32_t seq) const
|
|
||||||
{
|
|
||||||
ripple::uint256 curKey = key;
|
|
||||||
std::shared_lock lck{mtx_};
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
if (seq < mostRecentSequence_ - 1)
|
|
||||||
return {};
|
|
||||||
auto e = map_.lower_bound(curKey);
|
|
||||||
--e;
|
|
||||||
if (e == map_.begin())
|
|
||||||
return {};
|
|
||||||
auto const& entry = e->second;
|
|
||||||
auto blob = select(entry, seq);
|
|
||||||
if (!blob)
|
|
||||||
{
|
|
||||||
curKey = e->first;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return {{e->first, *blob}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::optional<Blob>
|
|
||||||
LayeredCache::get(ripple::uint256 const& key, uint32_t seq) const
|
|
||||||
{
|
|
||||||
std::shared_lock lck{mtx_};
|
|
||||||
auto e = map_.find(key);
|
|
||||||
if (e == map_.end())
|
|
||||||
return {};
|
|
||||||
auto const& entry = e->second;
|
|
||||||
return select(entry, seq);
|
|
||||||
}
|
|
||||||
} // namespace Backend
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
#ifndef CLIO_LAYEREDCACHE_H_INCLUDED
|
|
||||||
#define CLIO_LAYEREDCACHE_H_INCLUDED
|
|
||||||
|
|
||||||
#include <ripple/basics/base_uint.h>
|
|
||||||
#include <backend/Types.h>
|
|
||||||
#include <map>
|
|
||||||
#include <mutex>
|
|
||||||
#include <shared_mutex>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
namespace Backend {
|
|
||||||
class LayeredCache
|
|
||||||
{
|
|
||||||
struct SeqBlobPair
|
|
||||||
{
|
|
||||||
uint32_t seq;
|
|
||||||
Blob blob;
|
|
||||||
};
|
|
||||||
struct CacheEntry
|
|
||||||
{
|
|
||||||
SeqBlobPair recent;
|
|
||||||
SeqBlobPair old;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::map<ripple::uint256, CacheEntry> map_;
|
|
||||||
std::vector<ripple::uint256> pendingDeletes_;
|
|
||||||
std::vector<ripple::uint256> pendingSweeps_;
|
|
||||||
mutable std::shared_mutex mtx_;
|
|
||||||
uint32_t mostRecentSequence_;
|
|
||||||
|
|
||||||
void
|
|
||||||
insert(ripple::uint256 const& key, Blob const& value, uint32_t seq);
|
|
||||||
|
|
||||||
/*
|
|
||||||
void
|
|
||||||
insert(ripple::uint256 const& key, Blob const& value, uint32_t seq)
|
|
||||||
{
|
|
||||||
map_.emplace(key,{{seq,value,{}});
|
|
||||||
}
|
|
||||||
void
|
|
||||||
update(ripple::uint256 const& key, Blob const& value, uint32_t seq)
|
|
||||||
{
|
|
||||||
auto& entry = map_.find(key);
|
|
||||||
entry.old = entry.recent;
|
|
||||||
entry.recent = {seq, value};
|
|
||||||
pendingSweeps_.push_back(key);
|
|
||||||
}
|
|
||||||
void
|
|
||||||
erase(ripple::uint256 const& key, uint32_t seq)
|
|
||||||
{
|
|
||||||
update(key, {}, seq);
|
|
||||||
pendingDeletes_.push_back(key);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
std::optional<Blob>
|
|
||||||
select(CacheEntry const& entry, uint32_t seq) const;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void
|
|
||||||
update(std::vector<LedgerObject> const& blobs, uint32_t seq);
|
|
||||||
|
|
||||||
std::optional<Blob>
|
|
||||||
get(ripple::uint256 const& key, uint32_t seq) const;
|
|
||||||
|
|
||||||
std::optional<LedgerObject>
|
|
||||||
getSuccessor(ripple::uint256 const& key, uint32_t seq) const;
|
|
||||||
|
|
||||||
std::optional<LedgerObject>
|
|
||||||
getPredecessor(ripple::uint256 const& key, uint32_t seq) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Backend
|
|
||||||
#endif
|
|
||||||
@@ -1032,7 +1032,7 @@ ETLLoadBalancer::fetchLedger(
|
|||||||
auto [status, data] = source->fetchLedger(
|
auto [status, data] = source->fetchLedger(
|
||||||
ledgerSequence, getObjects, getObjectNeighbors);
|
ledgerSequence, getObjects, getObjectNeighbors);
|
||||||
response = std::move(data);
|
response = std::move(data);
|
||||||
if (status.ok() && (response.validated() || true))
|
if (status.ok() && response.validated())
|
||||||
{
|
{
|
||||||
BOOST_LOG_TRIVIAL(info)
|
BOOST_LOG_TRIVIAL(info)
|
||||||
<< "Successfully fetched ledger = " << ledgerSequence
|
<< "Successfully fetched ledger = " << ledgerSequence
|
||||||
|
|||||||
@@ -147,11 +147,9 @@ ReportingETL::publishLedger(ripple::LedgerInfo const& lgrInfo)
|
|||||||
backend_->cache().update(diff, lgrInfo.seq);
|
backend_->cache().update(diff, lgrInfo.seq);
|
||||||
backend_->updateRange(lgrInfo.seq);
|
backend_->updateRange(lgrInfo.seq);
|
||||||
}
|
}
|
||||||
auto now = std::chrono::duration_cast<std::chrono::seconds>(
|
|
||||||
std::chrono::system_clock::now().time_since_epoch())
|
setLastClose(lgrInfo.closeTime);
|
||||||
.count();
|
auto age = lastCloseAgeSeconds();
|
||||||
auto closeTime = lgrInfo.closeTime.time_since_epoch().count();
|
|
||||||
auto age = now - (rippleEpochStart + closeTime);
|
|
||||||
// if the ledger closed over 10 minutes ago, assume we are still
|
// if the ledger closed over 10 minutes ago, assume we are still
|
||||||
// catching up and don't publish
|
// catching up and don't publish
|
||||||
if (age < 600)
|
if (age < 600)
|
||||||
|
|||||||
@@ -139,6 +139,18 @@ private:
|
|||||||
lastPublish_ = std::chrono::system_clock::now();
|
lastPublish_ = std::chrono::system_clock::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The time that the most recently published ledger was closed.
|
||||||
|
std::chrono::time_point<ripple::NetClock> lastCloseTime_;
|
||||||
|
|
||||||
|
mutable std::shared_mutex closeTimeMtx_;
|
||||||
|
|
||||||
|
void
|
||||||
|
setLastClose(std::chrono::time_point<ripple::NetClock> lastCloseTime)
|
||||||
|
{
|
||||||
|
std::unique_lock lck(closeTimeMtx_);
|
||||||
|
lastCloseTime_ = lastCloseTime;
|
||||||
|
}
|
||||||
|
|
||||||
/// Download a ledger with specified sequence in full, via GetLedgerData,
|
/// Download a ledger with specified sequence in full, via GetLedgerData,
|
||||||
/// and write the data to the databases. This takes several minutes or
|
/// and write the data to the databases. This takes several minutes or
|
||||||
/// longer.
|
/// longer.
|
||||||
@@ -334,6 +346,17 @@ public:
|
|||||||
std::chrono::system_clock::now() - getLastPublish())
|
std::chrono::system_clock::now() - getLastPublish())
|
||||||
.count();
|
.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::uint32_t
|
||||||
|
lastCloseAgeSeconds() const
|
||||||
|
{
|
||||||
|
std::shared_lock lck(closeTimeMtx_);
|
||||||
|
auto now = std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
|
std::chrono::system_clock::now().time_since_epoch())
|
||||||
|
.count();
|
||||||
|
auto closeTime = lastCloseTime_.time_since_epoch().count();
|
||||||
|
return now - (rippleEpochStart + closeTime);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace Build {
|
|||||||
// and follow the format described at http://semver.org/
|
// and follow the format described at http://semver.org/
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// clang-format off
|
// clang-format off
|
||||||
char const* const versionString = "1.0.0"
|
char const* const versionString = "1.0.1"
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
#if defined(DEBUG) || defined(SANITIZER)
|
#if defined(DEBUG) || defined(SANITIZER)
|
||||||
|
|||||||
@@ -422,8 +422,8 @@ handle_request(
|
|||||||
"This is a clio server. clio only serves validated data. If you "
|
"This is a clio server. clio only serves validated data. If you "
|
||||||
"want to talk to rippled, include 'ledger_index':'current' in your "
|
"want to talk to rippled, include 'ledger_index':'current' in your "
|
||||||
"request");
|
"request");
|
||||||
auto lastPublishAge = context->etl->lastPublishAgeSeconds();
|
auto lastCloseAge = context->etl->lastCloseAgeSeconds();
|
||||||
if (lastPublishAge >= 60)
|
if (lastCloseAge >= 60)
|
||||||
warnings.emplace_back("This server may be out of date");
|
warnings.emplace_back("This server may be out of date");
|
||||||
result["warnings"] = warnings;
|
result["warnings"] = warnings;
|
||||||
responseStr = boost::json::serialize(response);
|
responseStr = boost::json::serialize(response);
|
||||||
|
|||||||
@@ -351,10 +351,9 @@ public:
|
|||||||
"want to talk to rippled, include 'ledger_index':'current' in your "
|
"want to talk to rippled, include 'ledger_index':'current' in your "
|
||||||
"request");
|
"request");
|
||||||
|
|
||||||
auto lastPublishAge = etl_->lastPublishAgeSeconds();
|
auto lastCloseAge = etl_->lastCloseAgeSeconds();
|
||||||
if (lastPublishAge >= 60)
|
if (lastCloseAge >= 60)
|
||||||
warnings.emplace_back("This server may be out of date");
|
warnings.emplace_back("This server may be out of date");
|
||||||
|
|
||||||
response["warnings"] = warnings;
|
response["warnings"] = warnings;
|
||||||
std::string responseStr = boost::json::serialize(response);
|
std::string responseStr = boost::json::serialize(response);
|
||||||
if (!dosGuard_.add(*ip, responseStr.size()))
|
if (!dosGuard_.add(*ip, responseStr.size()))
|
||||||
|
|||||||
Reference in New Issue
Block a user