YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/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
429
bool IsReplicaPlacementOption(const string& option) {
29
429
  return boost::starts_with(option, "replica_placement");
30
429
}
31
32
} // namespace
33
34
Result<PlacementInfoConverter::Placement> PlacementInfoConverter::FromJson(
35
426
    const string& placement_str, const rapidjson::Document& placement) {
36
426
  std::vector<PlacementInfo> placement_infos;
37
38
  // Parse the number of replicas
39
426
  if (!placement.HasMember("num_replicas") || 
!placement["num_replicas"].IsInt()425
) {
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
423
  const int num_replicas = placement["num_replicas"].GetInt();
45
46
  // Parse the placement blocks.
47
423
  if (!placement.HasMember("placement_blocks") ||
48
423
      
!placement["placement_blocks"].IsArray()422
) {
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
422
  const rapidjson::Value& pb = placement["placement_blocks"];
55
422
  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
422
  int64 total_min_replicas = 0;
62
1.16k
  for (rapidjson::SizeType i = 0; i < pb.Size(); 
++i738
) {
63
739
    const rapidjson::Value& placement = pb[i];
64
739
    if (!placement.HasMember("cloud") || !placement.HasMember("region") ||
65
739
        !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
739
    if (!placement["cloud"].IsString() || !placement["region"].IsString() ||
70
739
        !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
738
    const int min_rf = placement["min_num_replicas"].GetInt();
75
738
    placement_infos.emplace_back(PlacementInfo {
76
738
      .cloud = placement["cloud"].GetString(),
77
738
      .region = placement["region"].GetString(),
78
738
      .zone = placement["zone"].GetString(),
79
738
      .min_num_replicas = min_rf,
80
738
    });
81
738
    total_min_replicas += min_rf;
82
738
  }
83
421
  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
421
  return PlacementInfoConverter::Placement{
89
421
    .placement_infos = std::move(placement_infos),
90
421
    .num_replicas = num_replicas,
91
421
  };
92
421
}
93
94
// TODO(#10869): improve JSON parsing
95
Result<PlacementInfoConverter::Placement> PlacementInfoConverter::FromString(
96
429
    const std::string& placement) {
97
429
  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
429
  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
428
  vector<string> split;
112
428
  split = strings::Split(placement, "replica_placement=", strings::SkipEmpty());
113
428
  if (split.size() != 1) {
114
0
    return STATUS_FORMAT(Corruption, "replica_placement option illformed: $0", placement);
115
0
  }
116
428
  auto replica_placement = split[0].c_str();
117
118
428
  if (document.Parse(replica_placement).HasParseError() || 
!document.IsObject()427
) {
119
2
    return STATUS_FORMAT(Corruption,
120
2
                         "Json parsing of replica placement option failed: $0", split[0]);
121
2
  }
122
123
426
  return FromJson(replica_placement, document);
124
428
}
125
126
Result<PlacementInfoConverter::Placement> PlacementInfoConverter::FromQLValue(
127
430
    const vector<QLValuePB>& placement) {
128
  // Today only one option is supported, so this array should have only one option.
129
430
  if (placement.size() != 1) {
130
2
    return STATUS_FORMAT(Corruption,
131
2
                         "Unexpected number of options: $0", placement.size());
132
2
  }
133
134
428
  const string& option = placement[0].string_value();
135
428
  return FromString(option);
136
430
}
137
138
} // namespace yb