YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/common/placement_info.cc
Line
Count
Source (jump to first uncovered line)
1
//--------------------------------------------------------------------------------------------------
2
// Copyright (c) Yugabyte, Inc.
3
//
4
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5
// in compliance with the License.  You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software distributed under the License
10
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11
// or implied.  See the License for the specific language governing permissions and limitations
12
// under the License.
13
//--------------------------------------------------------------------------------------------------
14
15
#include "yb/common/placement_info.h"
16
17
#include <boost/algorithm/string/predicate.hpp>
18
19
#include <rapidjson/document.h>
20
21
#include "yb/gutil/strings/split.h"
22
#include "yb/util/status.h"
23
24
namespace yb {
25
26
namespace {
27
28
9
bool IsReplicaPlacementOption(const string& option) {
29
9
  return boost::starts_with(option, "replica_placement");
30
9
}
31
32
} // namespace
33
34
Result<PlacementInfoConverter::Placement> PlacementInfoConverter::FromJson(
35
6
    const string& placement_str, const rapidjson::Document& placement) {
36
6
  std::vector<PlacementInfo> placement_infos;
37
38
  // Parse the number of replicas
39
6
  if (!placement.HasMember("num_replicas") || !placement["num_replicas"].IsInt()) {
40
3
    return STATUS_FORMAT(Corruption,
41
3
                         "Invalid value found for \"num_replicas\" field in the placement "
42
3
                         "policy: $0", placement_str);
43
3
  }
44
3
  const int num_replicas = placement["num_replicas"].GetInt();
45
46
  // Parse the placement blocks.
47
3
  if (!placement.HasMember("placement_blocks") ||
48
2
      !placement["placement_blocks"].IsArray()) {
49
1
    return STATUS_FORMAT(Corruption,
50
1
                         "\"placement_blocks\" field not found in the placement policy: $0",
51
1
                         placement_str);
52
1
  }
53
54
2
  const rapidjson::Value& pb = placement["placement_blocks"];
55
2
  if (pb.Size() < 1) {
56
0
    return STATUS_FORMAT(Corruption,
57
0
                         "\"placement_blocks\" field has empty value in the placement "
58
0
                         "policy: $0", placement_str);
59
0
  }
60
61
2
  int64 total_min_replicas = 0;
62
4
  for (rapidjson::SizeType i = 0; i < pb.Size(); ++i) {
63
3
    const rapidjson::Value& placement = pb[i];
64
3
    if (!placement.HasMember("cloud") || !placement.HasMember("region") ||
65
3
        !placement.HasMember("zone") || !placement.HasMember("min_num_replicas")) {
66
0
      return STATUS_FORMAT(Corruption,
67
0
                           "Missing keys in replica placement option: $0", placement_str);
68
0
    }
69
3
    if (!placement["cloud"].IsString() || !placement["region"].IsString() ||
70
3
        !placement["zone"].IsString() || !placement["min_num_replicas"].IsInt()) {
71
1
      return STATUS_FORMAT(Corruption,
72
1
                           "Invalid value for replica_placement option: $0", placement_str);
73
1
    }
74
2
    const int min_rf = placement["min_num_replicas"].GetInt();
75
2
    placement_infos.emplace_back(PlacementInfo {
76
2
      .cloud = placement["cloud"].GetString(),
77
2
      .region = placement["region"].GetString(),
78
2
      .zone = placement["zone"].GetString(),
79
2
      .min_num_replicas = min_rf,
80
2
    });
81
2
    total_min_replicas += min_rf;
82
2
  }
83
1
  if (total_min_replicas > num_replicas) {
84
0
    return STATUS_FORMAT(Corruption,
85
0
                         "Sum of min_num_replicas fields exceeds the total replication factor "
86
0
                         "in the placement policy: $0", placement_str);
87
0
  }
88
1
  return PlacementInfoConverter::Placement{
89
1
    .placement_infos = std::move(placement_infos),
90
1
    .num_replicas = num_replicas,
91
1
  };
92
1
}
93
94
// TODO(#10869): improve JSON parsing
95
Result<PlacementInfoConverter::Placement> PlacementInfoConverter::FromString(
96
9
    const std::string& placement) {
97
9
  rapidjson::Document document;
98
99
  // The only tablespace option supported today is "replica_placement" that allows specification
100
  // of placement policies encoded as a JSON array. Example value:
101
  // replica_placement=
102
  //   '{"num_replicas":3, "placement_blocks": [
103
  //         {"cloud":"c1", "region":"r1", "zone":"z1", "min_num_replicas":1},
104
  //         {"cloud":"c2", "region":"r2", "zone":"z2", "min_num_replicas":1},
105
  //         {"cloud":"c3", "region":"r3", "zone":"z3", "min_num_replicas":1}]}'
106
9
  if (!IsReplicaPlacementOption(placement)) {
107
1
    return STATUS(InvalidArgument, "Invalid option found in spcoptions: $0", placement);
108
1
  }
109
110
  // First split the string and get only the json value in a string.
111
8
  vector<string> split;
112
8
  split = strings::Split(placement, "replica_placement=", strings::SkipEmpty());
113
8
  if (split.size() != 1) {
114
0
    return STATUS_FORMAT(Corruption, "replica_placement option illformed: $0", placement);
115
0
  }
116
8
  auto replica_placement = split[0].c_str();
117
118
8
  if (document.Parse(replica_placement).HasParseError() || !document.IsObject()) {
119
2
    return STATUS_FORMAT(Corruption,
120
2
                         "Json parsing of replica placement option failed: $0", split[0]);
121
2
  }
122
123
6
  return FromJson(replica_placement, document);
124
6
}
125
126
Result<PlacementInfoConverter::Placement> PlacementInfoConverter::FromQLValue(
127
11
    const vector<QLValuePB>& placement) {
128
  // Today only one option is supported, so this array should have only one option.
129
11
  if (placement.size() != 1) {
130
2
    return STATUS_FORMAT(Corruption,
131
2
                         "Unexpected number of options: $0", placement.size());
132
2
  }
133
134
9
  const string& option = placement[0].string_value();
135
9
  return FromString(option);
136
9
}
137
138
} // namespace yb