From 2ce293ec859585832638dfe16358a9fd644e7028 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sat, 13 Jul 2013 15:15:09 -0700 Subject: [PATCH 1/6] Slight refactor of the boundary between the node db and its backend(s). --- .../node/ripple_LevelDBBackendFactory.cpp | 8 -- .../node/ripple_MdbBackendFactory.cpp | 29 ---- modules/ripple_app/node/ripple_NodeStore.cpp | 125 ++++++++++-------- modules/ripple_app/node/ripple_NodeStore.h | 28 ++-- .../node/ripple_SqliteBackendFactory.cpp | 12 -- 5 files changed, 87 insertions(+), 115 deletions(-) diff --git a/modules/ripple_app/node/ripple_LevelDBBackendFactory.cpp b/modules/ripple_app/node/ripple_LevelDBBackendFactory.cpp index ef27c96ec7..b936e7349d 100644 --- a/modules/ripple_app/node/ripple_LevelDBBackendFactory.cpp +++ b/modules/ripple_app/node/ripple_LevelDBBackendFactory.cpp @@ -33,14 +33,6 @@ public: return mName; } - bool store (NodeObject::ref obj) - { - Blob blob (toBlob (obj)); - return mDB->Put (leveldb::WriteOptions (), - leveldb::Slice (reinterpret_cast(obj->getHash ().begin ()), 256 / 8), - leveldb::Slice (reinterpret_cast(&blob.front ()), blob.size ())).ok (); - } - bool bulkStore (const std::vector< NodeObject::pointer >& objs) { leveldb::WriteBatch batch; diff --git a/modules/ripple_app/node/ripple_MdbBackendFactory.cpp b/modules/ripple_app/node/ripple_MdbBackendFactory.cpp index cf6e02be7e..0b74349ab3 100644 --- a/modules/ripple_app/node/ripple_MdbBackendFactory.cpp +++ b/modules/ripple_app/node/ripple_MdbBackendFactory.cpp @@ -61,35 +61,6 @@ public: return m_name; } - bool store (NodeObject::ref obj) - { - MDB_txn *txn = nullptr; - int rc = 0; - - rc = mdb_txn_begin(m_env, NULL, 0, &txn); - - if (rc == 0) - { - MDB_val key, data; - Blob blob (toBlob (obj)); - - key.mv_size = (256 / 8); - key.mv_data = const_cast(obj->getHash().begin()); - - data.mv_size = blob.size(); - data.mv_data = &blob.front(); - - rc = mdb_put(txn, m_dbi, &key, &data, 0); - } - - if (rc == 0) - rc = mdb_txn_commit(txn); - else if (txn) - mdb_txn_abort(txn); - - return rc == 0; - } - bool bulkStore (std::vector const& objs) { MDB_txn *txn = nullptr; diff --git a/modules/ripple_app/node/ripple_NodeStore.cpp b/modules/ripple_app/node/ripple_NodeStore.cpp index 49034e1fdf..960e0d805f 100644 --- a/modules/ripple_app/node/ripple_NodeStore.cpp +++ b/modules/ripple_app/node/ripple_NodeStore.cpp @@ -10,14 +10,9 @@ NodeStore::NodeStore (String backendParameters, String fastBackendParameters, in : m_backend (createBackend (backendParameters)) , mCache ("NodeStore", cacheSize, cacheAge) , mNegativeCache ("HashedObjectNegativeCache", 0, 120) - , mWriteGeneration (0) - , mWriteLoad (0) - , mWritePending (false) { if (fastBackendParameters.isNotEmpty ()) m_fastBackend = createBackend (fastBackendParameters); - - mWriteSet.reserve (128); } void NodeStore::addBackendFactory (BackendFactory& factory) @@ -44,17 +39,14 @@ void NodeStore::sweep () void NodeStore::waitWrite () { - boost::mutex::scoped_lock sl (mWriteMutex); - int gen = mWriteGeneration; - - while (mWritePending && (mWriteGeneration == gen)) - mWriteCondition.wait (sl); + m_backend->waitWrite (); + if (m_fastBackend) + m_fastBackend->waitWrite (); } int NodeStore::getWriteLoad () { - boost::mutex::scoped_lock sl (mWriteMutex); - return std::max (mWriteLoad, static_cast (mWriteSet.size ())); + return m_backend->getWriteLoad (); } bool NodeStore::store (NodeObjectType type, uint32 index, @@ -72,56 +64,15 @@ bool NodeStore::store (NodeObjectType type, uint32 index, if (!mCache.canonicalize (hash, object)) { - boost::mutex::scoped_lock sl (mWriteMutex); - mWriteSet.push_back (object); - - if (!mWritePending) - { - mWritePending = true; - getApp().getJobQueue ().addJob (jtWRITE, "NodeObject::store", - BIND_TYPE (&NodeStore::bulkWrite, this, P_1)); - } + m_backend->store (object); + if (m_fastBackend) + m_fastBackend->store (object); } mNegativeCache.del (hash); return true; } -void NodeStore::bulkWrite (Job&) -{ - int setSize = 0; - - while (1) - { - std::vector< boost::shared_ptr > set; - set.reserve (128); - - { - boost::mutex::scoped_lock sl (mWriteMutex); - - mWriteSet.swap (set); - assert (mWriteSet.empty ()); - ++mWriteGeneration; - mWriteCondition.notify_all (); - - if (set.empty ()) - { - mWritePending = false; - mWriteLoad = 0; - return; - } - - mWriteLoad = std::max (setSize, static_cast (mWriteSet.size ())); - setSize = set.size (); - } - - m_backend->bulkStore (set); - - if (m_fastBackend) - m_fastBackend->bulkStore (set); - } -} - NodeObject::pointer NodeStore::retrieve (uint256 const& hash) { NodeObject::pointer obj = mCache.fetch (hash); @@ -232,3 +183,65 @@ NodeStore::Backend* NodeStore::createBackend (String const& parameters) return backend; } + +bool NodeStore::Backend::store (NodeObject::ref object) +{ + boost::mutex::scoped_lock sl (mWriteMutex); + mWriteSet.push_back (object); + + if (!mWritePending) + { + mWritePending = true; + getApp().getJobQueue ().addJob (jtWRITE, "NodeObject::store", + BIND_TYPE (&NodeStore::Backend::bulkWrite, this, P_1)); + } + return true; +} + +void NodeStore::Backend::bulkWrite (Job &) +{ + int setSize = 0; + + while (1) + { + std::vector< boost::shared_ptr > set; + set.reserve (128); + + { + boost::mutex::scoped_lock sl (mWriteMutex); + + mWriteSet.swap (set); + assert (mWriteSet.empty ()); + ++mWriteGeneration; + mWriteCondition.notify_all (); + + if (set.empty ()) + { + mWritePending = false; + mWriteLoad = 0; + return; + } + + mWriteLoad = std::max (setSize, static_cast (mWriteSet.size ())); + setSize = set.size (); + } + + bulkStore (set); + } +} + +void NodeStore::Backend::waitWrite () +{ + boost::mutex::scoped_lock sl (mWriteMutex); + int gen = mWriteGeneration; + + while (mWritePending && (mWriteGeneration == gen)) + mWriteCondition.wait (sl); +} + +int NodeStore::Backend::getWriteLoad () +{ + boost::mutex::scoped_lock sl (mWriteMutex); + + return std::max (mWriteLoad, static_cast (mWriteSet.size ())); +} diff --git a/modules/ripple_app/node/ripple_NodeStore.h b/modules/ripple_app/node/ripple_NodeStore.h index 78adb51726..564a85d046 100644 --- a/modules/ripple_app/node/ripple_NodeStore.h +++ b/modules/ripple_app/node/ripple_NodeStore.h @@ -23,13 +23,18 @@ public: // typedef boost::shared_ptr pointer; + Backend () : mWriteGeneration(0), mWriteLoad(0), mWritePending(false) + { + mWriteSet.reserve(128); + } + virtual ~Backend () { } virtual std::string getDataBaseName() = 0; // Store/retrieve a single object // These functions must be thread safe - virtual bool store (NodeObject::ref) = 0; + virtual bool store (NodeObject::ref); virtual NodeObject::pointer retrieve (uint256 const &hash) = 0; // Store a group of objects @@ -39,6 +44,18 @@ public: // Visit every object in the database // This function will only be called during an import operation virtual void visitAll (FUNCTION_TYPE ) = 0; + + virtual void bulkWrite (Job &); + virtual void waitWrite (); + virtual int getWriteLoad (); + + protected: + boost::mutex mWriteMutex; + boost::condition_variable mWriteCondition; + int mWriteGeneration; + int mWriteLoad; + bool mWritePending; + std::vector > mWriteSet; }; public: @@ -90,7 +107,6 @@ public: NodeObject::pointer retrieve (uint256 const& hash); - void bulkWrite (Job&); void waitWrite (); void tune (int size, int age); void sweep (); @@ -111,14 +127,6 @@ private: TaggedCache mCache; KeyCache mNegativeCache; - - boost::mutex mWriteMutex; - boost::condition_variable mWriteCondition; - int mWriteGeneration; - int mWriteLoad; - - std::vector< boost::shared_ptr > mWriteSet; - bool mWritePending; }; #endif diff --git a/modules/ripple_app/node/ripple_SqliteBackendFactory.cpp b/modules/ripple_app/node/ripple_SqliteBackendFactory.cpp index f1e74d3bba..0b421ac5be 100644 --- a/modules/ripple_app/node/ripple_SqliteBackendFactory.cpp +++ b/modules/ripple_app/node/ripple_SqliteBackendFactory.cpp @@ -24,18 +24,6 @@ public: return mName; } - bool store(NodeObject::ref object) - { - ScopedLock sl(mDb->getDBLock()); - static SqliteStatement pSt(mDb->getDB()->getSqliteDB(), - "INSERT OR IGNORE INTO CommittedObjects " - "(Hash,ObjType,LedgerIndex,Object) VALUES (?, ?, ?, ?);"); - bind(pSt, object); - pSt.step(); - pSt.reset(); - return true; - } - bool bulkStore(const std::vector< NodeObject::pointer >& objects) { ScopedLock sl(mDb->getDBLock()); From 5b2d5e84280249fd380142d624667dd1fe9893b7 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sat, 13 Jul 2013 19:02:35 -0700 Subject: [PATCH 2/6] Add a way for us to place jobs with a concurrency limit. The main use case is having all threads stuck in ledgerData, fighting each other. --- modules/ripple_core/functional/ripple_Job.cpp | 9 +++ modules/ripple_core/functional/ripple_Job.h | 4 + .../functional/ripple_JobQueue.cpp | 73 ++++++++++++++----- .../ripple_core/functional/ripple_JobQueue.h | 3 + src/cpp/ripple/ripple_Peer.cpp | 4 +- src/cpp/ripple/ripple_PeerSet.cpp | 3 +- 6 files changed, 76 insertions(+), 20 deletions(-) diff --git a/modules/ripple_core/functional/ripple_Job.cpp b/modules/ripple_core/functional/ripple_Job.cpp index fe0075a271..2c4b435b88 100644 --- a/modules/ripple_core/functional/ripple_Job.cpp +++ b/modules/ripple_core/functional/ripple_Job.cpp @@ -7,17 +7,20 @@ Job::Job () : mType (jtINVALID) , mJobIndex (0) + , m_limit (0) { } Job::Job (JobType type, uint64 index) : mType (type) , mJobIndex (index) + , m_limit (0) { } Job::Job (JobType type, std::string const& name, + int limit, uint64 index, LoadMonitor& lm, FUNCTION_TYPE const& job) @@ -25,6 +28,7 @@ Job::Job (JobType type, , mJobIndex (index) , mJob (job) , mName (name) + , m_limit(limit) { m_loadEvent = boost::make_shared (boost::ref (lm), name, false); } @@ -52,6 +56,11 @@ void Job::rename (std::string const& newName) mName = newName; } +int Job::getLimit () const +{ + return m_limit; +} + const char* Job::toString (JobType t) { switch (t) diff --git a/modules/ripple_core/functional/ripple_Job.h b/modules/ripple_core/functional/ripple_Job.h index a49589bf3f..d1305d7ad8 100644 --- a/modules/ripple_core/functional/ripple_Job.h +++ b/modules/ripple_core/functional/ripple_Job.h @@ -66,6 +66,7 @@ public: // VFALCO TODO try to remove the dependency on LoadMonitor. Job (JobType type, std::string const& name, + int limit, uint64 index, LoadMonitor& lm, FUNCTION_TYPE const& job); @@ -76,6 +77,8 @@ public: void rename (const std::string& n); + int getLimit () const; + // These comparison operators make the jobs sort in priority order in the job set bool operator< (const Job& j) const; bool operator> (const Job& j) const; @@ -90,6 +93,7 @@ private: FUNCTION_TYPE mJob; LoadEvent::pointer m_loadEvent; std::string mName; + int m_limit; }; #endif diff --git a/modules/ripple_core/functional/ripple_JobQueue.cpp b/modules/ripple_core/functional/ripple_JobQueue.cpp index e6741cd79f..5d88673ea0 100644 --- a/modules/ripple_core/functional/ripple_JobQueue.cpp +++ b/modules/ripple_core/functional/ripple_JobQueue.cpp @@ -31,6 +31,11 @@ JobQueue::JobQueue (boost::asio::io_service& svc) } void JobQueue::addJob (JobType type, const std::string& name, const FUNCTION_TYPE& jobFunc) +{ + addLimitJob(type, name, 0, jobFunc); +} + +void JobQueue::addLimitJob (JobType type, const std::string& name, int limit, const FUNCTION_TYPE& jobFunc) { assert (type != jtINVALID); @@ -39,7 +44,7 @@ void JobQueue::addJob (JobType type, const std::string& name, const FUNCTION_TYP if (type != jtCLIENT) // FIXME: Workaround incorrect client shutdown ordering assert (mThreadCount != 0); // do not add jobs to a queue with no threads - mJobSet.insert (Job (type, name, ++mLastJob, mJobLoads[type], jobFunc)); + mJobSet.insert (Job (type, name, limit, ++mLastJob, mJobLoads[type], jobFunc)); ++mJobCounts[type].first; mJobCond.notify_one (); } @@ -232,6 +237,37 @@ void JobQueue::setThreadCount (int c, bool const standaloneMode) mJobCond.notify_one (); // in case we sucked up someone else's signal } +bool JobQueue::getJob(Job& job) +{ + if (mJobSet.empty() || mShuttingDown) + return false; + + std::set::iterator it = mJobSet.begin (); + + while (1) + { + // Are we out of jobs? + if (it == mJobSet.end()) + return false; + + // Does this job have no limit? + if (it->getLimit() == 0) + break; + + // Is this job category below the limit? + if (mJobCounts[it->getType()].second < it->getLimit()) + break; + + // Try the next job, if any + ++it; + } + + job = *it; + mJobSet.erase (it); + + return true; +} + // do jobs until asked to stop void JobQueue::threadEntry () { @@ -239,27 +275,32 @@ void JobQueue::threadEntry () while (1) { + JobType type; + setCallingThreadName ("waiting"); - while (mJobSet.empty () && !mShuttingDown) { - mJobCond.wait (sl); - } - - if (mJobSet.empty ()) - break; - - JobType type; - std::set::iterator it = mJobSet.begin (); - { - Job job (*it); - mJobSet.erase (it); + Job job; + while (!getJob(job)) + { + if (mShuttingDown) + { + --mThreadCount; + mJobCond.notify_all(); + return; + } + mJobCond.wait (sl); + } type = job.getType (); -- (mJobCounts[type].first); if (type == jtDEATH) - break; + { + --mThreadCount; + mJobCond.notify_all(); + return; + } ++ (mJobCounts[type].second); sl.unlock (); @@ -267,12 +308,10 @@ void JobQueue::threadEntry () WriteLog (lsTRACE, JobQueue) << "Doing " << Job::toString (type) << " job"; job.doJob (); } // must destroy job without holding lock + sl.lock (); -- (mJobCounts[type].second); } - - --mThreadCount; - mJobCond.notify_all (); } // vim:ts=4 diff --git a/modules/ripple_core/functional/ripple_JobQueue.h b/modules/ripple_core/functional/ripple_JobQueue.h index 6526f90b32..30f877197b 100644 --- a/modules/ripple_core/functional/ripple_JobQueue.h +++ b/modules/ripple_core/functional/ripple_JobQueue.h @@ -16,6 +16,7 @@ public: // have to call bind. // void addJob (JobType type, const std::string& name, const FUNCTION_TYPE& job); + void addLimitJob (JobType type, const std::string& name, int limit, const FUNCTION_TYPE& job); int getJobCount (JobType t); // Jobs waiting at this priority int getJobCountTotal (JobType t); // Jobs waiting plus running at this priority @@ -59,6 +60,8 @@ private: boost::asio::io_service& mIOService; std::map > mJobCounts; + + bool getJob (Job& job); }; #endif diff --git a/src/cpp/ripple/ripple_Peer.cpp b/src/cpp/ripple/ripple_Peer.cpp index ab50643e54..d7bfe89478 100644 --- a/src/cpp/ripple/ripple_Peer.cpp +++ b/src/cpp/ripple/ripple_Peer.cpp @@ -2134,7 +2134,7 @@ void PeerImp::recvLedger (const boost::shared_ptr& packe } if (getApp().getInboundLedgers ().awaitLedgerData (hash)) - getApp().getJobQueue ().addJob (jtLEDGER_DATA, "gotLedgerData", + getApp().getJobQueue ().addLimitJob (jtLEDGER_DATA, "gotLedgerData", 2, BIND_TYPE (&InboundLedgers::gotLedgerData, &getApp().getInboundLedgers (), P_1, hash, packet_ptr, boost::weak_ptr (shared_from_this ()))); else @@ -2369,7 +2369,7 @@ void PeerImp::doFetchPack (const boost::shared_ptr& return; } - getApp().getJobQueue ().addJob (jtPACK, "MakeFetchPack", + getApp().getJobQueue ().addLimitJob (jtPACK, "MakeFetchPack", 1, BIND_TYPE (&NetworkOPs::makeFetchPack, &getApp().getOPs (), P_1, boost::weak_ptr (shared_from_this ()), packet, wantLedger, haveLedger, UptimeTimer::getInstance ().getElapsedSeconds ())); } diff --git a/src/cpp/ripple/ripple_PeerSet.cpp b/src/cpp/ripple/ripple_PeerSet.cpp index ef490aed27..9f9f02b956 100644 --- a/src/cpp/ripple/ripple_PeerSet.cpp +++ b/src/cpp/ripple/ripple_PeerSet.cpp @@ -82,7 +82,8 @@ void PeerSet::TimerEntry (boost::weak_ptr wptr, const boost::system::er ptr->setTimer (); } else - getApp().getJobQueue ().addJob (jtLEDGER_DATA, "timerEntry", BIND_TYPE (&PeerSet::TimerJobEntry, P_1, ptr)); + getApp().getJobQueue ().addLimitJob (jtLEDGER_DATA, "timerEntry", 2, + BIND_TYPE (&PeerSet::TimerJobEntry, P_1, ptr)); } } From 454a713e3f6fe077bd7eb750acda56f16b4e8e67 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sat, 13 Jul 2013 19:08:46 -0700 Subject: [PATCH 3/6] Wrong field type. --- src/cpp/ripple/ripple_Validations.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpp/ripple/ripple_Validations.cpp b/src/cpp/ripple/ripple_Validations.cpp index 9d3455b953..016c305a11 100644 --- a/src/cpp/ripple/ripple_Validations.cpp +++ b/src/cpp/ripple/ripple_Validations.cpp @@ -221,7 +221,7 @@ private: { ++trusted; if (it.second->isFieldPresent(sfLoadFee)) - fee += it.second->getFieldU64(sfLoadFee); + fee += it.second->getFieldU32(sfLoadFee); else fee += ref; } From 2addaacfbb614b96c35b5f804b225c5221a234ad Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sat, 13 Jul 2013 19:24:57 -0700 Subject: [PATCH 4/6] After a ledger is acquired, see if it is already fully-validated. --- src/cpp/ripple/LedgerMaster.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cpp/ripple/LedgerMaster.cpp b/src/cpp/ripple/LedgerMaster.cpp index cd52f306db..6e3a1ca26a 100644 --- a/src/cpp/ripple/LedgerMaster.cpp +++ b/src/cpp/ripple/LedgerMaster.cpp @@ -138,6 +138,8 @@ void LedgerMaster::storeLedger (Ledger::pointer ledger) if (ledger->isAccepted ()) mLedgerHistory.addAcceptedLedger (ledger, false); + + checkAccept (ledger->getHash(), ledger->getLedgerSeq()); } Ledger::pointer LedgerMaster::closeLedger (bool recover) From 0fd5f98c9cb19f98ca2073fb8673cd1104caa9b7 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sat, 13 Jul 2013 19:32:20 -0700 Subject: [PATCH 5/6] Clean up some confusion about the last validated ledger versus the last published ledger. --- src/cpp/ripple/LedgerMaster.cpp | 19 ++++++++++++++++++- src/cpp/ripple/LedgerMaster.h | 10 +++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/cpp/ripple/LedgerMaster.cpp b/src/cpp/ripple/LedgerMaster.cpp index 6e3a1ca26a..c7c1036aa6 100644 --- a/src/cpp/ripple/LedgerMaster.cpp +++ b/src/cpp/ripple/LedgerMaster.cpp @@ -23,6 +23,23 @@ Ledger::ref LedgerMaster::getCurrentSnapshot () return mCurrentSnapshot; } +int LedgerMaster::getPublishedLedgerAge () +{ + boost::recursive_mutex::scoped_lock ml (mLock); + if (!mPubLedger) + { + WriteLog (lsDEBUG, LedgerMaster) << "No published ledger"; + return 999999; + } + + int64 ret = getApp().getOPs ().getCloseTimeNC (); + ret -= static_cast (mPubLedger->getCloseTimeNC ()); + ret = std::max (0LL, ret); + + WriteLog (lsTRACE, LedgerMaster) << "Published ledger age is " << ret; + return static_cast (ret); +} + int LedgerMaster::getValidatedLedgerAge () { boost::recursive_mutex::scoped_lock ml (mLock); @@ -42,7 +59,7 @@ int LedgerMaster::getValidatedLedgerAge () bool LedgerMaster::isCaughtUp(std::string& reason) { - if (getValidatedLedgerAge() > 180) + if (getPublishedLedgerAge() > 180) { reason = "No recently-validated ledger"; return false; diff --git a/src/cpp/ripple/LedgerMaster.h b/src/cpp/ripple/LedgerMaster.h index 4df10d0eb7..d000566ae5 100644 --- a/src/cpp/ripple/LedgerMaster.h +++ b/src/cpp/ripple/LedgerMaster.h @@ -58,11 +58,19 @@ public: return mFinalizedLedger; } - // The published ledger is the last fully validated ledger + // The validated ledger is the last fully validated ledger Ledger::ref getValidatedLedger () + { + return mValidLedger; + } + + // This is the last ledger we published to clients and can lag the validated ledger + Ledger::ref getPublishedLedger () { return mPubLedger; } + + int getPublishedLedgerAge (); int getValidatedLedgerAge (); bool isCaughtUp(std::string& reason); From 1e26703684f9e8022ebaa8021472c59d891b8526 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sat, 13 Jul 2013 19:37:57 -0700 Subject: [PATCH 6/6] Report where the ledger publication stream is in server_info --- src/cpp/ripple/NetworkOPs.cpp | 8 +++++++- src/cpp/ripple/NetworkOPs.h | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp index 30910e4396..13694d7180 100644 --- a/src/cpp/ripple/NetworkOPs.cpp +++ b/src/cpp/ripple/NetworkOPs.cpp @@ -1509,6 +1509,12 @@ Json::Value NetworkOPs::getServerInfo (bool human, bool admin) info["validated_ledger"] = l; else info["closed_ledger"] = l; + + Ledger::pointer lpPublished = getPublishedLedger (); + if (!lpPublished) + info["published_ledger"] = "none"; + else if (lpPublished->getLedgerSeq() != lpClosed->getLedgerSeq()) + info["published_ledger"] = lpPublished->getLedgerSeq(); } return info; @@ -2375,7 +2381,7 @@ void NetworkOPs::doClusterReport () node.set_reporttime(it->second.getReportTime()); node.set_nodeload(it->second.getLoadFee()); if (!it->second.getName().empty()) - node.set_nodename(it->second.getName()); + node.set_nodename(it->second.getName()); } PackedMessage::pointer message = boost::make_shared(cluster, protocol::mtCLUSTER); diff --git a/src/cpp/ripple/NetworkOPs.h b/src/cpp/ripple/NetworkOPs.h index 0bb74cf30a..d2797f7471 100644 --- a/src/cpp/ripple/NetworkOPs.h +++ b/src/cpp/ripple/NetworkOPs.h @@ -80,6 +80,10 @@ public: { return mLedgerMaster->getValidatedLedger (); } + Ledger::ref getPublishedLedger () + { + return mLedgerMaster->getPublishedLedger (); + } Ledger::ref getCurrentLedger () { return mLedgerMaster->getCurrentLedger ();