Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Peer reservations #2935

Closed

Conversation

thejohnfreeman
Copy link
Collaborator

@thejohnfreeman thejohnfreeman commented May 15, 2019

I want to get this PR started now, while the work is in progress, to get more eyes and some direction. I'm going to link some internal resources for now; once they are polished, I'd like to find some way to publish them publicly.

For context, see #2938 (and JIRA 1712 if you have access).

I've written what I've deduced to be the steps for adding a command to rippled on our internal wiki. I've been following those steps below. Please comment there or here with any corrections or blind spots (especially blind spots; I don't know what I don't know).

I've left some questions in comments in the changes below. Please take a look and see if you can answer any. If you just want to skim for them, Ctrl-f for ?.

I'm having trouble figuring out how I should think about and handle "public key for a node" as a string. If there are any docs on this, please point me to them.

Similarly, our RPC "framework" seems to be immune to function abstraction. I've taken a stab at it below, but I'm not happy with it. Here's the pattern I see in the rest of the code:

Json::Value result;

auto tmpVar = tryToParse(someField);
if (notGood(tmpVar))
    return rpcError(rpcERROR_CONSTANT);

result[jss::some_field] = tmpVar;
return result;

The problem is the temporary variable combined with the early return for an error. If I try to put this in a function, I can't return both, so I end up taking an out parameter for the value. But I don't think I want to test for an empty Json::Value in the event of success, so I take that as an out parameter too and return a bool. It's all very gross to me. This kind of control flow is better suited by exceptions, in my opinion, or by an Error monad (do we have one available?). Thoughts?

@ripplelabs-jenkins
Copy link
Collaborator

ripplelabs-jenkins commented May 15, 2019

Jenkins Build Summary

Built from this commit

Built at 20190805 - 23:42:42

Test Results

Build Type Log Result Status
msvc.Debug logfile 1178 cases, 0 failed, t: 576s PASS ✅
msvc.Debug,
NINJA_BUILD=true
logfile 1178 cases, 0 failed, t: 607s PASS ✅
msvc.Debug
-Dunity=OFF
console no test results, t: n/a [BAD EXIT] FAIL 🔴
msvc.Release logfile 1178 cases, 0 failed, t: 412s PASS ✅
gcc.Release
-Dassert=ON,
MANUAL_TESTS=true
logfile 915 cases, 0 failed, t: 4m57s PASS ✅
gcc.Debug
-Dcoverage=ON,
TARGET=coverage_report,
SKIP_TESTS=true
logfile 1178 cases, 0 failed, t: 16m56s PASS ✅
docs,
TARGET=docs
logfile 1 cases, 0 failed, t: 0m1s PASS ✅
clang.Debug logfile 1178 cases, 0 failed, t: 2m58s PASS ✅
clang.Debug
-Dunity=OFF
logfile 1177 cases, 0 failed, t: 11m24s PASS ✅
gcc.Debug logfile 1178 cases, 0 failed, t: 3m0s PASS ✅
gcc.Debug
-Dunity=OFF
logfile 1177 cases, 0 failed, t: 11m45s PASS ✅
clang.Release
-Dassert=ON
logfile 1178 cases, 0 failed, t: 5m7s PASS ✅
gcc.Release
-Dassert=ON
logfile 1178 cases, 0 failed, t: 5m1s PASS ✅
gcc.Debug
-Dstatic=OFF
logfile 1178 cases, 0 failed, t: 3m0s PASS ✅
gcc.Debug
-Dstatic=OFF -DBUILD_SHARED_LIBS=ON
logfile 1178 cases, 0 failed, t: 2m58s PASS ✅
gcc.Debug,
NINJA_BUILD=true
logfile 1178 cases, 0 failed, t: 2m50s PASS ✅
clang.Debug
-Dunity=OFF -Dsan=address,
PARALLEL_TESTS=false,
DEBUGGER=false
logfile 1177 cases, 0 failed, t: 11m35s PASS ✅
clang.Debug
-Dunity=OFF -Dsan=undefined,
PARALLEL_TESTS=false
logfile 1177 cases, 0 failed, t: 12m57s PASS ✅

@MarkusTeufelberger
Copy link
Collaborator

For context, see JIRA 1712.

:-(

@nbougalis
Copy link
Contributor

nbougalis commented May 16, 2019

@MarkusTeufelberger, obviously having the conversation on our internal wiki isn't ideal (we're moving away from it ourselves, in favor of open conversation on GitHub). Here's the relevant content in the form of an issue: #2938

@MarkusTeufelberger
Copy link
Collaborator

Thanks! :-)

Much clearer what this is supposed to do (without having to guess at the intentions).

Copy link
Collaborator

