Files
greptimedb/src/cli/metadata/repair/partition_column.rs.html
2026-05-19 06:04:59 +00:00

327 lines
38 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Source of the Rust file `src/cli/src/metadata/repair/partition_column.rs`."><title>partition_column.rs - source</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../../" data-static-root-path="../../../../static.files/" data-current-crate="cli" data-themes="" data-resource-suffix="" data-rustdoc-version="1.96.0-nightly (ac7f9ec7d 2026-03-20)" data-channel="nightly" data-search-js="search-63369b7b.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../../static.files/storage-41dd4d93.js"></script><script defer src="../../../../static.files/src-script-813739b1.js"></script><script defer src="../../../../src-files.js"></script><script defer src="../../../../static.files/main-5013f961.js"></script><noscript><link rel="stylesheet" href="../../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../../static.files/favicon-044be391.svg"></head><body class="rustdoc src"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="src-sidebar-title"><h2>Files</h2></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><h1><div class="sub-heading">cli/metadata/repair/</div>partition_column.rs</h1><rustdoc-toolbar></rustdoc-toolbar></div><div class="example-wrap digits-3"><pre class="rust"><code><a href=#1 id=1 data-nosnippet>1</a><span class="comment">// Copyright 2023 Greptime Team
<a href=#2 id=2 data-nosnippet>2</a>//
<a href=#3 id=3 data-nosnippet>3</a>// Licensed under the Apache License, Version 2.0 (the "License");
<a href=#4 id=4 data-nosnippet>4</a>// you may not use this file except in compliance with the License.
<a href=#5 id=5 data-nosnippet>5</a>// You may obtain a copy of the License at
<a href=#6 id=6 data-nosnippet>6</a>//
<a href=#7 id=7 data-nosnippet>7</a>// http://www.apache.org/licenses/LICENSE-2.0
<a href=#8 id=8 data-nosnippet>8</a>//
<a href=#9 id=9 data-nosnippet>9</a>// Unless required by applicable law or agreed to in writing, software
<a href=#10 id=10 data-nosnippet>10</a>// distributed under the License is distributed on an "AS IS" BASIS,
<a href=#11 id=11 data-nosnippet>11</a>// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
<a href=#12 id=12 data-nosnippet>12</a>// See the License for the specific language governing permissions and
<a href=#13 id=13 data-nosnippet>13</a>// limitations under the License.
<a href=#14 id=14 data-nosnippet>14</a>
<a href=#15 id=15 data-nosnippet>15</a></span><span class="kw">use </span>std::collections::{HashMap, HashSet};
<a href=#16 id=16 data-nosnippet>16</a>
<a href=#17 id=17 data-nosnippet>17</a><span class="kw">use </span>async_trait::async_trait;
<a href=#18 id=18 data-nosnippet>18</a><span class="kw">use </span>clap::Parser;
<a href=#19 id=19 data-nosnippet>19</a><span class="kw">use </span>common_error::ext::BoxedError;
<a href=#20 id=20 data-nosnippet>20</a><span class="kw">use </span>common_meta::key::table_info::{TableInfoKey, TableInfoValue};
<a href=#21 id=21 data-nosnippet>21</a><span class="kw">use </span>common_meta::key::table_route::{TableRouteKey, TableRouteValue};
<a href=#22 id=22 data-nosnippet>22</a><span class="kw">use </span>common_meta::key::{MetadataKey, MetadataValue};
<a href=#23 id=23 data-nosnippet>23</a><span class="kw">use </span>common_meta::kv_backend::KvBackendRef;
<a href=#24 id=24 data-nosnippet>24</a><span class="kw">use </span>common_meta::range_stream::{DEFAULT_PAGE_SIZE, PaginationStream};
<a href=#25 id=25 data-nosnippet>25</a><span class="kw">use </span>common_meta::rpc::store::{PutRequest, RangeRequest};
<a href=#26 id=26 data-nosnippet>26</a><span class="kw">use </span>common_telemetry::{info, warn};
<a href=#27 id=27 data-nosnippet>27</a><span class="kw">use </span>futures::StreamExt;
<a href=#28 id=28 data-nosnippet>28</a><span class="kw">use </span>partition::expr::PartitionExpr;
<a href=#29 id=29 data-nosnippet>29</a><span class="kw">use </span>store_api::storage::TableId;
<a href=#30 id=30 data-nosnippet>30</a><span class="kw">use </span>table::metadata::TableType;
<a href=#31 id=31 data-nosnippet>31</a>
<a href=#32 id=32 data-nosnippet>32</a><span class="kw">use </span>crate::{StoreConfig, Tool};
<a href=#33 id=33 data-nosnippet>33</a>
<a href=#34 id=34 data-nosnippet>34</a><span class="doccomment">/// CLI command to repair partition column metadata mismatches.
<a href=#35 id=35 data-nosnippet>35</a></span><span class="attr">#[derive(Parser)]
<a href=#36 id=36 data-nosnippet>36</a></span><span class="kw">pub struct </span>RepairPartitionColumnCommand {
<a href=#37 id=37 data-nosnippet>37</a> <span class="attr">#[clap(flatten)]
<a href=#38 id=38 data-nosnippet>38</a> </span>store_config: StoreConfig,
<a href=#39 id=39 data-nosnippet>39</a>
<a href=#40 id=40 data-nosnippet>40</a> <span class="doccomment">/// Whether to actually do the update in the underlying metadata store, or not.
<a href=#41 id=41 data-nosnippet>41</a> </span><span class="attr">#[clap(long)]
<a href=#42 id=42 data-nosnippet>42</a> </span>dry_run: bool,
<a href=#43 id=43 data-nosnippet>43</a>
<a href=#44 id=44 data-nosnippet>44</a> <span class="doccomment">/// The maximum count of update times.
<a href=#45 id=45 data-nosnippet>45</a> </span><span class="attr">#[clap(long)]
<a href=#46 id=46 data-nosnippet>46</a> </span>update_limit: <span class="prelude-ty">Option</span>&lt;u32&gt;,
<a href=#47 id=47 data-nosnippet>47</a>}
<a href=#48 id=48 data-nosnippet>48</a>
<a href=#49 id=49 data-nosnippet>49</a><span class="kw">impl </span>RepairPartitionColumnCommand {
<a href=#50 id=50 data-nosnippet>50</a> <span class="kw">pub</span>(<span class="kw">super</span>) <span class="kw">async fn </span>build(<span class="kw-2">&amp;</span><span class="self">self</span>) -&gt; <span class="prelude-ty">Result</span>&lt;RepairPartitionColumnTool, BoxedError&gt; {
<a href=#51 id=51 data-nosnippet>51</a> <span class="kw">let </span>kv_backend = <span class="self">self</span>.store_config.build().<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#52 id=52 data-nosnippet>52</a> <span class="prelude-val">Ok</span>(RepairPartitionColumnTool {
<a href=#53 id=53 data-nosnippet>53</a> kv_backend,
<a href=#54 id=54 data-nosnippet>54</a> dry_run: <span class="self">self</span>.dry_run,
<a href=#55 id=55 data-nosnippet>55</a> update_limit: <span class="self">self</span>.update_limit,
<a href=#56 id=56 data-nosnippet>56</a> })
<a href=#57 id=57 data-nosnippet>57</a> }
<a href=#58 id=58 data-nosnippet>58</a>}
<a href=#59 id=59 data-nosnippet>59</a>
<a href=#60 id=60 data-nosnippet>60</a><span class="doccomment">/// Repair tool that reconciles partition columns between table info and routes.
<a href=#61 id=61 data-nosnippet>61</a></span><span class="kw">pub</span>(<span class="kw">crate</span>) <span class="kw">struct </span>RepairPartitionColumnTool {
<a href=#62 id=62 data-nosnippet>62</a> kv_backend: KvBackendRef,
<a href=#63 id=63 data-nosnippet>63</a> dry_run: bool,
<a href=#64 id=64 data-nosnippet>64</a> update_limit: <span class="prelude-ty">Option</span>&lt;u32&gt;,
<a href=#65 id=65 data-nosnippet>65</a>}
<a href=#66 id=66 data-nosnippet>66</a>
<a href=#67 id=67 data-nosnippet>67</a><span class="kw">impl </span>RepairPartitionColumnTool {
<a href=#68 id=68 data-nosnippet>68</a> <span class="kw">async fn </span>do_repair(
<a href=#69 id=69 data-nosnippet>69</a> <span class="kw-2">&amp;</span><span class="self">self</span>,
<a href=#70 id=70 data-nosnippet>70</a> table_infos: HashMap&lt;TableId, TableInfoValue&gt;,
<a href=#71 id=71 data-nosnippet>71</a> table_routes: HashMap&lt;TableId, TableRouteValue&gt;,
<a href=#72 id=72 data-nosnippet>72</a> ) -&gt; <span class="prelude-ty">Result</span>&lt;(), BoxedError&gt; {
<a href=#73 id=73 data-nosnippet>73</a> <span class="kw">let </span><span class="kw-2">mut </span>update_count = <span class="number">0</span>;
<a href=#74 id=74 data-nosnippet>74</a> <span class="kw">for </span>(table_id, table_info_value) <span class="kw">in </span><span class="kw-2">&amp;</span>table_infos {
<a href=#75 id=75 data-nosnippet>75</a> <span class="kw">let </span>table_meta = <span class="kw-2">&amp;</span>table_info_value.table_info.meta;
<a href=#76 id=76 data-nosnippet>76</a> <span class="kw">let </span><span class="kw-2">mut </span>partition_columns = Vec::with_capacity(table_meta.partition_key_indices.len());
<a href=#77 id=77 data-nosnippet>77</a> <span class="kw">for </span>i <span class="kw">in </span><span class="kw-2">&amp;</span>table_meta.partition_key_indices {
<a href=#78 id=78 data-nosnippet>78</a> <span class="kw">if let </span><span class="prelude-val">Some</span>(x) = table_meta.schema.column_schemas().get(<span class="kw-2">*</span>i) {
<a href=#79 id=79 data-nosnippet>79</a> partition_columns.push(<span class="kw-2">&amp;</span>x.name);
<a href=#80 id=80 data-nosnippet>80</a> } <span class="kw">else </span>{
<a href=#81 id=81 data-nosnippet>81</a> <span class="macro">warn!</span>(
<a href=#82 id=82 data-nosnippet>82</a> <span class="string">"Partition column not found by index: {i}, table: {}, id: {}"</span>,
<a href=#83 id=83 data-nosnippet>83</a> table_info_value.table_name(),
<a href=#84 id=84 data-nosnippet>84</a> table_id
<a href=#85 id=85 data-nosnippet>85</a> );
<a href=#86 id=86 data-nosnippet>86</a> }
<a href=#87 id=87 data-nosnippet>87</a> }
<a href=#88 id=88 data-nosnippet>88</a>
<a href=#89 id=89 data-nosnippet>89</a> <span class="kw">let </span><span class="prelude-val">Some</span>(TableRouteValue::Physical(table_route)) = table_routes.get(table_id) <span class="kw">else </span>{
<a href=#90 id=90 data-nosnippet>90</a> <span class="kw">continue</span>;
<a href=#91 id=91 data-nosnippet>91</a> };
<a href=#92 id=92 data-nosnippet>92</a> <span class="kw">let </span><span class="kw-2">mut </span>partition_expr_columns = HashSet::new();
<a href=#93 id=93 data-nosnippet>93</a> <span class="kw">for </span>region_route <span class="kw">in </span><span class="kw-2">&amp;</span>table_route.region_routes {
<a href=#94 id=94 data-nosnippet>94</a> <span class="kw">let </span>partition_expr_result =
<a href=#95 id=95 data-nosnippet>95</a> PartitionExpr::from_json_str(<span class="kw-2">&amp;</span>region_route.region.partition_expr());
<a href=#96 id=96 data-nosnippet>96</a> <span class="kw">let </span>partition_expr = <span class="kw">match </span>partition_expr_result {
<a href=#97 id=97 data-nosnippet>97</a> <span class="prelude-val">Ok</span>(<span class="prelude-val">Some</span>(expr)) =&gt; expr,
<a href=#98 id=98 data-nosnippet>98</a> <span class="prelude-val">Ok</span>(<span class="prelude-val">None</span>) =&gt; {
<a href=#99 id=99 data-nosnippet>99</a> <span class="comment">// No partition expression found, which might be valid.
<a href=#100 id=100 data-nosnippet>100</a> </span><span class="kw">continue</span>;
<a href=#101 id=101 data-nosnippet>101</a> }
<a href=#102 id=102 data-nosnippet>102</a> <span class="prelude-val">Err</span>(e) =&gt; {
<a href=#103 id=103 data-nosnippet>103</a> <span class="macro">warn!</span>(
<a href=#104 id=104 data-nosnippet>104</a> e;
<a href=#105 id=105 data-nosnippet>105</a> <span class="string">"Failed to deserialize partition expression for region: {:?}, table: {}"</span>,
<a href=#106 id=106 data-nosnippet>106</a> region_route.region.id,
<a href=#107 id=107 data-nosnippet>107</a> table_info_value.table_name()
<a href=#108 id=108 data-nosnippet>108</a> );
<a href=#109 id=109 data-nosnippet>109</a> <span class="kw">continue</span>;
<a href=#110 id=110 data-nosnippet>110</a> }
<a href=#111 id=111 data-nosnippet>111</a> };
<a href=#112 id=112 data-nosnippet>112</a> partition_expr.collect_column_names(<span class="kw-2">&amp;mut </span>partition_expr_columns);
<a href=#113 id=113 data-nosnippet>113</a> }
<a href=#114 id=114 data-nosnippet>114</a>
<a href=#115 id=115 data-nosnippet>115</a> <span class="kw">let </span><span class="kw-2">mut </span>partition_expr_columns = partition_expr_columns.iter().collect::&lt;Vec&lt;<span class="kw">_</span>&gt;&gt;();
<a href=#116 id=116 data-nosnippet>116</a> partition_expr_columns.sort();
<a href=#117 id=117 data-nosnippet>117</a> partition_columns.sort();
<a href=#118 id=118 data-nosnippet>118</a> <span class="kw">if </span>partition_expr_columns != partition_columns {
<a href=#119 id=119 data-nosnippet>119</a> <span class="macro">warn!</span>(
<a href=#120 id=120 data-nosnippet>120</a> <span class="string">"Columns in partition exprs: {:?} do not match partition columns: {:?} in table {}"</span>,
<a href=#121 id=121 data-nosnippet>121</a> partition_expr_columns,
<a href=#122 id=122 data-nosnippet>122</a> partition_columns,
<a href=#123 id=123 data-nosnippet>123</a> table_info_value.table_name(),
<a href=#124 id=124 data-nosnippet>124</a> );
<a href=#125 id=125 data-nosnippet>125</a>
<a href=#126 id=126 data-nosnippet>126</a> <span class="kw">if let </span><span class="prelude-val">Some</span>(update_limit) = <span class="self">self</span>.update_limit
<a href=#127 id=127 data-nosnippet>127</a> &amp;&amp; update_count &gt;= update_limit
<a href=#128 id=128 data-nosnippet>128</a> {
<a href=#129 id=129 data-nosnippet>129</a> <span class="macro">warn!</span>(
<a href=#130 id=130 data-nosnippet>130</a> <span class="string">"Reached update limit: {update_limit}. Stopping further table metadata updates."
<a href=#131 id=131 data-nosnippet>131</a> </span>);
<a href=#132 id=132 data-nosnippet>132</a> <span class="kw">return </span><span class="prelude-val">Ok</span>(());
<a href=#133 id=133 data-nosnippet>133</a> }
<a href=#134 id=134 data-nosnippet>134</a> <span class="self">self</span>.update_partition_columns(partition_expr_columns, table_info_value)
<a href=#135 id=135 data-nosnippet>135</a> .<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#136 id=136 data-nosnippet>136</a> update_count += <span class="number">1</span>;
<a href=#137 id=137 data-nosnippet>137</a> }
<a href=#138 id=138 data-nosnippet>138</a> }
<a href=#139 id=139 data-nosnippet>139</a> <span class="prelude-val">Ok</span>(())
<a href=#140 id=140 data-nosnippet>140</a> }
<a href=#141 id=141 data-nosnippet>141</a>
<a href=#142 id=142 data-nosnippet>142</a> <span class="kw">async fn </span>update_partition_columns(
<a href=#143 id=143 data-nosnippet>143</a> <span class="kw-2">&amp;</span><span class="self">self</span>,
<a href=#144 id=144 data-nosnippet>144</a> partition_expr_columns: Vec&lt;<span class="kw-2">&amp;</span>String&gt;,
<a href=#145 id=145 data-nosnippet>145</a> table_info_value: <span class="kw-2">&amp;</span>TableInfoValue,
<a href=#146 id=146 data-nosnippet>146</a> ) -&gt; <span class="prelude-ty">Result</span>&lt;(), BoxedError&gt; {
<a href=#147 id=147 data-nosnippet>147</a> <span class="kw">let </span>column_schemas = table_info_value.table_info.meta.schema.column_schemas();
<a href=#148 id=148 data-nosnippet>148</a> <span class="kw">let </span><span class="kw-2">mut </span>partition_column_indices = Vec::with_capacity(partition_expr_columns.len());
<a href=#149 id=149 data-nosnippet>149</a> <span class="kw">for </span>column_name <span class="kw">in </span><span class="kw-2">&amp;</span>partition_expr_columns {
<a href=#150 id=150 data-nosnippet>150</a> <span class="kw">if let </span><span class="prelude-val">Some</span>((i, <span class="kw">_</span>)) = column_schemas
<a href=#151 id=151 data-nosnippet>151</a> .iter()
<a href=#152 id=152 data-nosnippet>152</a> .enumerate()
<a href=#153 id=153 data-nosnippet>153</a> .find(|(<span class="kw">_</span>, x)| <span class="kw-2">&amp;</span>x.name == <span class="kw-2">*</span>column_name)
<a href=#154 id=154 data-nosnippet>154</a> {
<a href=#155 id=155 data-nosnippet>155</a> partition_column_indices.push(i);
<a href=#156 id=156 data-nosnippet>156</a> } <span class="kw">else </span>{
<a href=#157 id=157 data-nosnippet>157</a> <span class="macro">warn!</span>(
<a href=#158 id=158 data-nosnippet>158</a> <span class="string">"Partition column '{}' from partition expression not found in table schema '{}'. Skipping this column for update."</span>,
<a href=#159 id=159 data-nosnippet>159</a> column_name,
<a href=#160 id=160 data-nosnippet>160</a> table_info_value.table_name()
<a href=#161 id=161 data-nosnippet>161</a> );
<a href=#162 id=162 data-nosnippet>162</a> }
<a href=#163 id=163 data-nosnippet>163</a> }
<a href=#164 id=164 data-nosnippet>164</a>
<a href=#165 id=165 data-nosnippet>165</a> <span class="macro">info!</span>(
<a href=#166 id=166 data-nosnippet>166</a> <span class="string">"Updating partition columns to {:?} (by column indices: {:?}) in table '{}'"</span>,
<a href=#167 id=167 data-nosnippet>167</a> partition_expr_columns,
<a href=#168 id=168 data-nosnippet>168</a> partition_column_indices,
<a href=#169 id=169 data-nosnippet>169</a> table_info_value.table_name(),
<a href=#170 id=170 data-nosnippet>170</a> );
<a href=#171 id=171 data-nosnippet>171</a> <span class="kw">if </span><span class="self">self</span>.dry_run {
<a href=#172 id=172 data-nosnippet>172</a> <span class="macro">info!</span>(<span class="string">"Dry run enabled, do nothing"</span>);
<a href=#173 id=173 data-nosnippet>173</a> <span class="kw">return </span><span class="prelude-val">Ok</span>(());
<a href=#174 id=174 data-nosnippet>174</a> }
<a href=#175 id=175 data-nosnippet>175</a>
<a href=#176 id=176 data-nosnippet>176</a> <span class="kw">let </span><span class="kw-2">mut </span>new_table_info = table_info_value.table_info.clone();
<a href=#177 id=177 data-nosnippet>177</a> new_table_info.meta.partition_key_indices = partition_column_indices;
<a href=#178 id=178 data-nosnippet>178</a> <span class="kw">let </span>table_info = table_info_value.update(new_table_info);
<a href=#179 id=179 data-nosnippet>179</a>
<a href=#180 id=180 data-nosnippet>180</a> <span class="kw">let </span>request = PutRequest::new()
<a href=#181 id=181 data-nosnippet>181</a> .with_key(TableInfoKey::new(table_info.table_info.ident.table_id).to_bytes())
<a href=#182 id=182 data-nosnippet>182</a> .with_value(table_info.try_as_raw_value().map_err(BoxedError::new)<span class="question-mark">?</span>);
<a href=#183 id=183 data-nosnippet>183</a> <span class="kw">let _ </span>= <span class="self">self
<a href=#184 id=184 data-nosnippet>184</a> </span>.kv_backend
<a href=#185 id=185 data-nosnippet>185</a> .put(request)
<a href=#186 id=186 data-nosnippet>186</a> .<span class="kw">await
<a href=#187 id=187 data-nosnippet>187</a> </span>.map_err(BoxedError::new)<span class="question-mark">?</span>;
<a href=#188 id=188 data-nosnippet>188</a> <span class="prelude-val">Ok</span>(())
<a href=#189 id=189 data-nosnippet>189</a> }
<a href=#190 id=190 data-nosnippet>190</a>}
<a href=#191 id=191 data-nosnippet>191</a>
<a href=#192 id=192 data-nosnippet>192</a><span class="attr">#[async_trait]
<a href=#193 id=193 data-nosnippet>193</a></span><span class="kw">impl </span>Tool <span class="kw">for </span>RepairPartitionColumnTool {
<a href=#194 id=194 data-nosnippet>194</a> <span class="kw">async fn </span>do_work(<span class="kw-2">&amp;</span><span class="self">self</span>) -&gt; <span class="prelude-ty">Result</span>&lt;(), BoxedError&gt; {
<a href=#195 id=195 data-nosnippet>195</a> <span class="kw">let </span>key_values = PaginationStream::new(
<a href=#196 id=196 data-nosnippet>196</a> <span class="self">self</span>.kv_backend.clone(),
<a href=#197 id=197 data-nosnippet>197</a> RangeRequest::new().with_range(<span class="macro">vec!</span>[<span class="number">0</span>], <span class="macro">vec!</span>[<span class="number">0</span>]),
<a href=#198 id=198 data-nosnippet>198</a> DEFAULT_PAGE_SIZE,
<a href=#199 id=199 data-nosnippet>199</a> <span class="prelude-val">Ok</span>,
<a href=#200 id=200 data-nosnippet>200</a> )
<a href=#201 id=201 data-nosnippet>201</a> .into_stream();
<a href=#202 id=202 data-nosnippet>202</a> <span class="kw">let </span><span class="kw-2">mut </span>key_values = Box::pin(key_values);
<a href=#203 id=203 data-nosnippet>203</a>
<a href=#204 id=204 data-nosnippet>204</a> <span class="kw">let </span><span class="kw-2">mut </span>table_infos = HashMap::new();
<a href=#205 id=205 data-nosnippet>205</a> <span class="kw">let </span><span class="kw-2">mut </span>table_routes = HashMap::new();
<a href=#206 id=206 data-nosnippet>206</a> <span class="kw">while let </span><span class="prelude-val">Some</span>(result) = key_values.next().<span class="kw">await </span>{
<a href=#207 id=207 data-nosnippet>207</a> <span class="kw">match </span>result {
<a href=#208 id=208 data-nosnippet>208</a> <span class="prelude-val">Ok</span>(kv) =&gt; {
<a href=#209 id=209 data-nosnippet>209</a> <span class="kw">if let </span><span class="prelude-val">Ok</span>(key) = TableInfoKey::from_bytes(kv.key()) {
<a href=#210 id=210 data-nosnippet>210</a> <span class="kw">let </span><span class="prelude-val">Ok</span>(value) = TableInfoValue::try_from_raw_value(<span class="kw-2">&amp;</span>kv.value) <span class="kw">else </span>{
<a href=#211 id=211 data-nosnippet>211</a> <span class="macro">warn!</span>(<span class="string">"Skip corrupted key: {key}"</span>);
<a href=#212 id=212 data-nosnippet>212</a> <span class="kw">continue</span>;
<a href=#213 id=213 data-nosnippet>213</a> };
<a href=#214 id=214 data-nosnippet>214</a> <span class="kw">if </span>value.table_info.table_type == TableType::Base {
<a href=#215 id=215 data-nosnippet>215</a> table_infos.insert(value.table_info.ident.table_id, value);
<a href=#216 id=216 data-nosnippet>216</a> }
<a href=#217 id=217 data-nosnippet>217</a> } <span class="kw">else if let </span><span class="prelude-val">Ok</span>(key) = TableRouteKey::from_bytes(kv.key()) {
<a href=#218 id=218 data-nosnippet>218</a> <span class="kw">let </span><span class="prelude-val">Ok</span>(value) = TableRouteValue::try_from_raw_value(<span class="kw-2">&amp;</span>kv.value) <span class="kw">else </span>{
<a href=#219 id=219 data-nosnippet>219</a> <span class="macro">warn!</span>(<span class="string">"Skip corrupted key: {key}"</span>);
<a href=#220 id=220 data-nosnippet>220</a> <span class="kw">continue</span>;
<a href=#221 id=221 data-nosnippet>221</a> };
<a href=#222 id=222 data-nosnippet>222</a> <span class="kw">if </span>value.is_physical() {
<a href=#223 id=223 data-nosnippet>223</a> table_routes.insert(key.table_id, value);
<a href=#224 id=224 data-nosnippet>224</a> }
<a href=#225 id=225 data-nosnippet>225</a> }
<a href=#226 id=226 data-nosnippet>226</a> }
<a href=#227 id=227 data-nosnippet>227</a> <span class="prelude-val">Err</span>(e) =&gt; {
<a href=#228 id=228 data-nosnippet>228</a> <span class="macro">warn!</span>(e; <span class="string">"Failed to get next key"</span>)
<a href=#229 id=229 data-nosnippet>229</a> }
<a href=#230 id=230 data-nosnippet>230</a> }
<a href=#231 id=231 data-nosnippet>231</a> }
<a href=#232 id=232 data-nosnippet>232</a>
<a href=#233 id=233 data-nosnippet>233</a> <span class="self">self</span>.do_repair(table_infos, table_routes).<span class="kw">await
<a href=#234 id=234 data-nosnippet>234</a> </span>}
<a href=#235 id=235 data-nosnippet>235</a>}
<a href=#236 id=236 data-nosnippet>236</a>
<a href=#237 id=237 data-nosnippet>237</a><span class="attr">#[cfg(test)]
<a href=#238 id=238 data-nosnippet>238</a></span><span class="kw">mod </span>test {
<a href=#239 id=239 data-nosnippet>239</a> <span class="kw">use </span>std::sync::Arc;
<a href=#240 id=240 data-nosnippet>240</a>
<a href=#241 id=241 data-nosnippet>241</a> <span class="kw">use </span>common_meta::kv_backend::KvBackend;
<a href=#242 id=242 data-nosnippet>242</a> <span class="kw">use </span>common_meta::kv_backend::memory::MemoryKvBackend;
<a href=#243 id=243 data-nosnippet>243</a>
<a href=#244 id=244 data-nosnippet>244</a> <span class="kw">use </span>super::<span class="kw-2">*</span>;
<a href=#245 id=245 data-nosnippet>245</a>
<a href=#246 id=246 data-nosnippet>246</a> <span class="attr">#[tokio::test]
<a href=#247 id=247 data-nosnippet>247</a> </span><span class="kw">async fn </span>test_repair_partition_column() {
<a href=#248 id=248 data-nosnippet>248</a> common_telemetry::init_default_ut_logging();
<a href=#249 id=249 data-nosnippet>249</a>
<a href=#250 id=250 data-nosnippet>250</a> <span class="kw">let </span>kv_backend = Arc::new(MemoryKvBackend::new());
<a href=#251 id=251 data-nosnippet>251</a>
<a href=#252 id=252 data-nosnippet>252</a> <span class="kw">let </span>table_info_key = TableInfoKey::new(<span class="number">1282</span>).to_bytes();
<a href=#253 id=253 data-nosnippet>253</a> <span class="kw">let </span>table_info_value = <span class="string">r#"{"table_info":{"ident":{"table_id":1282,"version":2},"name":"foo","desc":null,"catalog_name":"greptime","schema_name":"public","meta":{"schema":{"column_schemas":[{"name":"c0","data_type":{"Int32":{}},"is_nullable":true,"is_time_index":false,"default_constraint":null,"metadata":{}},{"name":"c1","data_type":{"String":null},"is_nullable":true,"is_time_index":false,"default_constraint":null,"metadata":{}},{"name":"c2","data_type":{"String":null},"is_nullable":true,"is_time_index":false,"default_constraint":null,"metadata":{}},{"name":"c3","data_type":{"String":null},"is_nullable":true,"is_time_index":false,"default_constraint":null,"metadata":{}},{"name":"c4","data_type":{"Int32":{}},"is_nullable":true,"is_time_index":false,"default_constraint":null,"metadata":{}},{"name":"c5","data_type":{"Int32":{}},"is_nullable":true,"is_time_index":false,"default_constraint":null,"metadata":{}},{"name":"c6","data_type":{"Int32":{}},"is_nullable":true,"is_time_index":false,"default_constraint":null,"metadata":{}},{"name":"c7","data_type":{"Int32":{}},"is_nullable":true,"is_time_index":false,"default_constraint":null,"metadata":{}},{"name":"c8","data_type":{"Int32":{}},"is_nullable":true,"is_time_index":false,"default_constraint":null,"metadata":{}},{"name":"c9","data_type":{"Int32":{}},"is_nullable":true,"is_time_index":false,"default_constraint":null,"metadata":{}},{"name":"c10","data_type":{"Timestamp":{"Nanosecond":null}},"is_nullable":false,"is_time_index":true,"default_constraint":null,"metadata":{"greptime:time_index":"true"}}],"timestamp_index":10,"version":2},"primary_key_indices":[4,7],"value_indices":[0,1,2,3,5,6,8,9,10],"engine":"mito","next_column_id":11,"region_numbers":[0,1,2],"options":{"write_buffer_size":null,"ttl":"14days","skip_wal":false,"extra_options":{"append_mode":"true"}},"created_on":"2025-09-25T01:39:28.702584510Z","partition_key_indices":[3]},"table_type":"Base"},"version":2}"#</span>;
<a href=#254 id=254 data-nosnippet>254</a> kv_backend
<a href=#255 id=255 data-nosnippet>255</a> .put(
<a href=#256 id=256 data-nosnippet>256</a> PutRequest::new()
<a href=#257 id=257 data-nosnippet>257</a> .with_key(table_info_key.clone())
<a href=#258 id=258 data-nosnippet>258</a> .with_value(table_info_value),
<a href=#259 id=259 data-nosnippet>259</a> )
<a href=#260 id=260 data-nosnippet>260</a> .<span class="kw">await
<a href=#261 id=261 data-nosnippet>261</a> </span>.unwrap();
<a href=#262 id=262 data-nosnippet>262</a>
<a href=#263 id=263 data-nosnippet>263</a> <span class="kw">let </span>table_route_key = TableRouteKey::new(<span class="number">1282</span>).to_bytes();
<a href=#264 id=264 data-nosnippet>264</a> <span class="kw">let </span>table_route_value = <span class="string">r#"{"type":"physical","region_routes":[{"region":{"id":5506148073472,"name":"","partition":{"column_list":["c4"],"value_list":["{\"Expr\":{\"lhs\":{\"Column\":\"c4\"},\"op\":\"Lt\",\"rhs\":{\"Value\":{\"Int32\":1}}}}"]},"attrs":{}},"leader_peer":{"id":12,"addr":"192.168.1.1:3001"},"follower_peers":[],"leader_down_since":null},{"region":{"id":5506148073473,"name":"","partition":{"column_list":["c4"],"value_list":["{\"Expr\":{\"lhs\":{\"Expr\":{\"lhs\":{\"Column\":\"c4\"},\"op\":\"GtEq\",\"rhs\":{\"Value\":{\"Int32\":1}}}},\"op\":\"And\",\"rhs\":{\"Expr\":{\"lhs\":{\"Column\":\"c4\"},\"op\":\"Lt\",\"rhs\":{\"Value\":{\"Int32\":2}}}}}}"]},"attrs":{}},"leader_peer":{"id":13,"addr":"192.168.1.2:3001"},"follower_peers":[],"leader_down_since":null},{"region":{"id":5506148073474,"name":"","partition":{"column_list":["c4"],"value_list":["{\"Expr\":{\"lhs\":{\"Column\":\"c4\"},\"op\":\"GtEq\",\"rhs\":{\"Value\":{\"Int32\":2}}}}"]},"attrs":{}},"leader_peer":{"id":10,"addr":"192.168.1.3:3001"},"follower_peers":[],"leader_down_since":null}],"version":0}"#</span>;
<a href=#265 id=265 data-nosnippet>265</a> kv_backend
<a href=#266 id=266 data-nosnippet>266</a> .put(
<a href=#267 id=267 data-nosnippet>267</a> PutRequest::new()
<a href=#268 id=268 data-nosnippet>268</a> .with_key(table_route_key)
<a href=#269 id=269 data-nosnippet>269</a> .with_value(table_route_value),
<a href=#270 id=270 data-nosnippet>270</a> )
<a href=#271 id=271 data-nosnippet>271</a> .<span class="kw">await
<a href=#272 id=272 data-nosnippet>272</a> </span>.unwrap();
<a href=#273 id=273 data-nosnippet>273</a>
<a href=#274 id=274 data-nosnippet>274</a> <span class="kw">let </span>tool = RepairPartitionColumnTool {
<a href=#275 id=275 data-nosnippet>275</a> kv_backend: kv_backend.clone(),
<a href=#276 id=276 data-nosnippet>276</a> dry_run: <span class="bool-val">true</span>,
<a href=#277 id=277 data-nosnippet>277</a> update_limit: <span class="prelude-val">None</span>,
<a href=#278 id=278 data-nosnippet>278</a> };
<a href=#279 id=279 data-nosnippet>279</a> tool.do_work().<span class="kw">await</span>.unwrap();
<a href=#280 id=280 data-nosnippet>280</a> <span class="kw">let </span>actual = String::from_utf8(
<a href=#281 id=281 data-nosnippet>281</a> kv_backend
<a href=#282 id=282 data-nosnippet>282</a> .get(<span class="kw-2">&amp;</span>table_info_key)
<a href=#283 id=283 data-nosnippet>283</a> .<span class="kw">await
<a href=#284 id=284 data-nosnippet>284</a> </span>.unwrap()
<a href=#285 id=285 data-nosnippet>285</a> .unwrap()
<a href=#286 id=286 data-nosnippet>286</a> .value,
<a href=#287 id=287 data-nosnippet>287</a> )
<a href=#288 id=288 data-nosnippet>288</a> .unwrap();
<a href=#289 id=289 data-nosnippet>289</a> <span class="macro">assert_eq!</span>(actual, table_info_value);
<a href=#290 id=290 data-nosnippet>290</a>
<a href=#291 id=291 data-nosnippet>291</a> <span class="kw">let </span>tool = RepairPartitionColumnTool {
<a href=#292 id=292 data-nosnippet>292</a> kv_backend: kv_backend.clone(),
<a href=#293 id=293 data-nosnippet>293</a> dry_run: <span class="bool-val">false</span>,
<a href=#294 id=294 data-nosnippet>294</a> update_limit: <span class="prelude-val">Some</span>(<span class="number">0</span>),
<a href=#295 id=295 data-nosnippet>295</a> };
<a href=#296 id=296 data-nosnippet>296</a> tool.do_work().<span class="kw">await</span>.unwrap();
<a href=#297 id=297 data-nosnippet>297</a> <span class="kw">let </span>actual = String::from_utf8(
<a href=#298 id=298 data-nosnippet>298</a> kv_backend
<a href=#299 id=299 data-nosnippet>299</a> .get(<span class="kw-2">&amp;</span>table_info_key)
<a href=#300 id=300 data-nosnippet>300</a> .<span class="kw">await
<a href=#301 id=301 data-nosnippet>301</a> </span>.unwrap()
<a href=#302 id=302 data-nosnippet>302</a> .unwrap()
<a href=#303 id=303 data-nosnippet>303</a> .value,
<a href=#304 id=304 data-nosnippet>304</a> )
<a href=#305 id=305 data-nosnippet>305</a> .unwrap();
<a href=#306 id=306 data-nosnippet>306</a> <span class="macro">assert_eq!</span>(actual, table_info_value);
<a href=#307 id=307 data-nosnippet>307</a>
<a href=#308 id=308 data-nosnippet>308</a> <span class="kw">let </span>tool = RepairPartitionColumnTool {
<a href=#309 id=309 data-nosnippet>309</a> kv_backend: kv_backend.clone(),
<a href=#310 id=310 data-nosnippet>310</a> dry_run: <span class="bool-val">false</span>,
<a href=#311 id=311 data-nosnippet>311</a> update_limit: <span class="prelude-val">Some</span>(<span class="number">1</span>),
<a href=#312 id=312 data-nosnippet>312</a> };
<a href=#313 id=313 data-nosnippet>313</a> tool.do_work().<span class="kw">await</span>.unwrap();
<a href=#314 id=314 data-nosnippet>314</a> <span class="kw">let </span>actual = String::from_utf8(
<a href=#315 id=315 data-nosnippet>315</a> kv_backend
<a href=#316 id=316 data-nosnippet>316</a> .get(<span class="kw-2">&amp;</span>table_info_key)
<a href=#317 id=317 data-nosnippet>317</a> .<span class="kw">await
<a href=#318 id=318 data-nosnippet>318</a> </span>.unwrap()
<a href=#319 id=319 data-nosnippet>319</a> .unwrap()
<a href=#320 id=320 data-nosnippet>320</a> .value,
<a href=#321 id=321 data-nosnippet>321</a> )
<a href=#322 id=322 data-nosnippet>322</a> .unwrap();
<a href=#323 id=323 data-nosnippet>323</a> <span class="kw">let </span>expected = <span class="string">r#"{"table_info":{"ident":{"table_id":1282,"version":2},"name":"foo","desc":null,"catalog_name":"greptime","schema_name":"public","meta":{"schema":{"column_schemas":[{"name":"c0","data_type":{"Int32":{}},"is_nullable":true,"is_time_index":false,"default_constraint":null,"metadata":{}},{"name":"c1","data_type":{"String":{"size_type":"Utf8"}},"is_nullable":true,"is_time_index":false,"default_constraint":null,"metadata":{}},{"name":"c2","data_type":{"String":{"size_type":"Utf8"}},"is_nullable":true,"is_time_index":false,"default_constraint":null,"metadata":{}},{"name":"c3","data_type":{"String":{"size_type":"Utf8"}},"is_nullable":true,"is_time_index":false,"default_constraint":null,"metadata":{}},{"name":"c4","data_type":{"Int32":{}},"is_nullable":true,"is_time_index":false,"default_constraint":null,"metadata":{}},{"name":"c5","data_type":{"Int32":{}},"is_nullable":true,"is_time_index":false,"default_constraint":null,"metadata":{}},{"name":"c6","data_type":{"Int32":{}},"is_nullable":true,"is_time_index":false,"default_constraint":null,"metadata":{}},{"name":"c7","data_type":{"Int32":{}},"is_nullable":true,"is_time_index":false,"default_constraint":null,"metadata":{}},{"name":"c8","data_type":{"Int32":{}},"is_nullable":true,"is_time_index":false,"default_constraint":null,"metadata":{}},{"name":"c9","data_type":{"Int32":{}},"is_nullable":true,"is_time_index":false,"default_constraint":null,"metadata":{}},{"name":"c10","data_type":{"Timestamp":{"Nanosecond":null}},"is_nullable":false,"is_time_index":true,"default_constraint":null,"metadata":{"greptime:time_index":"true"}}],"version":2},"primary_key_indices":[4,7],"value_indices":[0,1,2,3,5,6,8,9,10],"engine":"mito","next_column_id":11,"options":{"write_buffer_size":null,"ttl":"14days","skip_wal":false,"extra_options":{"append_mode":"true"}},"created_on":"2025-09-25T01:39:28.702584510Z","updated_on":"2025-09-25T01:39:28.702584510Z","partition_key_indices":[4],"column_ids":[]},"table_type":"Base"},"version":3}"#</span>;
<a href=#324 id=324 data-nosnippet>324</a> <span class="macro">assert_eq!</span>(actual, expected);
<a href=#325 id=325 data-nosnippet>325</a> }
<a href=#326 id=326 data-nosnippet>326</a>}
</code></pre></div></section></main></body></html>