/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 |