@scottschurr scottschurr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should start out by saying I have a great deal of sympathy for the position you're in. When I started working on the rippled code base a lot of the conventions did not make sense to me. Many of them still don't "make sense", but I've learned to live with most of them. A few of them I've managed to adjust so they make more sense to me, but possible less sense to others.

Regarding adding src/ripple/basics/Result.h, I believe we already have a (less elegant) approach to solving that problem, at least at the RPC level where you are using it at the moment. Take a look at https://github.com/ripple/rippled/blob/develop/src/ripple/protocol/ErrorCodes.h. That file provides a way to inject an error message into a Json::Value (make_error) and test a Json::Value for the presence of a such an error (contains_error). I suggest you see if you can coerce those facilities to match your needs.

There are definitely problems with error handling in the rippled code base. I'm not trying to defend the current state of affairs. But I think it's a better idea to stick with the approaches we have until someone can come up with a proposal that we think significantly improves the situation. Otherwise we're simply proliferating error handling machinery, which in the end makes the matter worse/more confusing.

src/ripple/overlay/PeerReservationTable.h Outdated Show resolved Hide resolved
src/ripple/protocol/jss.h Show resolved Hide resolved
src/ripple/protocol/jss.h Outdated Show resolved Hide resolved
src/ripple/protocol/jss.h Outdated Show resolved Hide resolved
src/ripple/rpc/handlers/Reservations.cpp Outdated Show resolved Hide resolved
@miguelportilla
Copy link
Contributor

@thejohnfreeman For unity builds, rcpcx1.cpp should include Reservations.cpp

// TODO: Make a formal team decision that we do not want to raise an
// error upon a collision.
auto emplaced = context.app.peerReservations().emplace(
std::make_pair(identity, overlay::PeerReservation{identity, name}));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realize its a WIP but you might get away with simply storing an optional string versus a PeerReservation.

@miguelportilla
Copy link
Contributor

miguelportilla commented May 21, 2019

@thejohnfreeman You might want to consider having add and remove accept an array of objects allowing the caller to specify one or more pubkey/name pairs in a single call.

@manojsdoshi manojsdoshi mentioned this pull request May 21, 2019
@codecov-io
Copy link

Codecov Report

Merging #2935 into develop will decrease coverage by 0.07%.
The diff coverage is 16.09%.

Impacted file tree graph

@@            Coverage Diff             @@
##           develop   #2935      +/-   ##
==========================================
- Coverage    69.38%   69.3%   -0.08%     
==========================================
  Files          708     710       +2     
  Lines        56426   56490      +64     
==========================================
+ Hits         39152   39153       +1     
- Misses       17274   17337      +63
Impacted Files Coverage Δ
src/ripple/peerfinder/impl/SlotImp.cpp 51.28% <ø> (ø) ⬆️
src/ripple/peerfinder/Slot.h 100% <ø> (ø) ⬆️
src/ripple/app/main/Application.h 100% <ø> (ø) ⬆️
src/ripple/peerfinder/PeerfinderManager.h 100% <ø> (ø) ⬆️
src/ripple/overlay/impl/PeerImp.h 0% <ø> (ø) ⬆️
src/ripple/peerfinder/impl/PeerfinderManager.cpp 55% <0%> (ø) ⬆️
src/ripple/overlay/impl/PeerImp.cpp 0% <0%> (ø) ⬆️
src/ripple/overlay/impl/OverlayImpl.cpp 28.91% <0%> (-0.05%) ⬇️
src/ripple/overlay/PeerReservationTable.h 0% <0%> (ø)
src/ripple/net/impl/RPCCall.cpp 94.38% <0%> (-1.46%) ⬇️
... and 11 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 5214b3c...3f5098e. Read the comment docs.

@scottschurr
Copy link
Collaborator

Sadly, this branch is is not currently building for me on macOS using Boost 1.70.0. It looks like 1.70.0 lost the get_io_service() method in a couple of places. All of the build errors I'm seeing involve the invocation of get_io_service() member functions. The lines that I'm seeing build errors on are:

src/ripple/server/impl/PlainWSPeer.h:68
src/ripple/server/impl/PlainHTTPPeer.h:71
src/ripple/server/impl/SSLWSPeer.h:76
src/ripple/server/impl/SSLHTTPPeer.h:81
src/ripple/server/impl/Door.h:180
src/ripple/server/impl/Door.h:182
src/ripple/server/impl/Door.h:376

It's possible that a rebase atop 1.3.0-b6 will fix the issues. It looks like the branch is currently sitting atop 1.3.0-b3

Copy link
Collaborator

@scottschurr scottschurr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general the code looks very nice. I like the way you've made an effort to work in coordination with local conventions, even though you may find those conventions irksome. That's not to say we can never change these conventions. But if we do, the team should agree that the change should be made and we should then fix it everywhere (in my not so humble opinion). So you're doing something that some other folks in this code base have not been so good at. Thank you.

