From: Loic Dachary Date: Fri, 15 Aug 2014 16:33:38 +0000 (+0200) Subject: erasure-code: high level LRC configuration X-Git-Tag: v0.86~169^2~3 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=c22260175de6d05db45863598abb932753b26623;p=ceph.git erasure-code: high level LRC configuration Add a configuration mode to the LRC plugin, suitable for most use cases and simpler to explain and understand. It uses k,m parameters similar to the jerasure and isa plugins and adds l for locality. For instance: k=4 m=2 l=3 ruleset_locality=rack ruleset_failure_domain=host Will translate into local_group_count = (k+m)/l ruleset-steps = "[" " [\"choose\", \"rack\", " + local_group_count + "], " " [\"chooseleaf\", \"host\", " + (l + 1) + "], " "]"; layers = "[ " " [ \"DDc_DDc_\", \"\" ]," " [ \"DDDc____\", \"\" ]," " [ \"____DDDc\", \"\" ]," "]"; It is less flexible because k+m must be a multiple of l and other similar relationships and also because it cannot express recursive configurations with locality in the datacenter and locality in a rack within the datacenter. Signed-off-by: Loic Dachary --- diff --git a/src/erasure-code/LRC/ErasureCodeLRC.cc b/src/erasure-code/LRC/ErasureCodeLRC.cc index 02d22ceac513..99d1147f333f 100644 --- a/src/erasure-code/LRC/ErasureCodeLRC.cc +++ b/src/erasure-code/LRC/ErasureCodeLRC.cc @@ -277,6 +277,112 @@ int ErasureCodeLRC::parse(const map ¶meters, return parse_ruleset(parameters, ss); } +int ErasureCodeLRC::parse_kml(map ¶meters, + ostream *ss) +{ + int err = ErasureCode::parse(parameters, ss); + const int DEFAULT = -1; + int k, m, l; + err |= to_int("k", parameters, &k, DEFAULT, ss); + err |= to_int("m", parameters, &m, DEFAULT, ss); + err |= to_int("l", parameters, &l, DEFAULT, ss); + + if (k == DEFAULT && m == DEFAULT && l == DEFAULT) + return 0; + + if ((k != DEFAULT || m != DEFAULT || l != DEFAULT) && + (k == DEFAULT || m == DEFAULT || l == DEFAULT)) { + *ss << "All of k, m, l must be set or none of them in " + << parameters << std::endl; + return ERROR_LRC_ALL_OR_NOTHING; + } + + const char *generated[] = { "mapping", + "layers", + "ruleset-steps" }; + + for (int i = 0; i < 3; i++) { + if (parameters.count(generated[i])) { + *ss << "The " << generated[i] << " parameter cannot be set " + << "when k, m, l are set in " << parameters << std::endl; + return ERROR_LRC_GENERATED; + } + } + + if ((k + m) % l) { + *ss << "k + m must be a multiple of l in " + << parameters << std::endl; + return ERROR_LRC_K_M_MODULO; + } + + int local_group_count = (k + m) / l; + + if (k % local_group_count) { + *ss << "k must be a multiple of (k + m) / l in " + << parameters << std::endl; + return ERROR_LRC_K_MODULO; + } + + if (m % local_group_count) { + *ss << "m must be a multiple of (k + m) / l in " + << parameters << std::endl; + return ERROR_LRC_M_MODULO; + } + + string mapping; + for (int i = 0; i < local_group_count; i++) { + mapping += string(k / local_group_count, 'D') + + string(m / local_group_count, '_') + "_"; + } + parameters["mapping"] = mapping; + + string layers = "[ "; + + // global layer + layers += " [ \""; + for (int i = 0; i < local_group_count; i++) { + layers += string(k / local_group_count, 'D') + + string(m / local_group_count, 'c') + "_"; + } + layers += "\", \"\" ],"; + + // local layers + for (int i = 0; i < local_group_count; i++) { + layers += " [ \""; + for (int j = 0; j < local_group_count; j++) { + if (i == j) + layers += string(l, 'D') + "c"; + else + layers += string(l + 1, '_'); + } + layers += "\", \"\" ],"; + } + parameters["layers"] = layers + "]"; + + map::const_iterator parameter; + string ruleset_locality; + parameter = parameters.find("ruleset-locality"); + if (parameter != parameters.end()) + ruleset_locality = parameter->second; + string ruleset_failure_domain = "host"; + parameter = parameters.find("ruleset-failure-domain"); + if (parameter != parameters.end()) + ruleset_failure_domain = parameter->second; + + if (ruleset_locality != "") { + ruleset_steps.clear(); + ruleset_steps.push_back(Step("choose", ruleset_locality, + local_group_count)); + ruleset_steps.push_back(Step("chooseleaf", ruleset_failure_domain, + l + 1)); + } else if (ruleset_failure_domain != "") { + ruleset_steps.clear(); + ruleset_steps.push_back(Step("chooseleaf", ruleset_failure_domain, 0)); + } + + return 0; +} + int ErasureCodeLRC::parse_ruleset(const map ¶meters, ostream *ss) { @@ -373,16 +479,21 @@ int ErasureCodeLRC::init(const map ¶meters, { int r; - r = parse(parameters, ss); + map parameters_rw = parameters; + r = parse_kml(parameters_rw, ss); + if (r) + return r; + + r = parse(parameters_rw, ss); if (r) return r; json_spirit::mArray description; - r = layers_description(parameters, &description, ss); + r = layers_description(parameters_rw, &description, ss); if (r) return r; - string description_string = parameters.find("layers")->second; + string description_string = parameters_rw.find("layers")->second; dout(10) << "init(" << description_string << ")" << dendl; @@ -394,11 +505,11 @@ int ErasureCodeLRC::init(const map ¶meters, if (r) return r; - if (parameters.count("mapping") == 0) { - *ss << "the 'mapping' parameter is missing from " << parameters; + if (parameters_rw.count("mapping") == 0) { + *ss << "the 'mapping' parameter is missing from " << parameters_rw; return ERROR_LRC_MAPPING; } - string mapping = parameters.find("mapping")->second; + string mapping = parameters_rw.find("mapping")->second; data_chunk_count = 0; for(std::string::iterator it = mapping.begin(); it != mapping.end(); ++it) { if (*it == 'D') diff --git a/src/erasure-code/LRC/ErasureCodeLRC.h b/src/erasure-code/LRC/ErasureCodeLRC.h index f12f429bb8b2..3ad592cce983 100644 --- a/src/erasure-code/LRC/ErasureCodeLRC.h +++ b/src/erasure-code/LRC/ErasureCodeLRC.h @@ -37,6 +37,11 @@ #define ERROR_LRC_RULESET_OP -(MAX_ERRNO + 14) #define ERROR_LRC_RULESET_TYPE -(MAX_ERRNO + 15) #define ERROR_LRC_RULESET_N -(MAX_ERRNO + 16) +#define ERROR_LRC_ALL_OR_NOTHING -(MAX_ERRNO + 17) +#define ERROR_LRC_GENERATED -(MAX_ERRNO + 18) +#define ERROR_LRC_K_M_MODULO -(MAX_ERRNO + 19) +#define ERROR_LRC_K_MODULO -(MAX_ERRNO + 20) +#define ERROR_LRC_M_MODULO -(MAX_ERRNO + 21) class ErasureCodeLRC : public ErasureCode { public: @@ -113,6 +118,8 @@ public: virtual int parse(const map ¶meters, ostream *ss); + int parse_kml(map ¶meters, ostream *ss); + int parse_ruleset(const map ¶meters, ostream *ss); int parse_ruleset_step(string description_string, diff --git a/src/test/erasure-code/TestErasureCodeLRC.cc b/src/test/erasure-code/TestErasureCodeLRC.cc index 02e6082e4d14..6fcd8a064f95 100644 --- a/src/test/erasure-code/TestErasureCodeLRC.cc +++ b/src/test/erasure-code/TestErasureCodeLRC.cc @@ -166,6 +166,81 @@ TEST(ErasureCodeTest, create_ruleset) EXPECT_EQ(second_rack, out[9] / num_host / num_osd); } +TEST(ErasureCodeLRC, parse_kml) +{ + ErasureCodeLRC LRC; + map parameters; + EXPECT_EQ(0, LRC.parse_kml(parameters, &cerr)); + parameters["k"] = "4"; + EXPECT_EQ(ERROR_LRC_ALL_OR_NOTHING, LRC.parse_kml(parameters, &cerr)); + const char *generated[] = { "mapping", + "layers", + "ruleset-steps" }; + parameters["m"] = "2"; + parameters["l"] = "3"; + + for (int i = 0; i < 3; i++) { + parameters[generated[i]] = "SET"; + EXPECT_EQ(ERROR_LRC_GENERATED, LRC.parse_kml(parameters, &cerr)); + parameters.erase(parameters.find(generated[i])); + } + + parameters["k"] = "4"; + parameters["m"] = "2"; + parameters["l"] = "7"; + EXPECT_EQ(ERROR_LRC_K_M_MODULO, LRC.parse_kml(parameters, &cerr)); + + parameters["k"] = "3"; + parameters["m"] = "3"; + parameters["l"] = "3"; + EXPECT_EQ(ERROR_LRC_K_MODULO, LRC.parse_kml(parameters, &cerr)); + + parameters["k"] = "4"; + parameters["m"] = "2"; + parameters["l"] = "3"; + EXPECT_EQ(0, LRC.parse_kml(parameters, &cerr)); + EXPECT_EQ("[ " + " [ \"DDc_DDc_\", \"\" ]," + " [ \"DDDc____\", \"\" ]," + " [ \"____DDDc\", \"\" ]," + "]", parameters["layers"]); + EXPECT_EQ("DD__DD__", parameters["mapping"]); + EXPECT_EQ("chooseleaf", LRC.ruleset_steps[0].op); + EXPECT_EQ("host", LRC.ruleset_steps[0].type); + EXPECT_EQ(0, LRC.ruleset_steps[0].n); + EXPECT_EQ(1U, LRC.ruleset_steps.size()); + parameters.erase(parameters.find("mapping")); + parameters.erase(parameters.find("layers")); + + parameters["k"] = "4"; + parameters["m"] = "2"; + parameters["l"] = "3"; + parameters["ruleset-failure-domain"] = "osd"; + EXPECT_EQ(0, LRC.parse_kml(parameters, &cerr)); + EXPECT_EQ("chooseleaf", LRC.ruleset_steps[0].op); + EXPECT_EQ("osd", LRC.ruleset_steps[0].type); + EXPECT_EQ(0, LRC.ruleset_steps[0].n); + EXPECT_EQ(1U, LRC.ruleset_steps.size()); + parameters.erase(parameters.find("mapping")); + parameters.erase(parameters.find("layers")); + + parameters["k"] = "4"; + parameters["m"] = "2"; + parameters["l"] = "3"; + parameters["ruleset-failure-domain"] = "osd"; + parameters["ruleset-locality"] = "rack"; + EXPECT_EQ(0, LRC.parse_kml(parameters, &cerr)); + EXPECT_EQ("choose", LRC.ruleset_steps[0].op); + EXPECT_EQ("rack", LRC.ruleset_steps[0].type); + EXPECT_EQ(2, LRC.ruleset_steps[0].n); + EXPECT_EQ("chooseleaf", LRC.ruleset_steps[1].op); + EXPECT_EQ("osd", LRC.ruleset_steps[1].type); + EXPECT_EQ(4, LRC.ruleset_steps[1].n); + EXPECT_EQ(2U, LRC.ruleset_steps.size()); + parameters.erase(parameters.find("mapping")); + parameters.erase(parameters.find("layers")); +} + TEST(ErasureCodeLRC, layers_description) { ErasureCodeLRC LRC; @@ -360,6 +435,18 @@ TEST(ErasureCodeLRC, init) EXPECT_EQ(0, LRC.init(parameters, &cerr)); } +TEST(ErasureCodeLRC, init_kml) +{ + ErasureCodeLRC LRC; + map parameters; + parameters["k"] = "4"; + parameters["m"] = "2"; + parameters["l"] = "3"; + parameters["directory"] = ".libs"; + EXPECT_EQ(0, LRC.init(parameters, &cerr)); + EXPECT_EQ((unsigned int)(4 + 2 + (4 + 2) / 3), LRC.get_chunk_count()); +} + TEST(ErasureCodeLRC, minimum_to_decode) { // trivial : no erasures, the minimum is want_to_read