]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
erasure-code: high level LRC configuration
authorLoic Dachary <loic@dachary.org>
Fri, 15 Aug 2014 16:33:38 +0000 (18:33 +0200)
committerLoic Dachary <loic-201408@dachary.org>
Fri, 29 Aug 2014 17:38:57 +0000 (19:38 +0200)
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 <loic@dachary.org>
src/erasure-code/LRC/ErasureCodeLRC.cc
src/erasure-code/LRC/ErasureCodeLRC.h
src/test/erasure-code/TestErasureCodeLRC.cc

index 02d22ceac5132774228dc247e503906be451708b..99d1147f333f0f0cf8e9dcaff8f41e4ff1c44b06 100644 (file)
@@ -277,6 +277,112 @@ int ErasureCodeLRC::parse(const map<string,string> &parameters,
   return parse_ruleset(parameters, ss);
 }
 
+int ErasureCodeLRC::parse_kml(map<string,string> &parameters,
+                             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<string,string>::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<string,string> &parameters,
                                  ostream *ss)
 {
@@ -373,16 +479,21 @@ int ErasureCodeLRC::init(const map<string,string> &parameters,
 {
   int r;
 
-  r = parse(parameters, ss);
+  map<string,string> 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<string,string> &parameters,
   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')
index f12f429bb8b2d91161e3d7c090f470afc4dff900..3ad592cce983f449c3512ac384d070aea5d6bb16 100644 (file)
 #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<string,string> &parameters, ostream *ss);
 
+  int parse_kml(map<string,string> &parameters, ostream *ss);
+
   int parse_ruleset(const map<string,string> &parameters, ostream *ss);
 
   int parse_ruleset_step(string description_string,
index 02e6082e4d146304055bfb15a5fdc1d5eba6d163..6fcd8a064f9534ec5b02bc5c7d4ac65f373ea45b 100644 (file)
@@ -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<std::string,std::string> 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<std::string,std::string> 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