Added concept of type to JsonString. Appending single items or key/value pairs is now type-safe and only allowed in certain cases. JsonString will refuse to produce invalid JSON. All duplicative functions have been replaced with templates. Encoding of values uses json_spirit's value writer which should be no worse performance than format() and it will escape everything properly. Final string form is now built directly using knowledge of type, such as when an instance becomes an Array or Object the appropriate opening character is written. This avoids a full copy just to prepend the opening character later. Index interface for key/value pairs no longer makes a temporary copy of the key string. JsonString is now only needed by Status.actor.cpp. Still more work to be done here.

This commit is contained in:
Stephen Atherton 2018-09-08 07:15:28 -07:00
parent 54989bbc63
commit ce3f01a0cf
7 changed files with 229 additions and 502 deletions

3
fdbclient/ClusterInterface.h Normal file → Executable file
View File

@ -27,7 +27,6 @@
#include "Status.h"
#include "ClientDBInfo.h"
#include "ClientWorkerInterface.h"
#include "JsonString.h"
struct ClusterInterface {
RequestStream< struct OpenDatabaseRequest > openDatabase;
@ -193,7 +192,7 @@ struct StatusReply {
StatusReply() {}
explicit StatusReply(StatusObject obj) : statusObj(obj), statusStr(json_spirit::write_string(json_spirit::mValue(obj))) {}
explicit StatusReply(JsonString obj) : statusStr(obj.getJson()) {}
explicit StatusReply(std::string &&text) : statusStr(text) {}
template <class Ar>
void serialize(Ar& ar) {

346
fdbclient/JsonString.cpp Normal file → Executable file
View File

@ -1,355 +1,9 @@
#include "JsonString.h"
#include <iostream>
std::string format( const char* form, ... );
bool shouldEscape(char c) {
switch( c ) {
case '"':
case '\\':
case '\b':
case '\f':
case '\n':
case '\r':
case '\t':
return true;
default:
return false;
}
}
void escape( const std::string& in, std::string& out ) {
int beginCopy = 0;
for (int i = 0; i < in.size(); i++) {
if (shouldEscape(in[i])) {
out.append(in, beginCopy, i - beginCopy);
beginCopy = i + 1;
out += '\\';
out += in[i];
}
}
if(beginCopy < in.size()) {
out.append(in, beginCopy, in.size() - beginCopy);
}
}
JsonString::JsonString() : hasKey(false) {
}
JsonString::JsonString( const JsonString& jsonString) : _jsonText(jsonString._jsonText), hasKey(jsonString.hasKey) {
}
JsonString::JsonString( const JsonStringArray& jsonArray) : hasKey(false) {
append(jsonArray);
}
JsonString::JsonString( const std::string& value ) : hasKey(false) {
append(value);
}
JsonString::JsonString( const char* value ) : hasKey(false) {
append(value);
}
JsonString::JsonString( const json_spirit::mObject& value ) : hasKey(false) {
_jsonText = json_spirit::write_string(json_spirit::mValue(value));
_jsonText = _jsonText.substr(1,_jsonText.size()-2); //remove outer {}
hasKey = !_jsonText.empty();
}
JsonString::JsonString( const std::string& name, const std::string& value ) : hasKey(false) {
append(name, value);
}
JsonString::JsonString( const std::string& name, const char* value ) : hasKey(false) {
append(name, value);
}
JsonString::JsonString( const std::string& name, double value ) : hasKey(false) {
append(name, value);
}
JsonString::JsonString( const std::string& name, long int value ) : hasKey(false) {
append(name, value);
}
JsonString::JsonString( const std::string& name, long unsigned int value ) : hasKey(false) {
append(name, value);
}
JsonString::JsonString( const std::string& name, long long int value ) : hasKey(false) {
append(name, value);
}
JsonString::JsonString( const std::string& name, long long unsigned int value ) : hasKey(false) {
append(name, value);
}
JsonString::JsonString( const std::string& name, int value ) : hasKey(false) {
append(name, value);
}
JsonString::JsonString( const std::string& name, unsigned value ) : hasKey(false) {
append(name, value);
}
JsonString::JsonString( const std::string& name, bool value ) : hasKey(false) {
append(name, value);
}
JsonString::JsonString( const std::string& name, const JsonString& value ) : hasKey(false) {
append(name, value);
}
JsonString::JsonString( const std::string& name, const JsonStringArray& value ) : hasKey(false) {
append(name, value);
}
JsonString JsonString::makeMessage(const char *name, const char *description) {
JsonString out;
out["name"] = name;
out["description"] = description;
return out;
}
JsonString& JsonString::appendImpl( const std::string& name, const std::string& value, bool quote ) {
_jsonText.reserve(_jsonText.size() + name.size() + (quote ? (2*value.size() + 6) : (value.size() + 4)));
hasKey = true;
if(!_jsonText.empty()) {
_jsonText += ',';
}
_jsonText += '"';
_jsonText += name;
_jsonText += "\":";
if (quote) {
_jsonText += "\"";
escape(value, _jsonText);
_jsonText += "\"";
} else {
_jsonText += value;
}
return *this;
}
JsonString& JsonString::appendImpl( const std::string& value, bool quote ) {
_jsonText.reserve(_jsonText.size() + (quote ? (2*value.size() + 3) : (value.size() + 1)));
if(!_jsonText.empty()) {
_jsonText += ',';
}
if (quote) {
_jsonText += "\"";
escape(value, _jsonText);
_jsonText += "\"";
} else {
_jsonText += value;
}
return *this;
}
std::string JsonString::stringify(const char* value) {
return std::string(value);
}
std::string JsonString::stringify(double value) {
return format("%g", value);
}
std::string JsonString::stringify(long int value) {
return format("%ld", value);
}
std::string JsonString::stringify(long unsigned int value) {
return format("%lu", value);
}
std::string JsonString::stringify(long long int value) {
return format("%lld", value);
}
std::string JsonString::stringify(long long unsigned int value) {
return format("%llu", value);
}
std::string JsonString::stringify(int value) {
return format("%d", value);
}
std::string JsonString::stringify(unsigned value) {
return format("%u", value);
}
std::string JsonString::stringify(bool value) {
return value ? "true" : "false";
}
JsonString& JsonString::append( const std::string& name, const std::string& value ) {
return appendImpl(name, value, true);
}
JsonString& JsonString::append( const std::string& name, const char* value ) {
return appendImpl(name, stringify(value), true);
}
JsonString& JsonString::append( const std::string& name, double value ) {
return appendImpl(name, stringify(value), false);
}
JsonString& JsonString::append( const std::string& name, long int value ) {
return appendImpl(name, stringify(value), false);
}
JsonString& JsonString::append( const std::string& name, long unsigned int value ) {
return appendImpl(name, stringify(value), false);
}
JsonString& JsonString::append( const std::string& name, long long int value ) {
return appendImpl(name, stringify(value), false);
}
JsonString& JsonString::append( const std::string& name, long long unsigned int value ) {
return appendImpl(name, stringify(value), false);
}
JsonString& JsonString::append( const std::string& name, int value ) {
return appendImpl(name, stringify(value), false);
}
JsonString& JsonString::append( const std::string& name, unsigned value ) {
return appendImpl(name, stringify(value), false);
}
JsonString& JsonString::append( const std::string& name, bool value ) {
return appendImpl(name, stringify(value), false);
}
JsonString& JsonString::append( const std::string& name, const JsonString& value ) {
_jsonText.reserve(_jsonText.size() + name.size() + value._jsonText.size() + 6);
hasKey = true;
if(!_jsonText.empty()) {
_jsonText += ',';
}
_jsonText += '\"';
_jsonText += name;
_jsonText += "\":{";
_jsonText += value._jsonText;
_jsonText += '}';
return *this;
}
JsonString& JsonString::append( const std::string& name, const JsonStringArray& values ) {
int valueBytes = 0;
for (auto const& value : values) {
valueBytes += value.getLength();
}
_jsonText.reserve(_jsonText.size() + name.size() + values.size() + valueBytes + 6);
hasKey = true;
if(!_jsonText.empty()) {
_jsonText += ',';
}
_jsonText += '"';
_jsonText += name;
_jsonText += "\":[";
bool first = true;
for (auto const& value : values) {
if (!first) {
_jsonText += ',';
}
_jsonText += value.getJson();
first = false;
}
_jsonText += ']';
return *this;
}
JsonString& JsonString::append( const std::string& value ) {
return appendImpl(value, true);
}
JsonString& JsonString::append( const char* value ) {
return appendImpl(stringify(value), true);
}
JsonString& JsonString::append( double value ) {
return appendImpl(stringify(value), false);
}
JsonString& JsonString::append( long int value ) {
return appendImpl(stringify(value), false);
}
JsonString& JsonString::append( long unsigned int value ) {
return appendImpl(stringify(value), false);
}
JsonString& JsonString::append( long long int value ) {
return appendImpl(stringify(value), false);
}
JsonString& JsonString::append( long long unsigned int value ) {
return appendImpl(stringify(value), false);
}
JsonString& JsonString::append( int value ) {
return appendImpl(stringify(value), false);
}
JsonString& JsonString::append( unsigned value ) {
return appendImpl(stringify(value), false);
}
JsonString& JsonString::append( bool value ) {
return appendImpl(stringify(value), false);
}
JsonString& JsonString::append( const JsonString& value ) {
// Only do something, if not empty
if (!value.empty()) {
_jsonText.reserve(_jsonText.size() + value._jsonText.size() + 1);
if (!_jsonText.empty()) {
_jsonText += ',';
}
_jsonText += value._jsonText;
if(value.hasKey) {
hasKey = true;
}
}
return *this;
}
JsonString& JsonString::append( const JsonStringArray& values ) {
int valueBytes = 0;
for (auto const& value : values) {
valueBytes += value.getLength();
}
_jsonText.reserve(_jsonText.size() + values.size() + valueBytes + 3);
if (!_jsonText.empty()) {
_jsonText += ',';
}
_jsonText += '[';
bool first = true;
for (auto const& value : values) {
if (!first) {
_jsonText += ',';
}
_jsonText += value.getJson();
first = false;
}
_jsonText += ']';
return *this;
}
JsonString& JsonString::clear() {
_jsonText.clear();
hasKey = false;
return *this;
}
bool JsonString::empty() const {
return _jsonText.empty();
}
const std::string& JsonString::getJsonText() const {
return _jsonText;
}
size_t JsonString::getLength() const {
return _jsonText.length() + ((!empty() && !hasKey) ? 0 : 2);
}
std::string JsonString::getJson() const {
// If not empty with no names (only values), don't add brackets because prob in an array
if (!empty() && !hasKey) {
return _jsonText;
} else {
std::string result;
result.reserve(_jsonText.size() + 2);
result += '{';
result += _jsonText;
result += '}';
return result;
}
}
JsonString& JsonString::copy( const JsonString& jsonString ) {
_jsonText = jsonString._jsonText;
hasKey = jsonString.hasKey;
return *this;
}
JsonString& JsonString::operator=( const JsonString& jsonString ) {
return copy(jsonString);
}
int JsonString::compare( const JsonString& jsonString ) const {
return jsonString._jsonText.compare(_jsonText);
}
bool JsonString::equals( const JsonString& jsonString ) const {
return (compare(jsonString) == 0);
}
JsonStringArray::JsonStringArray() {
}
JsonStringArray::JsonStringArray( const JsonStringArray& arr) : std::vector<JsonString>(arr.begin(), arr.end()) {
}
JsonStringArray::~JsonStringArray() {
}
JsonStringSetter::JsonStringSetter( JsonString& jsonString, const std::string& name ) : _jsonString(jsonString), _name(name) {
}

287
fdbclient/JsonString.h Normal file → Executable file
View File

@ -9,141 +9,190 @@
#include "fdbrpc/JSONDoc.h"
class JsonString;
class JsonStringArray;
class JsonStringSetter;
template <typename T> class JsonStringSetter;
// Class for building JSON string values.
// Default value is 'empty' which is different from JSON null
class JsonString {
public:
JsonString();
JsonString( const JsonString& jsonString);
JsonString( JsonString&& jsonString) = default;
explicit JsonString( const JsonStringArray& jsonArray);
explicit JsonString( const char* value ); // Used to define values (used in an Array)
explicit JsonString( const std::string& value ); // Used to define values (used in an Array)
explicit JsonString( const json_spirit::mObject& value );
public:
enum EType { EMPTY, NULLVALUE, SCALAR, OBJECT, ARRAY, FINAL, DESTROYED };
JsonString(EType type = EType::EMPTY) : type(type)
{
if(type == EType::NULLVALUE) {
write("null");
}
else if(type == EType::ARRAY) {
write("[");
}
else if(type == EType::OBJECT) {
write("{");
}
}
JsonString( const std::string& name, const char* value );
JsonString( const std::string& name, const std::string& value );
JsonString( const std::string& name, double value );
JsonString( const std::string& name, long int value );
JsonString( const std::string& name, long unsigned int value );
JsonString( const std::string& name, long long int value );
JsonString( const std::string& name, long long unsigned int value );
JsonString( const std::string& name, int value );
JsonString( const std::string& name, unsigned value );
JsonString( const std::string& name, bool value );
JsonString( const std::string& name, const JsonString& value );
JsonString( const std::string& name, const JsonStringArray& value );
int empty() const {
return type == EType::EMPTY || (jsonText.size() == 1 && (type == EType::ARRAY || type == EType::OBJECT));
}
JsonString& append( const std::string& name, const char* value );
JsonString& append( const std::string& name, const std::string& value );
JsonString& append( const std::string& name, double value );
JsonString& append( const std::string& name, long int value );
JsonString& append( const std::string& name, long unsigned int value );
JsonString& append( const std::string& name, long long int value );
JsonString& append( const std::string& name, long long unsigned int value );
JsonString& append( const std::string& name, int value );
JsonString& append( const std::string& name, unsigned value );
JsonString& append( const std::string& name, bool value );
JsonString& append( const std::string& name, const JsonString& value );
JsonString& append( const std::string& name, const JsonStringArray& value );
void clear() {
type = EMPTY;
jsonText.clear();
}
JsonString& append( const std::string& value );
JsonString& append( const char* value );
JsonString& append( const JsonStringArray& value );
// Finish the JSON value - close array or object if still open and set final state in either case
// Scalar and null types stay as they are
void finalize() {
ASSERT(type != EType::DESTROYED);
if(type != EType::FINAL) {
if(type == EType::ARRAY) {
write("]");
type = EType::FINAL;
}
else if(type == EType::OBJECT) {
write("}");
type = EType::FINAL;
}
}
}
JsonString& append( double value );
JsonString& append( long int value );
JsonString& append( long unsigned int value );
JsonString& append( long long int value );
JsonString& append( long long unsigned int value );
JsonString& append( int value );
JsonString& append( unsigned value );
JsonString& append( bool value );
JsonString& append( const JsonString& value );
int getFinalLength() {
finalize();
return jsonText.size();
}
JsonStringSetter operator[]( const std::string& name );
JsonStringSetter operator[]( const char* name );
std::string getJson() {
finalize();
return jsonText;
}
int compare( const JsonString& jsonString ) const;
bool equals( const JsonString& jsonString ) const;
bool operator==( const JsonString& jsonString ) const { return equals(jsonString); }
bool operator!=( const JsonString& jsonString ) const { return !equals(jsonString); }
// Can add value to an array or turn an empty into a scalar
template<typename VT> inline JsonString & append(VT &&val) {
if(type == EType::ARRAY) {
if(jsonText.size() != 1)
write(",");
}
else {
ASSERT(type == EType::EMPTY);
type = EType::SCALAR;
}
JsonString& copy( const JsonString& jsonString );
JsonString& operator=( const JsonString& jsonString );
std::string getJson() const;
size_t getLength() const;
JsonString& clear();
bool empty() const;
static JsonString makeMessage(const char *name, const char *description);
protected:
JsonString& appendImpl( const std::string& name, const std::string& value, bool quote);
JsonString& appendImpl( const std::string& value, bool quote);
static std::string stringify(const char* value);
static std::string stringify(double value);
static std::string stringify(long int value);
static std::string stringify(long unsigned int value);
static std::string stringify(long long int value);
static std::string stringify(long long unsigned int value);
static std::string stringify(int value);
static std::string stringify(unsigned value);
static std::string stringify(bool value);
protected:
std::string _jsonText;
bool hasKey;
// Uneditted text
const std::string& getJsonText() const;
};
// Make protected because no virtual destructor
class JsonStringArray : protected std::vector<JsonString>
{
typedef JsonString T;
typedef std::vector<JsonString> vector;
public:
using vector::push_back;
using vector::operator[];
using vector::begin;
using vector::end;
using vector::size;
using vector::empty;
using vector::clear;
public:
JsonStringArray();
JsonStringArray( const JsonStringArray& jsonStringArray);
JsonStringArray( JsonStringArray&& jsonStringArray) = default;
virtual ~JsonStringArray();
};
class JsonStringSetter {
public:
JsonStringSetter( JsonString& jsonString, const std::string& name );
template <class valClass>
JsonStringSetter& operator=( const valClass& value ) {
_jsonString.append(_name, value);
writeValue(val);
return *this;
}
protected:
JsonString& _jsonString;
std::string _name;
template<typename KT, typename VT> inline JsonString & append(KT &&name, VT &&val) {
if(type == EType::EMPTY) {
type = EType::OBJECT;
write("{");
}
else {
ASSERT(type == EType::OBJECT);
if(jsonText.size() != 1)
write(",");
}
write("\"");
write(name);
write("\":");
// The easy alternative to this is using format() which also creates a temporary string, so let's
// let json_spirit handle stringification of native types for now.
writeValue(val);
return *this;
}
template<typename T> inline JsonStringSetter<T> operator[](T &&name);
static JsonString makeMessage(const char *name, const char *description);
protected:
EType type;
std::string jsonText;
template<typename T> inline void write(T &&s) {
jsonText.append(std::forward<T>(s));
}
template<typename T> inline void writeValue(T &&val) {
write(json_spirit::write_string(json_spirit::mValue(val)));
}
};
inline JsonStringSetter JsonString::operator[]( const std::string& name ) {
return JsonStringSetter(*this, name);
// A JsonString is already a proper JSON string (or empty)
template<> inline void JsonString::writeValue(const JsonString &val) {
// Convert empty to null because in this context we must write something.
if(val.type == EType::EMPTY) {
write("null");
}
else {
write(const_cast<JsonString &>(val).getJson());
}
}
inline JsonStringSetter JsonString::operator[]( const char* name ) {
return JsonStringSetter(*this, name);
template<> inline void JsonString::writeValue(JsonString &val) {
writeValue<const JsonString &>(val);
}
// "Deep" append of mObject based on type of this
// Add kv pairs individually for empty or object, add as a closed object for array
template<> inline JsonString & JsonString::append(const json_spirit::mObject &obj) {
if(type == EType::EMPTY || type == EType::OBJECT) {
for(auto &kv : obj) {
append(kv.first, kv.second);
}
}
else {
ASSERT(type == EType::ARRAY);
append(json_spirit::mValue(obj));
}
return *this;
}
// Starts as an Array type and adds push_back interface to appear array-like for writing
class JsonStringArray : protected JsonString {
public:
JsonStringArray() : JsonString(EType::ARRAY) {}
template<typename VT> void push_back(VT &&o) {
append(std::forward<VT>(o));
++count;
}
// Clear but maintain Array-ness
void clear() {
clear();
type = EType::ARRAY;
}
int size() const {
return count;
}
protected:
int count;
};
// A JsonString is already a proper JSON string (or empty)
template<> inline void JsonString::writeValue(JsonStringArray &val) {
writeValue((JsonString &)val);
}
// Template is the key name, accepted as an r-value to avoid copying if it's a string
template<typename KT>
class JsonStringSetter {
public:
JsonStringSetter( JsonString& dest, KT &&name) : dest(dest), name(std::forward<KT>(name)) {}
template <class VT> inline JsonStringSetter& operator=(VT &&value) {
dest.append(name, std::forward<VT>(value));
return *this;
}
protected:
JsonString& dest;
KT name;
};
template<typename T> inline JsonStringSetter<T> JsonString::operator[](T &&name) {
return JsonStringSetter<T>(*this, std::forward<T>(name));
}
#endif

5
fdbclient/Status.h Normal file → Executable file
View File

@ -23,6 +23,11 @@
#include "../fdbrpc/JSONDoc.h"
// Reads the entire string s as a JSON value
// Throws if no value can be parsed or if s contains data after the first JSON value
// Trailing whitespace in s is allowed
json_spirit::mValue readJSONStrictly(const std::string s);
struct StatusObject : json_spirit::mObject {
typedef json_spirit::mObject Map;
typedef json_spirit::mArray Array;

73
fdbserver/Status.actor.cpp Normal file → Executable file
View File

@ -32,6 +32,23 @@
#include "flow/UnitTest.h"
#include "QuietDatabase.h"
#include "RecoveryState.h"
#include "fdbclient/JsonString.h"
json_spirit::mValue readJSONStrictly(const std::string s) {
json_spirit::mValue val;
std::string::const_iterator i = s.begin();
if(!json_spirit::read_range(i, s.end(), val))
throw json_malformed();
// Allow trailing whitespace
while(i != s.end()) {
if(!isspace(*i))
throw json_eof_expected();
++i;
}
return val;
}
const char* RecoveryStatus::names[] = {
"reading_coordinated_state", "locking_coordinated_state", "locking_old_transaction_servers", "reading_transaction_system_state",
@ -233,7 +250,7 @@ static JsonString getLocalityInfo(const LocalityData& locality) {
localityObj[it->first.toString()] = it->second.get().toString();
}
else {
localityObj[it->first.toString()] = JsonString();
localityObj[it->first.toString()] = JsonString(JsonString::NULLVALUE);
}
}
@ -1075,7 +1092,7 @@ static JsonString configurationFetcher(Optional<DatabaseConfiguration> conf, Ser
try {
if(conf.present()) {
DatabaseConfiguration configuration = conf.get();
statusObj = JsonString( configuration.toJSON() );
statusObj.append(configuration.toJSON());
JsonStringArray excludedServersArr;
std::set<AddressExclusion> excludedServers = configuration.getExcludedServers();
@ -1623,7 +1640,7 @@ static JsonStringArray getClientIssuesAsMessages( ProcessIssuesMap const& _issue
JsonString message = JsonString::makeMessage(i.first.c_str(), getIssueDescription(i.first).c_str());
JsonStringArray addresses;
for(auto addr : i.second) {
addresses.push_back(JsonString(addr));
addresses.push_back(addr);
}
message["addresses"] = addresses;
@ -1692,8 +1709,9 @@ ACTOR Future<JsonString> layerStatusFetcher(Database cx, JsonStringArray *messag
}
json.cleanOps();
JsonString statusObj = JsonString( result );
TraceEvent("LayerStatusFetcher").detail("Duration", now()-tStart).detail("StatusSize",statusObj.getLength());
JsonString statusObj;
statusObj.append(result);
TraceEvent("LayerStatusFetcher").detail("Duration", now()-tStart).detail("StatusSize",statusObj.getFinalLength());
return statusObj;
}
@ -1800,7 +1818,7 @@ ACTOR Future<StatusReply> clusterGetStatus(
JsonString message = JsonString::makeMessage("unreachable_processes", "The cluster has some unreachable processes.");
JsonStringArray unreachableProcs;
for (auto m : mergeUnreachable){
unreachableProcs.push_back(JsonString("address", m));
unreachableProcs.push_back(JsonString().append("address", m));
}
message["unreachable_processes"] = unreachableProcs;
messages.push_back(message);
@ -1931,9 +1949,10 @@ ACTOR Future<StatusReply> clusterGetStatus(
}
else {
// Set layers status to { _valid: false, error: "configurationMissing"}
JsonString jsonString("_valid", false);
jsonString.append("_error", "configurationMissing");
statusObj["layers"] = jsonString;
JsonString layers;
layers.append("_valid", false);
layers.append("_error", "configurationMissing");
statusObj["layers"] = layers;
}
JsonString processStatus = wait(processStatusFetcher(db, workers, pMetrics, mMetrics, latestError, traceFileOpenErrors, programStarts, processIssues, storageServers, tLogs, cx, configuration, &status_incomplete_reasons));
@ -1942,7 +1961,7 @@ ACTOR Future<StatusReply> clusterGetStatus(
JsonStringArray incompatibleConnectionsArray;
for(auto it : incompatibleConnections) {
incompatibleConnectionsArray.push_back(JsonString(it.toString()));
incompatibleConnectionsArray.push_back(it.toString());
}
statusObj["incompatible_connections"] = incompatibleConnectionsArray;
statusObj["datacenter_version_difference"] = datacenterVersionDifference;
@ -1965,7 +1984,7 @@ ACTOR Future<StatusReply> clusterGetStatus(
// Make a JSON array of all of the reasons in the status_incomplete_reasons set.
JsonStringArray reasons;
for (auto i : status_incomplete_reasons) {
reasons.push_back(JsonString("description", i));
reasons.push_back(JsonString().append("description", i));
}
incomplete_message["reasons"] = reasons;
messages.push_back(incomplete_message);
@ -1978,9 +1997,9 @@ ACTOR Future<StatusReply> clusterGetStatus(
statusObj["cluster_controller_timestamp"] = clusterTime;
}
TraceEvent("ClusterGetStatus").detail("Duration", timer()-tStart).detail("StatusSize",statusObj.getLength());
TraceEvent("ClusterGetStatus").detail("Duration", timer()-tStart).detail("StatusSize",statusObj.getFinalLength());
return StatusReply(statusObj);
return StatusReply(statusObj.getJson());
} catch( Error&e ) {
TraceEvent(SevError, "StatusError").error(e);
throw;
@ -1995,30 +2014,32 @@ TEST_CASE("status/json/jsonstring") {
jsonString1["_valid"] = false;
jsonString1["error"] = "configurationMissing";
JsonString jsonString2("_valid", false);
JsonString jsonString2;
jsonString2.append("_valid", false);
jsonString2["error"] = "configurationMissing";
JsonString jsonString3("error", "configurationMissing");
JsonString jsonString3;
jsonString3.append("error", "configurationMissing");
jsonString3["_valid"] = false;
JsonString jsonString4("_valid", false);
JsonString jsonString4;
jsonString4.append("_valid", false);
jsonString4.append("error", "configurationMissing");
std::cout << "Json: " << jsonString1.getJson() << std::endl;
// Validate equality
ASSERT(jsonString1 == jsonString2);
ASSERT(jsonString1 != jsonString3); // wrong order
ASSERT(jsonString1.equals(jsonString4));
ASSERT(jsonString4.equals(jsonString1));
ASSERT(jsonString1.getJson() == jsonString2.getJson());
ASSERT(jsonString1.getJson() != jsonString3.getJson()); // wrong order
ASSERT(jsonString1.getJson() == jsonString4.getJson());
// Check empty
ASSERT(!jsonString1.empty());
ASSERT(JsonString().empty());
ASSERT(!jsonString1.getJson().empty());
ASSERT(JsonString().getJson().empty());
// Check clear
jsonString1.clear();
ASSERT(jsonString1.empty());
ASSERT(jsonString1.getJson().empty());
// Set object
jsonObj.clear();
@ -2047,9 +2068,9 @@ TEST_CASE("status/json/jsonstring") {
std::cout << "Json (complex array): " << jsonObj.getJson() << std::endl;
jsonArray.clear();
jsonArray.push_back(JsonString("nissan"));
jsonArray.push_back(JsonString("honda"));
jsonArray.push_back(JsonString("bmw"));
jsonArray.push_back("nissan");
jsonArray.push_back("honda");
jsonArray.push_back("bmw");
jsonObj.clear();
jsonObj.append(jsonArray);
std::cout << "Array (simple array #1): " << jsonObj.getJson() << std::endl;

9
fdbserver/workloads/StatusWorkload.actor.cpp Normal file → Executable file
View File

@ -43,8 +43,7 @@ struct StatusWorkload : TestWorkload {
requestsPerSecond = getOption(options, LiteralStringRef("requestsPerSecond"), 0.5);
auto statusSchemaStr = getOption(options, LiteralStringRef("schema"), JSONSchemas::statusSchema);
if (statusSchemaStr.size()) {
json_spirit::mValue schema;
json_spirit::read_string( statusSchemaStr.toString(), schema );
json_spirit::mValue schema = readJSONStrictly(statusSchemaStr.toString());
parsedSchema = schema.get_obj();
// This is sort of a hack, but generate code coverage *requirements* for everything in schema
@ -142,12 +141,10 @@ struct StatusWorkload : TestWorkload {
WorkloadFactory<StatusWorkload> StatusWorkloadFactory("Status");
TEST_CASE("fdbserver/status/schema/basic") {
json_spirit::mValue schema;
json_spirit::read_string( std::string("{\"apple\":3,\"banana\":\"foo\",\"sub\":{\"thing\":true},\"arr\":[{\"a\":1,\"b\":2}],\"en\":{\"$enum\":[\"foo\",\"bar\"]},\"mapped\":{\"$map\":{\"x\":true}}"), schema );
json_spirit::mValue schema = readJSONStrictly("{\"apple\":3,\"banana\":\"foo\",\"sub\":{\"thing\":true},\"arr\":[{\"a\":1,\"b\":2}],\"en\":{\"$enum\":[\"foo\",\"bar\"]},\"mapped\":{\"$map\":{\"x\":true}}");
auto check = [&schema](bool expect_ok, std::string t) {
json_spirit::mValue test;
json_spirit::read_string( t, test );
json_spirit::mValue test = readJSONStrictly(t);
TraceEvent("SchemaMatch").detail("Schema", json_spirit::write_string(schema)).detail("Value", json_spirit::write_string(test)).detail("Expect", expect_ok);
std::string errorStr;
ASSERT( expect_ok == schemaMatch(schema.get_obj(), test.get_obj(), errorStr, expect_ok ? SevError : SevInfo, true) );

2
flow/error_definitions.h Normal file → Executable file
View File

@ -192,6 +192,8 @@ ERROR( task_invalid_version, 2381, "Invalid task version")
ERROR( task_interrupted, 2382, "Task execution stopped due to timeout, abort, or completion by another worker")
ERROR( key_not_found, 2400, "Expected key is missing")
ERROR( json_malformed, 2401, "JSON string was malformed")
ERROR( json_eof_expected, 2402, "JSON string did not terminate where expected")
// 4xxx Internal errors (those that should be generated only by bugs) are decimal 4xxx
ERROR( unknown_error, 4000, "An unknown error occurred" ) // C++ exception not of type Error