This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch dev-1.1.2
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/dev-1.1.2 by this push:
new b6936cc2a1 [feature](stmt) add ADMIN COPY TABLET stmt for local debug
(#12176) (#12216)
b6936cc2a1 is described below
commit b6936cc2a13beb6c9793c2dab4bd79e41ab209e3
Author: Mingyu Chen <[email protected]>
AuthorDate: Wed Aug 31 14:07:34 2022 +0800
[feature](stmt) add ADMIN COPY TABLET stmt for local debug (#12176) (#12216)
Add a new stmt ADMIN COPY TABLET for easy copy a tablet to local env to
reproduce problem.
See document for more details.
---
be/src/http/action/snapshot_action.cpp | 6 +-
be/src/http/action/snapshot_action.h | 2 +-
be/src/olap/snapshot_manager.cpp | 15 ++-
be/src/olap/snapshot_manager.h | 3 +
be/src/olap/tablet_meta.cpp | 29 +++++
be/src/olap/tablet_meta.h | 1 +
.../ADMIN-COPY-TABLET.md | 101 +++++++++++++++++
.../ADMIN-COPY-TABLET.md | 101 +++++++++++++++++
fe/fe-core/src/main/cup/sql_parser.cup | 8 +-
.../apache/doris/analysis/AdminCopyTabletStmt.java | 124 +++++++++++++++++++++
.../java/org/apache/doris/catalog/Catalog.java | 22 +++-
.../apache/doris/httpv2/rest/GetDdlStmtAction.java | 2 +-
.../doris/httpv2/rest/StmtExecutionAction.java | 2 +-
.../java/org/apache/doris/master/MasterImpl.java | 10 +-
.../java/org/apache/doris/qe/ShowExecutor.java | 115 ++++++++++++++++++-
.../java/org/apache/doris/task/SnapshotTask.java | 44 ++++++--
fe/fe-core/src/main/jflex/sql_scanner.flex | 1 +
.../apache/doris/catalog/CreateTableLikeTest.java | 11 +-
.../java/org/apache/doris/qe/ShowExecutorTest.java | 4 +-
gensrc/thrift/AgentService.thrift | 1 +
20 files changed, 565 insertions(+), 37 deletions(-)
diff --git a/be/src/http/action/snapshot_action.cpp
b/be/src/http/action/snapshot_action.cpp
index 86336b45d9..b3bd98e498 100644
--- a/be/src/http/action/snapshot_action.cpp
+++ b/be/src/http/action/snapshot_action.cpp
@@ -78,7 +78,7 @@ void SnapshotAction::handle(HttpRequest* req) {
VLOG_ROW << "get make snapshot tablet info: " << tablet_id << "-" <<
schema_hash;
std::string snapshot_path;
- int64_t ret = make_snapshot(tablet_id, schema_hash, &snapshot_path);
+ int64_t ret = _make_snapshot(tablet_id, schema_hash, &snapshot_path);
if (ret != 0L) {
std::string error_msg = std::string("make snapshot failed");
HttpChannel::send_reply(req, HttpStatus::INTERNAL_SERVER_ERROR,
error_msg);
@@ -93,8 +93,8 @@ void SnapshotAction::handle(HttpRequest* req) {
LOG(INFO) << "deal with snapshot request finished! tablet id: " <<
tablet_id;
}
-int64_t SnapshotAction::make_snapshot(int64_t tablet_id, int32_t schema_hash,
- std::string* snapshot_path) {
+int64_t SnapshotAction::_make_snapshot(int64_t tablet_id, int32_t schema_hash,
+ std::string* snapshot_path) {
TSnapshotRequest request;
request.tablet_id = tablet_id;
request.schema_hash = schema_hash;
diff --git a/be/src/http/action/snapshot_action.h
b/be/src/http/action/snapshot_action.h
index 673d761182..1a2c5a9c09 100644
--- a/be/src/http/action/snapshot_action.h
+++ b/be/src/http/action/snapshot_action.h
@@ -38,7 +38,7 @@ public:
void handle(HttpRequest* req) override;
private:
- int64_t make_snapshot(int64_t tablet_id, int schema_hash, std::string*
snapshot_path);
+ int64_t _make_snapshot(int64_t tablet_id, int schema_hash, std::string*
snapshot_path);
}; // end class SnapshotAction
} // end namespace doris
diff --git a/be/src/olap/snapshot_manager.cpp b/be/src/olap/snapshot_manager.cpp
index 0cd09e80a8..a0dbd899cf 100644
--- a/be/src/olap/snapshot_manager.cpp
+++ b/be/src/olap/snapshot_manager.cpp
@@ -285,6 +285,11 @@ std::string SnapshotManager::_get_header_full_path(const
TabletSharedPtr& ref_ta
return header_name_stream.str();
}
+std::string SnapshotManager::_get_json_header_full_path(const TabletSharedPtr&
ref_tablet,
+ const std::string&
schema_hash_path) const {
+ return fmt::format("{}/{}.hdr.json", schema_hash_path,
ref_tablet->tablet_id());
+}
+
OLAPStatus SnapshotManager::_link_index_and_data_files(
const FilePathDesc& schema_hash_path_desc, const TabletSharedPtr&
ref_tablet,
const std::vector<RowsetSharedPtr>& consistent_rowsets) {
@@ -309,13 +314,13 @@ OLAPStatus SnapshotManager::_create_snapshot_files(const
TabletSharedPtr& ref_ta
return OLAP_ERR_INPUT_PARAMETER_ERROR;
}
- // snapshot_id_path:
- // /data/shard_id/tablet_id/snapshot/time_str/id.timeout/
- string snapshot_id_path;
int64_t timeout_s = config::snapshot_expire_time_sec;
if (request.__isset.timeout) {
timeout_s = request.timeout;
}
+ // snapshot_id_path:
+ // /data/shard_id/tablet_id/snapshot/time_str/id.timeout/
+ std::string snapshot_id_path;
res = _calc_snapshot_id_path(ref_tablet, timeout_s, &snapshot_id_path);
if (res != OLAP_SUCCESS) {
LOG(WARNING) << "failed to calc snapshot_id_path, ref tablet="
@@ -329,6 +334,7 @@ OLAPStatus SnapshotManager::_create_snapshot_files(const
TabletSharedPtr& ref_ta
// header_path:
// /schema_full_path/tablet_id.hdr
string header_path = _get_header_full_path(ref_tablet,
schema_full_path_desc.filepath);
+ auto json_header_path = _get_json_header_full_path(ref_tablet,
schema_full_path_desc.filepath);
if (FileUtils::check_exist(schema_full_path_desc.filepath)) {
VLOG_TRACE << "remove the old schema_full_path.";
FileUtils::remove_all(schema_full_path_desc.filepath);
@@ -454,6 +460,9 @@ OLAPStatus SnapshotManager::_create_snapshot_files(const
TabletSharedPtr& ref_ta
<< ", schema hash:" << new_tablet_meta->schema_hash();
} else if (snapshot_version ==
g_Types_constants.TSNAPSHOT_REQ_VERSION2) {
res = new_tablet_meta->save(header_path);
+ if (res == OLAP_SUCCESS && request.__isset.is_copy_tablet_task &&
request.is_copy_tablet_task) {
+ res = new_tablet_meta->save_as_json(json_header_path);
+ }
} else {
res = OLAP_ERR_INVALID_SNAPSHOT_VERSION;
}
diff --git a/be/src/olap/snapshot_manager.h b/be/src/olap/snapshot_manager.h
index 0efa64711b..27c70236a5 100644
--- a/be/src/olap/snapshot_manager.h
+++ b/be/src/olap/snapshot_manager.h
@@ -73,6 +73,9 @@ private:
std::string _get_header_full_path(const TabletSharedPtr& ref_tablet,
const std::string& schema_hash_path)
const;
+ std::string _get_json_header_full_path(const TabletSharedPtr& ref_tablet,
+ const std::string&
schema_hash_path) const;
+
OLAPStatus _link_index_and_data_files(const FilePathDesc& header_path_desc,
const TabletSharedPtr& ref_tablet,
const std::vector<RowsetSharedPtr>&
consistent_rowsets);
diff --git a/be/src/olap/tablet_meta.cpp b/be/src/olap/tablet_meta.cpp
index c4696d10c8..3283214459 100644
--- a/be/src/olap/tablet_meta.cpp
+++ b/be/src/olap/tablet_meta.cpp
@@ -20,6 +20,7 @@
#include <boost/algorithm/string.hpp>
#include <sstream>
+#include "exec/local_file_writer.h"
#include "olap/file_helper.h"
#include "olap/olap_common.h"
#include "olap/olap_define.h"
@@ -293,6 +294,34 @@ std::string TabletMeta::construct_header_file_path(const
string& schema_hash_pat
return header_name_stream.str();
}
+OLAPStatus TabletMeta::save_as_json(const string& file_path) {
+ std::string json_meta;
+ json2pb::Pb2JsonOptions json_options;
+ json_options.pretty_json = true;
+ json_options.bytes_to_base64 = true;
+ to_json(&json_meta, json_options);
+ // save to file
+ LocalFileWriter writer(file_path, 0);
+ Status st = writer.open();
+ if (!st.ok()) {
+ LOG(WARNING) << "failed to open for: " << file_path << ", err: " <<
st.get_error_msg();
+ return OLAP_ERR_IO_ERROR;
+ }
+ size_t written_len = 0;
+ st = writer.write((const uint8_t*) json_meta.data(), json_meta.length(),
&written_len);
+ if (!st.ok()) {
+ LOG(WARNING) << "failed to write for: " << file_path << ", err: " <<
st.get_error_msg();
+ return OLAP_ERR_IO_ERROR;
+ }
+ st = writer.close();
+ if (!st.ok()) {
+ LOG(WARNING) << "failed to close writer for: " << file_path << ", err:
" << st.get_error_msg();
+ return OLAP_ERR_IO_ERROR;
+ }
+
+ return OLAP_SUCCESS;
+}
+
OLAPStatus TabletMeta::save(const string& file_path) {
TabletMetaPB tablet_meta_pb;
to_meta_pb(&tablet_meta_pb);
diff --git a/be/src/olap/tablet_meta.h b/be/src/olap/tablet_meta.h
index 5654fee29e..30c3875f03 100644
--- a/be/src/olap/tablet_meta.h
+++ b/be/src/olap/tablet_meta.h
@@ -93,6 +93,7 @@ public:
// Previous tablet_meta is a physical file in tablet dir, which is not
stored in rocksdb.
OLAPStatus create_from_file(const std::string& file_path);
OLAPStatus save(const std::string& file_path);
+ OLAPStatus save_as_json(const string& file_path);
static OLAPStatus save(const std::string& file_path, const TabletMetaPB&
tablet_meta_pb);
static OLAPStatus reset_tablet_uid(const std::string& file_path);
static std::string construct_header_file_path(const std::string&
schema_hash_path,
diff --git
a/docs/en/docs/sql-manual/sql-reference/Database-Administration-Statements/ADMIN-COPY-TABLET.md
b/docs/en/docs/sql-manual/sql-reference/Database-Administration-Statements/ADMIN-COPY-TABLET.md
new file mode 100644
index 0000000000..26b4631d60
--- /dev/null
+++
b/docs/en/docs/sql-manual/sql-reference/Database-Administration-Statements/ADMIN-COPY-TABLET.md
@@ -0,0 +1,101 @@
+---
+{
+ "title": "ADMIN-COPY-TABLET"
+ "language": "en"
+}
+---
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+## ADMIN-COPY-TABLET
+
+### Name
+
+ADMIN COPY TABLET
+
+### Description
+
+This statement is used to make a snapshot for the specified tablet, mainly
used to load the tablet locally to reproduce the problem.
+
+syntax:
+
+```sql
+ADMIN COPY TABLET tablet_id PROPERTIES("xxx");
+```
+
+Notes:
+
+This command requires ROOT privileges.
+
+PROPERTIES supports the following properties:
+
+1. backend_id: Specifies the id of the BE node where the replica is located.
If not specified, a replica is randomly selected.
+
+2. version: Specifies the version of the snapshot. The version must be less
than or equal to the largest version of the replica. If not specified, the
largest version is used.
+
+3. expiration_minutes: Snapshot retention time. The default is 1 hour. It will
automatically clean up after a timeout. Unit minutes.
+
+The results are shown below:
+
+```
+ TabletId: 10020
+ BackendId: 10003
+ Ip: 192.168.10.1
+ Path: /path/to/be/storage/snapshot/20220830101353.2.3600
+ExpirationMinutes: 60
+ CreateTableStmt: CREATE TABLE `tbl1` (
+ `k1` int(11) NULL,
+ `k2` int(11) NULL
+) ENGINE=OLAP
+DUPLICATE KEY(`k1`, `k2`)
+DISTRIBUTED BY HASH(k1) BUCKETS 1
+PROPERTIES (
+"replication_num" = "1",
+"version_info" = "2"
+);
+```
+
+* TabletId: tablet id
+* BackendId: BE node id
+* Ip: BE node ip
+* Path: The directory where the snapshot is located
+* ExpirationMinutes: snapshot expiration time
+* CreateTableStmt: The table creation statement for the table corresponding to
the tablet. This statement is not the original table-building statement, but a
simplified table-building statement for later loading the tablet locally.
+
+### Example
+
+1. Take a snapshot of the replica on the specified BE node
+
+ ```sql
+ ADMIN COPY TABLET 10010 PROPERTIES("backend_id" = "10001");
+ ```
+
+2. Take a snapshot of the specified version of the replica on the specified BE
node
+
+ ```sql
+ ADMIN COPY TABLET 10010 PROPERTIES("backend_id" = "10001", "version" =
"10");
+ ```
+
+### Keywords
+
+ ADMIN, COPY, TABLET
+
+### Best Practice
+
diff --git
a/docs/zh-CN/docs/sql-manual/sql-reference/Database-Administration-Statements/ADMIN-COPY-TABLET.md
b/docs/zh-CN/docs/sql-manual/sql-reference/Database-Administration-Statements/ADMIN-COPY-TABLET.md
new file mode 100644
index 0000000000..1645a1c18a
--- /dev/null
+++
b/docs/zh-CN/docs/sql-manual/sql-reference/Database-Administration-Statements/ADMIN-COPY-TABLET.md
@@ -0,0 +1,101 @@
+---
+{
+ "title": "ADMIN-COPY-TABLET"
+ "language": "zh-CN"
+}
+---
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+## ADMIN-COPY-TABLET
+
+### Name
+
+ADMIN COPY TABLET
+
+### Description
+
+该语句用于为指定的 tablet 制作快照,主要用于本地加载 tablet 来复现问题。
+
+语法:
+
+```sql
+ADMIN COPY TABLET tablet_id PROPERTIES("xxx");
+```
+
+说明:
+
+该命令需要 ROOT 权限。
+
+PROPERTIES 支持如下属性:
+
+1. backend_id:指定副本所在的 BE 节点的 id。如果不指定,则随机选择一个副本。
+
+2. version:指定快照的版本。该版本需小于等于副本的最大版本。如不指定,则使用最大版本。
+
+3. expiration_minutes:快照保留时长。默认为1小时。超时后会自动清理。单位分钟。
+
+结果展示如下:
+
+```
+ TabletId: 10020
+ BackendId: 10003
+ Ip: 192.168.10.1
+ Path: /path/to/be/storage/snapshot/20220830101353.2.3600
+ExpirationMinutes: 60
+ CreateTableStmt: CREATE TABLE `tbl1` (
+ `k1` int(11) NULL,
+ `k2` int(11) NULL
+) ENGINE=OLAP
+DUPLICATE KEY(`k1`, `k2`)
+DISTRIBUTED BY HASH(k1) BUCKETS 1
+PROPERTIES (
+"replication_num" = "1",
+"version_info" = "2"
+);
+```
+
+* TabletId: tablet id
+* BackendId: BE 节点 id
+* Ip: BE 节点 ip
+* Path: 快照所在目录
+* ExpirationMinutes: 快照过期时间
+* CreateTableStmt: tablet 对应的表的建表语句。该语句不是原始的建表语句,而是用于之后本地加载 tablet 的简化后的建表语句。
+
+### Example
+
+1. 对指定 BE 节点上的副本做快照
+
+ ```sql
+ ADMIN COPY TABLET 10010 PROPERTIES("backend_id" = "10001");
+ ```
+
+2. 对指定 BE 节点上的副本,做指定版本的快照
+
+ ```sql
+ ADMIN COPY TABLET 10010 PROPERTIES("backend_id" = "10001", "version" =
"10");
+ ```
+
+### Keywords
+
+ ADMIN, COPY, TABLET
+
+### Best Practice
+
diff --git a/fe/fe-core/src/main/cup/sql_parser.cup
b/fe/fe-core/src/main/cup/sql_parser.cup
index 9752b9ee08..ba98bc8849 100644
--- a/fe/fe-core/src/main/cup/sql_parser.cup
+++ b/fe/fe-core/src/main/cup/sql_parser.cup
@@ -239,7 +239,7 @@ terminal String KW_ADD, KW_ADMIN, KW_AFTER, KW_AGGREGATE,
KW_ALIAS, KW_ALL, KW_A
KW_BACKEND, KW_BACKUP, KW_BETWEEN, KW_BEGIN, KW_BIGINT, KW_BINLOG,
KW_BITMAP, KW_BITMAP_UNION, KW_BLOB, KW_BOOLEAN, KW_BROKER, KW_BACKENDS, KW_BY,
KW_BUILTIN,
KW_CANCEL, KW_CASE, KW_CAST, KW_CHAIN, KW_CHAR, KW_CHARSET, KW_CHECK,
KW_CLUSTER, KW_CLUSTERS, KW_CLEAN, KW_CURRENT_TIMESTAMP,
KW_COLLATE, KW_COLLATION, KW_COLUMN, KW_COLUMNS, KW_COMMENT, KW_COMMIT,
KW_COMMITTED, KW_COMPACT,
- KW_CONFIG, KW_CONNECTION, KW_CONNECTION_ID, KW_CONSISTENT, KW_CONVERT,
KW_COUNT, KW_CREATE, KW_CREATION, KW_CROSS, KW_CUBE, KW_CURRENT,
KW_CURRENT_USER,
+ KW_CONFIG, KW_CONNECTION, KW_CONNECTION_ID, KW_CONSISTENT, KW_CONVERT,
KW_COPY, KW_COUNT, KW_CREATE, KW_CREATION, KW_CROSS, KW_CUBE, KW_CURRENT,
KW_CURRENT_USER,
KW_DATA, KW_DATABASE, KW_DATABASES, KW_DATE, KW_DATETIME, KW_DAY,
KW_DECIMAL, KW_DECOMMISSION, KW_DEFAULT, KW_DESC, KW_DESCRIBE,
KW_DELETE, KW_UPDATE, KW_DIAGNOSE, KW_DISTINCT, KW_DISTINCTPC,
KW_DISTINCTPCSA, KW_DISTRIBUTED, KW_DISTRIBUTION, KW_DYNAMIC, KW_BUCKETS,
KW_DIV, KW_DOUBLE, KW_DROP, KW_DROPP, KW_DUPLICATE,
KW_ELSE, KW_ENABLE, KW_ENCRYPTKEY, KW_ENCRYPTKEYS, KW_END, KW_ENGINE,
KW_ENGINES, KW_ENTER, KW_ERRORS, KW_EVENTS, KW_EXCEPT, KW_EXCLUDE,
@@ -5305,6 +5305,10 @@ admin_stmt ::=
{:
RESULT = new AdminShowTabletStorageFormatStmt(true);
:}
+ | KW_ADMIN KW_COPY KW_TABLET INTEGER_LITERAL:tabletId
opt_properties:properties
+ {:
+ RESULT = new AdminCopyTabletStmt(tabletId, properties);
+ :}
;
truncate_stmt ::=
@@ -5470,6 +5474,8 @@ keyword ::=
{: RESULT = id; :}
| KW_CONVERT:id
{: RESULT = id; :}
+ | KW_COPY:id
+ {: RESULT = id; :}
| KW_CREATION:id
{: RESULT = id; :}
| KW_DATA:id
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/analysis/AdminCopyTabletStmt.java
b/fe/fe-core/src/main/java/org/apache/doris/analysis/AdminCopyTabletStmt.java
new file mode 100644
index 0000000000..68800feb0a
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/analysis/AdminCopyTabletStmt.java
@@ -0,0 +1,124 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.analysis;
+
+import org.apache.doris.catalog.Catalog;
+import org.apache.doris.catalog.Column;
+import org.apache.doris.catalog.ScalarType;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.ErrorCode;
+import org.apache.doris.common.ErrorReport;
+import org.apache.doris.mysql.privilege.PrivPredicate;
+import org.apache.doris.qe.ConnectContext;
+import org.apache.doris.qe.ShowResultSetMetaData;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
+
+import java.util.Iterator;
+import java.util.Map;
+
+// ADMIN COPY TABLET 10110 PROPERTIES('version' = '1000', backend_id =
'10001');
+public class AdminCopyTabletStmt extends ShowStmt {
+ public static final String PROP_VERSION = "version";
+ public static final String PROP_BACKEND_ID = "backend_id";
+ public static final String PROP_EXPIRATION = "expiration_minutes";
+ private static final long DEFAULT_EXPIRATION_MINUTES = 60;
+ public static final ImmutableList<String> TITLE_NAMES = new
ImmutableList.Builder<String>().add("TabletId")
+
.add("BackendId").add("Ip").add("Path").add("ExpirationMinutes").add("CreateTableStmt").build();
+
+ private long tabletId;
+ private Map<String, String> properties = Maps.newHashMap();
+ private long version = -1;
+ private long backendId = -1;
+ private long expirationMinutes = DEFAULT_EXPIRATION_MINUTES; // default
60min
+
+ public AdminCopyTabletStmt(long tabletId, Map<String, String> properties) {
+ this.tabletId = tabletId;
+ this.properties = properties;
+ }
+
+ public long getTabletId() {
+ return tabletId;
+ }
+
+ public long getVersion() {
+ return version;
+ }
+
+ public long getBackendId() {
+ return backendId;
+ }
+
+ public long getExpirationMinutes() {
+ return expirationMinutes;
+ }
+
+ @Override
+ public void analyze(Analyzer analyzer) throws AnalysisException {
+ if
(!Catalog.getCurrentCatalog().getAuth().checkGlobalPriv(ConnectContext.get(),
PrivPredicate.OPERATOR)) {
+
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR,
"NODE");
+ }
+
+ if (properties == null) {
+ return;
+ }
+
+ try {
+ Iterator<Map.Entry<String, String>> iter =
properties.entrySet().iterator();
+ while (iter.hasNext()) {
+ Map.Entry<String, String> entry = iter.next();
+ if (entry.getKey().equalsIgnoreCase(PROP_VERSION)) {
+ version = Long.valueOf(entry.getValue());
+ iter.remove();
+ continue;
+ } else if (entry.getKey().equalsIgnoreCase(PROP_BACKEND_ID)) {
+ backendId = Long.valueOf(entry.getValue());
+ iter.remove();
+ continue;
+ } else if (entry.getKey().equalsIgnoreCase(PROP_EXPIRATION)) {
+ expirationMinutes = Long.valueOf(entry.getValue());
+ expirationMinutes = Math.min(DEFAULT_EXPIRATION_MINUTES,
expirationMinutes);
+ iter.remove();
+ continue;
+ }
+ }
+ } catch (NumberFormatException e) {
+ throw new AnalysisException("Invalid property: " + e.getMessage());
+ }
+
+ if (!properties.isEmpty()) {
+ throw new AnalysisException("Unknown property: " + properties);
+ }
+ }
+
+ @Override
+ public ShowResultSetMetaData getMetaData() {
+ ShowResultSetMetaData.Builder builder =
ShowResultSetMetaData.builder();
+ for (String title : TITLE_NAMES) {
+ builder.addColumn(new Column(title,
ScalarType.createStringType()));
+ }
+ return builder.build();
+ }
+
+ @Override
+ public RedirectStatus getRedirectStatus() {
+ return RedirectStatus.NO_FORWARD;
+ }
+}
+
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/Catalog.java
b/fe/fe-core/src/main/java/org/apache/doris/catalog/Catalog.java
index bd8edc57f3..83ba31b1e3 100755
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/Catalog.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/Catalog.java
@@ -3091,7 +3091,7 @@ public class Catalog {
throw new DdlException("Table[" + table.getName() + "] is
external, not support rollup copy");
}
- Catalog.getDdlStmt(stmt, stmt.getDbName(), table,
createTableStmt, null, null, false, false, true);
+ Catalog.getDdlStmt(stmt, stmt.getDbName(), table,
createTableStmt, null, null, false, false, true, -1L);
if (createTableStmt.isEmpty()) {
ErrorReport.reportDdlException(ErrorCode.ERROR_CREATE_TABLE_LIKE_EMPTY,
"CREATE");
}
@@ -4113,9 +4113,9 @@ public class Catalog {
}
public static void getDdlStmt(Table table, List<String> createTableStmt,
List<String> addPartitionStmt,
- List<String> createRollupStmt, boolean
separatePartition, boolean hidePassword) {
- getDdlStmt(null, null, table, createTableStmt, addPartitionStmt,
createRollupStmt,
- separatePartition, hidePassword, false);
+ List<String> createRollupStmt, boolean separatePartition, boolean
hidePassword, long specificVersion) {
+ getDdlStmt(null, null, table, createTableStmt, addPartitionStmt,
createRollupStmt, separatePartition,
+ hidePassword, false, specificVersion);
}
/**
@@ -4124,8 +4124,8 @@ public class Catalog {
* @param getDdlForLike Get schema for 'create table like' or not. when
true, without hidden columns.
*/
public static void getDdlStmt(DdlStmt ddlStmt, String dbName, Table table,
List<String> createTableStmt,
- List<String> addPartitionStmt, List<String>
createRollupStmt,
- boolean separatePartition, boolean
hidePassword, boolean getDdlForLike) {
+ List<String> addPartitionStmt, List<String> createRollupStmt,
boolean separatePartition,
+ boolean hidePassword, boolean getDdlForLike, long specificVersion)
{
StringBuilder sb = new StringBuilder();
// 1. create table
@@ -4193,6 +4193,16 @@ public class Catalog {
}
sb.append(Joiner.on(", ").join(keysColumnNames)).append(")");
+ if (specificVersion != -1) {
+ // for copy tablet operation
+ sb.append("\nDISTRIBUTED BY
HASH(").append(olapTable.getBaseSchema().get(0).getName())
+ .append(") BUCKETS 1");
+ sb.append("\nPROPERTIES (\n" + "\"replication_num\" =
\"1\",\n" + "\"version_info\" = \""
+ + specificVersion + "\"\n" + ")");
+ createTableStmt.add(sb + ";");
+ return;
+ }
+
if (!Strings.isNullOrEmpty(table.getComment())) {
sb.append("\nCOMMENT
\"").append(table.getComment(true)).append("\"");
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/GetDdlStmtAction.java
b/fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/GetDdlStmtAction.java
index ce6d93ac20..5291b7e800 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/GetDdlStmtAction.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/GetDdlStmtAction.java
@@ -78,7 +78,7 @@ public class GetDdlStmtAction extends RestBaseController {
table.readLock();
try {
- Catalog.getDdlStmt(table, createTableStmt, addPartitionStmt,
createRollupStmt, true, false /* show password */);
+ Catalog.getDdlStmt(table, createTableStmt, addPartitionStmt,
createRollupStmt, true, false /* show password */, -1);
} finally {
table.readUnlock();
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/StmtExecutionAction.java
b/fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/StmtExecutionAction.java
index b2ffd17935..00f8a0d317 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/StmtExecutionAction.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/StmtExecutionAction.java
@@ -177,7 +177,7 @@ public class StmtExecutionAction extends RestBaseController
{
List<String> createStmts = Lists.newArrayList();
for (Table tbl : tableMap.values()) {
List<String> createTableStmts = Lists.newArrayList();
- Catalog.getDdlStmt(tbl, createTableStmts, null, null, false,
true);
+ Catalog.getDdlStmt(tbl, createTableStmts, null, null, false,
true, -1L);
if (!createTableStmts.isEmpty()) {
createStmts.add(createTableStmts.get(0));
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/master/MasterImpl.java
b/fe/fe-core/src/main/java/org/apache/doris/master/MasterImpl.java
index c1a12a840c..c799402c62 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/master/MasterImpl.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/master/MasterImpl.java
@@ -715,10 +715,14 @@ public class MasterImpl {
private void finishMakeSnapshot(AgentTask task, TFinishTaskRequest
request) {
SnapshotTask snapshotTask = (SnapshotTask) task;
- if
(Catalog.getCurrentCatalog().getBackupHandler().handleFinishedSnapshotTask(snapshotTask,
request)) {
- AgentTaskQueue.removeTask(task.getBackendId(),
TTaskType.MAKE_SNAPSHOT, task.getSignature());
+ if (snapshotTask.isCopyTabletTask()) {
+ snapshotTask.setResultSnapshotPath(request.getSnapshotPath());
+ snapshotTask.countDown(task.getBackendId(), task.getTabletId());
+ } else {
+ if
(Catalog.getCurrentCatalog().getBackupHandler().handleFinishedSnapshotTask(snapshotTask,
request)) {
+ AgentTaskQueue.removeTask(task.getBackendId(),
TTaskType.MAKE_SNAPSHOT, task.getSignature());
+ }
}
-
}
private void finishUpload(AgentTask task, TFinishTaskRequest request) {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java
b/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java
index 6ae0f4c349..8e4647d67d 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java
@@ -17,6 +17,7 @@
package org.apache.doris.qe;
+import org.apache.doris.analysis.AdminCopyTabletStmt;
import org.apache.doris.analysis.AdminDiagnoseTabletStmt;
import org.apache.doris.analysis.AdminShowConfigStmt;
import org.apache.doris.analysis.AdminShowReplicaDistributionStmt;
@@ -125,6 +126,7 @@ import org.apache.doris.common.DdlException;
import org.apache.doris.common.ErrorCode;
import org.apache.doris.common.ErrorReport;
import org.apache.doris.common.FeConstants;
+import org.apache.doris.common.MarkedCountDownLatch;
import org.apache.doris.common.MetaNotFoundException;
import org.apache.doris.common.PatternMatcher;
import org.apache.doris.common.proc.BackendsProcDir;
@@ -161,18 +163,22 @@ import org.apache.doris.mysql.privilege.PrivPredicate;
import org.apache.doris.system.Backend;
import org.apache.doris.system.Diagnoser;
import org.apache.doris.system.SystemInfoService;
+import org.apache.doris.task.AgentBatchTask;
import org.apache.doris.task.AgentClient;
+import org.apache.doris.task.AgentTaskExecutor;
+import org.apache.doris.task.AgentTaskQueue;
+import org.apache.doris.task.SnapshotTask;
import org.apache.doris.thrift.TCheckStorageFormatResult;
+import org.apache.doris.thrift.TTaskType;
import org.apache.doris.thrift.TUnit;
import org.apache.doris.transaction.GlobalTransactionMgr;
+import org.apache.doris.transaction.TransactionStatus;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
-
import org.apache.commons.lang3.tuple.Triple;
-import org.apache.doris.transaction.TransactionStatus;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -188,6 +194,7 @@ import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
// Execute one show statement.
@@ -344,6 +351,8 @@ public class ShowExecutor {
handleAdminDiagnoseTablet();
} else if (stmt instanceof ShowCreateMaterializedViewStmt) {
handleShowCreateMaterializedView();
+ } else if (stmt instanceof AdminCopyTabletStmt) {
+ handleCopyTablet();
} else {
handleEmtpy();
}
@@ -807,7 +816,7 @@ public class ShowExecutor {
table.readLock();
try {
List<String> createTableStmt = Lists.newArrayList();
- Catalog.getDdlStmt(table, createTableStmt, null, null, false, true
/* hide password */);
+ Catalog.getDdlStmt(table, createTableStmt, null, null, false, true
/* hide password */, -1L);
if (createTableStmt.isEmpty()) {
resultSet = new ShowResultSet(showStmt.getMetaData(), rows);
return;
@@ -917,7 +926,7 @@ public class ShowExecutor {
view.readLock();
try {
List<String> createViewStmt = Lists.newArrayList();
- Catalog.getDdlStmt(view, createViewStmt, null, null, false,
true /* hide password */);
+ Catalog.getDdlStmt(view, createViewStmt, null, null, false,
true /* hide password */, -1L);
if (!createViewStmt.isEmpty()) {
rows.add(Lists.newArrayList(view.getName(),
createViewStmt.get(0)));
}
@@ -2206,4 +2215,102 @@ public class ShowExecutor {
resultSet = new ShowResultSet(showStmt.getMetaData(), resultRowSet);
}
+ private void handleCopyTablet() throws AnalysisException {
+ AdminCopyTabletStmt copyStmt = (AdminCopyTabletStmt) stmt;
+ long tabletId = copyStmt.getTabletId();
+ long version = copyStmt.getVersion();
+ long backendId = copyStmt.getBackendId();
+
+ TabletInvertedIndex invertedIndex = Catalog.getCurrentInvertedIndex();
+ TabletMeta tabletMeta = invertedIndex.getTabletMeta(tabletId);
+ if (tabletMeta == null) {
+ throw new AnalysisException("Unknown tablet: " + tabletId);
+ }
+
+ // 1. find replica
+ Replica replica = null;
+ if (backendId != -1) {
+ replica = invertedIndex.getReplica(tabletId, backendId);
+ } else {
+ List<Replica> replicas =
invertedIndex.getReplicasByTabletId(tabletId);
+ if (!replicas.isEmpty()) {
+ replica = replicas.get(0);
+ }
+ }
+ if (replica == null) {
+ throw new AnalysisException("Replica not found on backend: " +
backendId);
+ }
+ backendId = replica.getBackendId();
+ Backend be = Catalog.getCurrentSystemInfo().getBackend(backendId);
+ if (be == null || !be.isAlive()) {
+ throw new AnalysisException("Unavailable backend: " + backendId);
+ }
+
+ // 2. find version
+ if (version != -1 && replica.getVersion() < version) {
+ throw new AnalysisException("Version is larger than replica max
version: " + replica.getVersion());
+ }
+ version = version == -1 ? replica.getVersion() : version;
+
+ // 3. get create table stmt
+ Database db =
Catalog.getCurrentCatalog().getDbOrAnalysisException(tabletMeta.getDbId());
+ OlapTable tbl = (OlapTable)
db.getTableNullable(tabletMeta.getTableId());
+ if (tbl == null) {
+ throw new AnalysisException("Failed to find table: " +
tabletMeta.getTableId());
+ }
+
+ List<String> createTableStmt = Lists.newArrayList();
+ tbl.readLock();
+ try {
+ Catalog.getDdlStmt(tbl, createTableStmt, null, null, false, true
/* hide password */, version);
+ } finally {
+ tbl.readUnlock();
+ }
+
+ // 4. create snapshot task
+ SnapshotTask task = new SnapshotTask(null, backendId, tabletId, -1,
tabletMeta.getDbId(),
+ tabletMeta.getTableId(), tabletMeta.getPartitionId(),
tabletMeta.getIndexId(), tabletId, version, tabletMeta.getOldSchemaHash(),
+ copyStmt.getExpirationMinutes() * 60 * 1000, false);
+ task.setIsCopyTabletTask(true);
+ MarkedCountDownLatch<Long, Long> countDownLatch = new
MarkedCountDownLatch<Long, Long>(1);
+ countDownLatch.addMark(backendId, tabletId);
+ task.setCountDownLatch(countDownLatch);
+
+ // 5. send task and wait
+ AgentBatchTask batchTask = new AgentBatchTask();
+ batchTask.addTask(task);
+ try {
+ AgentTaskQueue.addBatchTask(batchTask);
+ AgentTaskExecutor.submit(batchTask);
+
+ boolean ok = false;
+ try {
+ ok = countDownLatch.await(10, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ LOG.warn("InterruptedException: ", e);
+ ok = false;
+ }
+
+ if (!ok) {
+ throw new AnalysisException(
+ "Failed to make snapshot for tablet " + tabletId + "
on backend: " + backendId);
+ }
+
+ // send result
+ List<List<String>> resultRowSet = Lists.newArrayList();
+ List<String> row = Lists.newArrayList();
+ row.add(String.valueOf(tabletId));
+ row.add(String.valueOf(backendId));
+ row.add(be.getHost());
+ row.add(task.getResultSnapshotPath());
+ row.add(String.valueOf(copyStmt.getExpirationMinutes()));
+ row.add(createTableStmt.get(0));
+ resultRowSet.add(row);
+
+ ShowResultSetMetaData showMetaData = copyStmt.getMetaData();
+ resultSet = new ShowResultSet(showMetaData, resultRowSet);
+ } finally {
+ AgentTaskQueue.removeBatchTask(batchTask, TTaskType.MAKE_SNAPSHOT);
+ }
+ }
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/task/SnapshotTask.java
b/fe/fe-core/src/main/java/org/apache/doris/task/SnapshotTask.java
index 7da6a5afea..71b3570f28 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/task/SnapshotTask.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/task/SnapshotTask.java
@@ -17,6 +17,7 @@
package org.apache.doris.task;
+import org.apache.doris.common.MarkedCountDownLatch;
import org.apache.doris.thrift.TResourceInfo;
import org.apache.doris.thrift.TSnapshotRequest;
import org.apache.doris.thrift.TTaskType;
@@ -24,18 +25,22 @@ import org.apache.doris.thrift.TypesConstants;
public class SnapshotTask extends AgentTask {
private long jobId;
-
private long version;
-
private int schemaHash;
-
private long timeoutMs;
-
private boolean isRestoreTask;
- public SnapshotTask(TResourceInfo resourceInfo, long backendId, long
signature, long jobId,
- long dbId, long tableId, long partitionId, long indexId, long
tabletId,
- long version, int schemaHash, long timeoutMs, boolean
isRestoreTask) {
+ // Set to true if this task for AdminCopyTablet.
+ // Otherwise, it is for Backup/Restore operation.
+ private boolean isCopyTabletTask = false;
+ private MarkedCountDownLatch<Long, Long> countDownLatch;
+ // Only for copy tablet task.
+ // Save the snapshot path.
+ private String resultSnapshotPath;
+
+ public SnapshotTask(TResourceInfo resourceInfo, long backendId, long
signature, long jobId, long dbId, long tableId,
+ long partitionId, long indexId, long tabletId, long version, int
schemaHash, long timeoutMs,
+ boolean isRestoreTask) {
super(resourceInfo, backendId, TTaskType.MAKE_SNAPSHOT, dbId, tableId,
partitionId, indexId, tabletId,
signature);
@@ -49,6 +54,22 @@ public class SnapshotTask extends AgentTask {
this.isRestoreTask = isRestoreTask;
}
+ public void setIsCopyTabletTask(boolean value) {
+ this.isCopyTabletTask = value;
+ }
+
+ public boolean isCopyTabletTask() {
+ return isCopyTabletTask;
+ }
+
+ public void setCountDownLatch(MarkedCountDownLatch<Long, Long>
countDownLatch) {
+ this.countDownLatch = countDownLatch;
+ }
+
+ public void countDown(long backendId, long tabletId) {
+ this.countDownLatch.markedCountDown(backendId, tabletId);
+ }
+
public long getJobId() {
return jobId;
}
@@ -69,12 +90,21 @@ public class SnapshotTask extends AgentTask {
return isRestoreTask;
}
+ public void setResultSnapshotPath(String resultSnapshotPath) {
+ this.resultSnapshotPath = resultSnapshotPath;
+ }
+
+ public String getResultSnapshotPath() {
+ return resultSnapshotPath;
+ }
+
public TSnapshotRequest toThrift() {
TSnapshotRequest request = new TSnapshotRequest(tabletId, schemaHash);
request.setVersion(version);
request.setListFiles(true);
request.setPreferredSnapshotVersion(TypesConstants.TPREFER_SNAPSHOT_REQ_VERSION);
request.setTimeout(timeoutMs / 1000);
+ request.setIsCopyTabletTask(isCopyTabletTask);
return request;
}
}
diff --git a/fe/fe-core/src/main/jflex/sql_scanner.flex
b/fe/fe-core/src/main/jflex/sql_scanner.flex
index faaf4fdfe5..edabe681e6 100644
--- a/fe/fe-core/src/main/jflex/sql_scanner.flex
+++ b/fe/fe-core/src/main/jflex/sql_scanner.flex
@@ -143,6 +143,7 @@ import org.apache.doris.qe.SqlModeHelper;
keywordMap.put("connection_id", new
Integer(SqlParserSymbols.KW_CONNECTION_ID));
keywordMap.put("consistent", new
Integer(SqlParserSymbols.KW_CONSISTENT));
keywordMap.put("convert", new Integer(SqlParserSymbols.KW_CONVERT));
+ keywordMap.put("copy", new Integer(SqlParserSymbols.KW_COPY));
keywordMap.put("count", new Integer(SqlParserSymbols.KW_COUNT));
keywordMap.put("create", new Integer(SqlParserSymbols.KW_CREATE));
keywordMap.put("creation", new Integer(SqlParserSymbols.KW_CREATION));
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/catalog/CreateTableLikeTest.java
b/fe/fe-core/src/test/java/org/apache/doris/catalog/CreateTableLikeTest.java
index bda5ea67bd..8d90c0a08a 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/catalog/CreateTableLikeTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/catalog/CreateTableLikeTest.java
@@ -25,13 +25,12 @@ import org.apache.doris.common.ExceptionChecker;
import org.apache.doris.qe.ConnectContext;
import org.apache.doris.utframe.UtFrameUtils;
+import com.google.common.collect.Lists;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
-import com.google.common.collect.Lists;
-
import java.io.File;
import java.util.List;
import java.util.UUID;
@@ -80,11 +79,13 @@ public class CreateTableLikeTest {
private static void checkTableEqual(Table newTable, Table existedTable,
int rollupSize) {
List<String> newCreateTableStmt = Lists.newArrayList();
List<String> newAddRollupStmt = Lists.newArrayList();
- Catalog.getDdlStmt(newTable, newCreateTableStmt, null,
newAddRollupStmt, false, true /* hide password */);
+ Catalog.getDdlStmt(newTable, newCreateTableStmt, null,
newAddRollupStmt, false, true /* hide password */, -1L);
List<String> existedTableStmt = Lists.newArrayList();
List<String> existedAddRollupStmt = Lists.newArrayList();
- Catalog.getDdlStmt(existedTable, existedTableStmt, null,
existedAddRollupStmt, false, true /* hide password */);
-
Assert.assertEquals(newCreateTableStmt.get(0).replace(newTable.getName(),
existedTable.getName()), existedTableStmt.get(0));
+ Catalog.getDdlStmt(existedTable, existedTableStmt, null,
existedAddRollupStmt, false, true /* hide password */,
+ -1L);
+
Assert.assertEquals(newCreateTableStmt.get(0).replace(newTable.getName(),
existedTable.getName()),
+ existedTableStmt.get(0));
checkTableRollup(existedAddRollupStmt, newAddRollupStmt,
newTable.getName(), existedTable.getName(), rollupSize);
}
diff --git a/fe/fe-core/src/test/java/org/apache/doris/qe/ShowExecutorTest.java
b/fe/fe-core/src/test/java/org/apache/doris/qe/ShowExecutorTest.java
index a6aa8b8084..fd46949d35 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/qe/ShowExecutorTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/qe/ShowExecutorTest.java
@@ -200,10 +200,10 @@ public class ShowExecutorTest {
minTimes = 0;
result = catalog;
- Catalog.getDdlStmt((Table) any, (List) any, (List) any, (List)
any, anyBoolean, anyBoolean);
+ Catalog.getDdlStmt((Table) any, (List) any, (List) any, (List)
any, anyBoolean, anyBoolean, anyLong);
minTimes = 0;
- Catalog.getDdlStmt((Table) any, (List) any, null, null,
anyBoolean, anyBoolean);
+ Catalog.getDdlStmt((Table) any, (List) any, null, null,
anyBoolean, anyBoolean, anyLong);
minTimes = 0;
}
};
diff --git a/gensrc/thrift/AgentService.thrift
b/gensrc/thrift/AgentService.thrift
index 62855abfe1..6301cef6ca 100644
--- a/gensrc/thrift/AgentService.thrift
+++ b/gensrc/thrift/AgentService.thrift
@@ -250,6 +250,7 @@ struct TSnapshotRequest {
// Deprecated since version 0.13
8: optional bool allow_incremental_clone
9: optional i32 preferred_snapshot_version =
Types.TPREFER_SNAPSHOT_REQ_VERSION
+ 10: optional bool is_copy_tablet_task
}
struct TReleaseSnapshotRequest {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]