I did leave some comments that I'd either like to see addressed or understand a motivation for not making the change. I'm good either way, but I think some of the points are worth discussing.

Thanks. Good work.

src/ripple/app/main/Main.cpp Outdated Show resolved Hide resolved
src/ripple/overlay/PeerReservationTable.h Outdated Show resolved Hide resolved
src/ripple/overlay/PeerReservationTable.h Show resolved Hide resolved
src/ripple/overlay/PeerReservationTable.h Outdated Show resolved Hide resolved
src/ripple/overlay/PeerReservationTable.h Outdated Show resolved Hide resolved
@@ -231,7 +231,7 @@ class Counts
map ["in"] << m_in_active << "/" << m_in_max;
map ["out"] << m_out_active << "/" << m_out_max;
map ["fixed"] = m_fixed_active;
map ["cluster"] = m_cluster;
map ["reserved"] = m_reserved;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is written to a public API, so I'm guessing the rename from cluster to reserved won't hurt anybody. But that would be a good thing to research and confirm. And maybe leave a comment once you've done the research. Thanks.

// REVIEWER: What is the policy on forward declarations? We can use one
// for DatabaseCon.
DatabaseCon* connection_;
table_type table_;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My spidey sense may be telling me about a race condition here? What assurances do we have that, say, upsert() and begin() cannot be called on different threads? Now or in the future?

If you can't give that assurance, and document why that assurance is valid, then I think accesses to table_ will need to be guarded with an mutex. That would also mean removing the iterator interface that returns your internal implementation. If this is necessary then I'd suggest replacing the iterator interface with a call that returns a sorted std::vector<PerReservation>.

src/ripple/overlay/impl/PeerReservationTable.cpp Outdated Show resolved Hide resolved
if (slot.cluster())
item ["cluster"] = "yes";
if (slot.reserved())
item ["reserved"] = "yes";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to Counts.h, I don't think this is written to a public API, so I'm guessing the rename from cluster to reserved won't hurt anybody. But that would be a good thing to research and confirm. And maybe leave a comment once you've done the research. Thanks.

src/ripple/app/main/DBInit.cpp Outdated Show resolved Hide resolved
src/ripple/overlay/impl/OverlayImpl.cpp Outdated Show resolved Hide resolved
src/ripple/overlay/impl/OverlayImpl.cpp Outdated Show resolved Hide resolved
Copy link
Collaborator

@seelabs seelabs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 (tho left a couple new nits)

@nbougalis nbougalis mentioned this pull request Aug 5, 2019
Copy link
Collaborator

@scottschurr scottschurr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thread safety looks like it's almost there, but there's a call to table_.size() which should be moved under a lock.

src/ripple/overlay/PeerReservationTable.h Outdated Show resolved Hide resolved
src/ripple/overlay/impl/PeerReservationTable.cpp Outdated Show resolved Hide resolved
// https://stackoverflow.com/q/49651835/618906
// Regardless, we don't expect this function to be called often, or
// for the table to be very large, so this less-than-ideal
// remove-then-insert is acceptable in order to present a better API.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, I suspect the awkwardness of this method suggests that the right choice for table_'s container would have been a map or unordered_map. Then you'd be able to replace the value without needing to remove the entire node. But I also know this has been discussed plenty, and I'm not asking for any changes.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree. I think it's a problem with unordered_set. If it had an insert_or_assign method the same as unordered_map, then there would be no awkwardness. There is no technical reason it cannot have such a method. I don't think a deficiency in one standard container means the right choice is to pick a differently deficient container.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the perspective of the standard designers there is no deficiency. The key to both a set and a map must be immutable. If you were allowed to mutate a key in place that would corrupt the data structure. So, sensibly enough, the interface doesn't allow you to mutate the key (without working hard).

A set only has a key, and there are no options for a partially non-const key. If you need a partially non-const key, well, that's what map is for.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you define "mutate the key"? I don't see insert_or_assign as a mutation of the key. I see it as an atomic replacement. It is not like asking the container to return an iterator to a non-const key where I can mutate the key outside the purview of the container. I am asking the container itself to atomically remove one key and insert another. It has full control of its invariants before, during, and after that process. No corruption would be possible. I don't need the key_type or value_type of unordered_set to be mutable at all. I just need an amotic replace/upsert/insert_or_assign/whatever-you-want-to-call-it method.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason I was referring to "mutate the key" is because the part of the key that determines the location in the set does not change during the "atomic replacement". The only thing that changes is the description_. So you could leave the key in place and "mutate the key" by changing (only) description_.

I'm not going to argue with you about whether insert_or_assign should be a part of the standard set interface.

Copy link
Collaborator

@scottschurr scottschurr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@thejohnfreeman thejohnfreeman deleted the 1712-peer-reservations branch April 8, 2020 01:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants