-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
test: Retry all RPC commands to fix MacOS CI jobs #5171
base: develop
Are you sure you want to change the base?
Changes from 4 commits
1150626
38a290d
97a5be0
75130e9
205fe37
bf76d9d
63768ce
3f9a7ad
58e2652
e66d2e1
315b7dd
7f74c46
34d918a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -292,6 +292,17 @@ class Env | |
The command is examined and used to build | ||
the correct JSON as per the arguments. | ||
*/ | ||
using rpcCallback = std::function<bool(Json::Value const&)>; | ||
const rpcCallback useDefaultCallback{}; | ||
const rpcCallback expectInternalRPCError = [](Json::Value const&) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we rename this to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. Fixed. |
||
return true; | ||
}; | ||
rpcCallback | ||
getInternalFailureCallback(bool shouldFail) | ||
{ | ||
return shouldFail ? expectInternalRPCError : useDefaultCallback; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we rename this to |
||
|
||
template <class... Args> | ||
Json::Value | ||
rpc(unsigned apiVersion, | ||
|
@@ -303,12 +314,23 @@ class Env | |
Json::Value | ||
rpc(unsigned apiVersion, std::string const& cmd, Args&&... args); | ||
|
||
template <class... Args> | ||
Json::Value | ||
rpc(rpcCallback cb, | ||
std::unordered_map<std::string, std::string> const& headers, | ||
std::string const& cmd, | ||
Args&&... args); | ||
|
||
template <class... Args> | ||
Json::Value | ||
rpc(std::unordered_map<std::string, std::string> const& headers, | ||
std::string const& cmd, | ||
Args&&... args); | ||
|
||
template <class... Args> | ||
Json::Value | ||
rpc(rpcCallback cb, std::string const& cmd, Args&&... args); | ||
|
||
template <class... Args> | ||
Json::Value | ||
rpc(std::string const& cmd, Args&&... args); | ||
|
@@ -514,7 +536,7 @@ class Env | |
/** Gets the TER result and `didApply` flag from a RPC Json result object. | ||
*/ | ||
static ParsedResult | ||
parseResult(Json::Value const& jr); | ||
parseResult(Json::Value const& jr, bool checkTER); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't quite understand what this parameter is supposed to be doing. Can we add an explanation in the docstring? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A few more checks, and I was able to get rid of |
||
|
||
/** Submit an existing JTx. | ||
This calls postconditions. | ||
|
@@ -696,6 +718,7 @@ class Env | |
|
||
Json::Value | ||
do_rpc( | ||
rpcCallback cb, | ||
unsigned apiVersion, | ||
std::vector<std::string> const& args, | ||
std::unordered_map<std::string, std::string> const& headers = {}); | ||
|
@@ -746,6 +769,7 @@ Env::rpc( | |
Args&&... args) | ||
{ | ||
return do_rpc( | ||
useDefaultCallback, | ||
apiVersion, | ||
std::vector<std::string>{cmd, std::forward<Args>(args)...}, | ||
headers); | ||
|
@@ -765,16 +789,39 @@ Env::rpc(unsigned apiVersion, std::string const& cmd, Args&&... args) | |
template <class... Args> | ||
Json::Value | ||
Env::rpc( | ||
rpcCallback cb, | ||
std::unordered_map<std::string, std::string> const& headers, | ||
std::string const& cmd, | ||
Args&&... args) | ||
{ | ||
return do_rpc( | ||
cb, | ||
RPC::apiCommandLineVersion, | ||
std::vector<std::string>{cmd, std::forward<Args>(args)...}, | ||
headers); | ||
} | ||
|
||
template <class... Args> | ||
Json::Value | ||
Env::rpc( | ||
std::unordered_map<std::string, std::string> const& headers, | ||
std::string const& cmd, | ||
Args&&... args) | ||
{ | ||
return rpc(useDefaultCallback, headers, cmd, std::forward<Args>(args)...); | ||
} | ||
|
||
template <class... Args> | ||
Json::Value | ||
Env::rpc(rpcCallback cb, std::string const& cmd, Args&&... args) | ||
{ | ||
return rpc( | ||
cb, | ||
std::unordered_map<std::string, std::string>(), | ||
cmd, | ||
std::forward<Args>(args)...); | ||
} | ||
|
||
template <class... Args> | ||
Json::Value | ||
Env::rpc(std::string const& cmd, Args&&... args) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -273,7 +273,7 @@ Env::trust(STAmount const& amount, Account const& account) | |
} | ||
|
||
Env::ParsedResult | ||
Env::parseResult(Json::Value const& jr) | ||
Env::parseResult(Json::Value const& jr, bool checkTER) | ||
{ | ||
auto error = [](ParsedResult& parsed, Json::Value const& object) { | ||
// Use an error code that is not used anywhere in the transaction | ||
|
@@ -296,14 +296,19 @@ Env::parseResult(Json::Value const& jr) | |
if (jr.isObject() && jr.isMember(jss::result)) | ||
{ | ||
auto const& result = jr[jss::result]; | ||
if (result.isMember(jss::engine_result_code)) | ||
if (checkTER && result.isMember(jss::engine_result_code)) | ||
{ | ||
parsed.ter = TER::fromInt(result[jss::engine_result_code].asInt()); | ||
parsed.rpcCode.emplace(rpcSUCCESS); | ||
} | ||
else if (!checkTER && !result.isMember(jss::error)) | ||
parsed.rpcCode.emplace(rpcSUCCESS); | ||
else | ||
error(parsed, result); | ||
} | ||
else if ( | ||
jr.isObject() && jr.isMember(jss::error) && jr[jss::error].isObject()) | ||
error(parsed, jr[jss::error]); | ||
else | ||
error(parsed, jr); | ||
|
||
|
@@ -317,24 +322,20 @@ Env::submit(JTx const& jt) | |
auto const jr = [&]() { | ||
if (jt.stx) | ||
{ | ||
// We shouldn't need to retry, but it fixes the test on macOS for | ||
// the moment. | ||
int retries = 3; | ||
do | ||
{ | ||
txid_ = jt.stx->getTransactionID(); | ||
Serializer s; | ||
jt.stx->add(s); | ||
auto const jr = rpc("submit", strHex(s.slice())); | ||
|
||
parsedResult = parseResult(jr); | ||
txid_ = jt.stx->getTransactionID(); | ||
Serializer s; | ||
jt.stx->add(s); | ||
auto const cb = [&](Json::Value const& jr) { | ||
parsedResult = parseResult(jr, true); | ||
test.expect(parsedResult.ter, "ter uninitialized!"); | ||
ter_ = parsedResult.ter.value_or(telENV_RPC_FAILED); | ||
if (ter_ != telENV_RPC_FAILED || | ||
return ( | ||
ter_ != telENV_RPC_FAILED || | ||
parsedResult.rpcCode != rpcINTERNAL || | ||
jt.ter == telENV_RPC_FAILED || --retries <= 0) | ||
return jr; | ||
} while (true); | ||
jt.ter == telENV_RPC_FAILED); | ||
}; | ||
// rpc() will call cb(), which does all the parsing | ||
return rpc(cb, "submit", strHex(s.slice())); | ||
} | ||
else | ||
{ | ||
|
@@ -379,7 +380,7 @@ Env::sign_and_submit(JTx const& jt, Json::Value params) | |
if (!txid_.parseHex(jr[jss::result][jss::tx_json][jss::hash].asString())) | ||
txid_.zero(); | ||
|
||
ParsedResult const parsedResult = parseResult(jr); | ||
ParsedResult const parsedResult = parseResult(jr, true); | ||
test.expect(parsedResult.ter, "ter uninitialized!"); | ||
ter_ = parsedResult.ter.value_or(telENV_RPC_FAILED); | ||
|
||
|
@@ -562,12 +563,37 @@ Env::ust(JTx const& jt) | |
|
||
Json::Value | ||
Env::do_rpc( | ||
rpcCallback cb, | ||
unsigned apiVersion, | ||
std::vector<std::string> const& args, | ||
std::unordered_map<std::string, std::string> const& headers) | ||
{ | ||
return rpcClient(args, app().config(), app().logs(), apiVersion, headers) | ||
.second; | ||
// We shouldn't need to retry, but it fixes the test on macOS for | ||
// the moment. | ||
if (!cb) | ||
{ | ||
static auto const defaultCallback = [](Json::Value const& jr) { | ||
auto const parsedResult = parseResult(jr, false); | ||
return parsedResult.rpcCode != rpcINTERNAL; | ||
}; | ||
cb = defaultCallback; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we move this out of the function, into the definition of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. Fixed. |
||
} | ||
int retries = 3; | ||
do | ||
{ | ||
auto const ret = | ||
rpcClient(args, app().config(), app().logs(), apiVersion, headers) | ||
.second; | ||
if (cb(ret) || --retries <= 0) | ||
return ret; | ||
std::stringstream ss; | ||
for (auto const& arg : args) | ||
{ | ||
ss << arg << ", "; | ||
} | ||
test.log << "RPC failed: " << ss.str() << " -> " << to_string(ret) | ||
<< std::endl; | ||
} while (true); | ||
} | ||
|
||
void | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we capitalize the name (
RpcCallback
)?Please add a comment like "Given the return value of an RPC, returns true if it is acceptable."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Fixed.