2
2
3
3
#include < ydb/public/lib/json_value/ydb_json_value.h>
4
4
#include < ydb/public/lib/ydb_cli/common/pretty_table.h>
5
- #include < ydb/public/lib/ydb_cli/common/print_utils.h>
6
5
#include < ydb/public/lib/ydb_cli/common/scheme_printers.h>
7
- #include < ydb/public/sdk/cpp/client/ydb_proto/accessor.h>
8
- #include < google/protobuf/port_def.inc>
6
+ #include < ydb/public/sdk/cpp/client/ydb_topic/topic.h>
9
7
10
8
#include < util/string/join.h>
11
9
@@ -125,6 +123,90 @@ void PrintAllPermissions(
125
123
PrintPermissions (effectivePermissions);
126
124
}
127
125
126
+ int PrintPrettyDescribeConsumerResult (const NYdb::NTopic::TConsumerDescription& description, bool withPartitionsStats) {
127
+ // Consumer info
128
+ const NYdb::NTopic::TConsumer& consumer = description.GetConsumer ();
129
+ Cout << " Consumer " << consumer.GetConsumerName () << " : " << Endl;
130
+ Cout << " Important: " << (consumer.GetImportant () ? " Yes" : " No" ) << Endl;
131
+ if (const TInstant& readFrom = consumer.GetReadFrom ()) {
132
+ Cout << " Read from: " << readFrom.ToRfc822StringLocal () << Endl;
133
+ } else {
134
+ Cout << " Read from: 0" << Endl;
135
+ }
136
+ Cout << " Supported codecs: " << JoinSeq (" , " , consumer.GetSupportedCodecs ()) << Endl;
137
+
138
+ if (const auto & attrs = consumer.GetAttributes (); !attrs.empty ()) {
139
+ TPrettyTable attrTable ({ " Attribute" , " Value" }, TPrettyTableConfig ().WithoutRowDelimiters ());
140
+ for (const auto & [k, v] : attrs) {
141
+ attrTable.AddRow ()
142
+ .Column (0 , k)
143
+ .Column (1 , v);
144
+ }
145
+ Cout << " Attributes:" << Endl << attrTable;
146
+ }
147
+
148
+ // Partitions
149
+ TVector<TString> columnNames = {
150
+ " #" ,
151
+ " Active" ,
152
+ " ChildIds" ,
153
+ " ParentIds"
154
+ };
155
+
156
+ size_t statsBase = columnNames.size ();
157
+ if (withPartitionsStats) {
158
+ columnNames.insert (columnNames.end (),
159
+ {
160
+ " Start offset" ,
161
+ " End offset" ,
162
+ " Size" ,
163
+ " Last write time" ,
164
+ " Max write time lag" ,
165
+ " Written size per minute" ,
166
+ " Written size per hour" ,
167
+ " Written size per day" ,
168
+ " Committed offset" ,
169
+ " Last read offset" ,
170
+ " Reader name" ,
171
+ " Read session id"
172
+ }
173
+ );
174
+ }
175
+
176
+ TPrettyTable partitionsTable (columnNames, TPrettyTableConfig ().WithoutRowDelimiters ());
177
+ for (const NYdb::NTopic::TPartitionInfo& partition : description.GetPartitions ()) {
178
+ auto & row = partitionsTable.AddRow ();
179
+ row
180
+ .Column (0 , partition.GetPartitionId ())
181
+ .Column (1 , partition.GetActive ())
182
+ .Column (2 , JoinSeq (" ," , partition.GetChildPartitionIds ()))
183
+ .Column (3 , JoinSeq (" ," , partition.GetParentPartitionIds ()));
184
+ if (withPartitionsStats) {
185
+ if (const auto & maybeStats = partition.GetPartitionStats ()) {
186
+ row
187
+ .Column (statsBase + 0 , maybeStats->GetStartOffset ())
188
+ .Column (statsBase + 1 , maybeStats->GetEndOffset ())
189
+ .Column (statsBase + 2 , PrettySize (maybeStats->GetStoreSizeBytes ()))
190
+ .Column (statsBase + 3 , FormatTime (maybeStats->GetLastWriteTime ()))
191
+ .Column (statsBase + 4 , FormatDuration (maybeStats->GetMaxWriteTimeLag ()))
192
+ .Column (statsBase + 5 , PrettySize (maybeStats->GetBytesWrittenPerMinute ()))
193
+ .Column (statsBase + 6 , PrettySize (maybeStats->GetBytesWrittenPerHour ()))
194
+ .Column (statsBase + 7 , PrettySize (maybeStats->GetBytesWrittenPerDay ()));
195
+ }
196
+
197
+ if (const auto & maybeStats = partition.GetPartitionConsumerStats ()) {
198
+ row
199
+ .Column (statsBase + 8 , maybeStats->GetCommittedOffset ())
200
+ .Column (statsBase + 9 , maybeStats->GetLastReadOffset ())
201
+ .Column (statsBase + 10 , maybeStats->GetReaderName ())
202
+ .Column (statsBase + 11 , maybeStats->GetReadSessionId ());
203
+ }
204
+ }
205
+ }
206
+ Cout << " Partitions:" << Endl << partitionsTable;
207
+ return EXIT_SUCCESS;
208
+ }
209
+
128
210
TCommandDescribe::TCommandDescribe ()
129
211
: TYdbOperationCommand(" describe" , std::initializer_list<TString>(), " Show information about object at given object" )
130
212
{}
@@ -138,14 +220,14 @@ void TCommandDescribe::Config(TConfig& config) {
138
220
config.Opts ->AddLongOption (" partition-boundaries" , " [Table] Show partition key boundaries" ).StoreTrue (&ShowKeyShardBoundaries)
139
221
.AddLongName (" shard-boundaries" );
140
222
config.Opts ->AddLongOption (" stats" , " [Table|Topic|Replication] Show table/topic/replication statistics" ).StoreTrue (&ShowStats);
141
- config.Opts ->AddLongOption (" partition-stats" , " [Table|Topic] Show partition statistics" ).StoreTrue (&ShowPartitionStats);
223
+ config.Opts ->AddLongOption (" partition-stats" , " [Table|Topic|Consumer ] Show partition statistics" ).StoreTrue (&ShowPartitionStats);
142
224
143
225
AddDeprecatedJsonOption (config, " (Deprecated, will be removed soon. Use --format option instead) [Table] Output in json format" );
144
226
AddFormats (config, { EOutputFormat::Pretty, EOutputFormat::ProtoJsonBase64 });
145
227
config.Opts ->MutuallyExclusive (" json" , " format" );
146
228
147
229
config.SetFreeArgsNum (1 );
148
- SetFreeArgTitle (0 , " <path>" , " Path to an object to describe" );
230
+ SetFreeArgTitle (0 , " <path>" , " Path to an object to describe. If object is topic consumer, it must be specified as <topic_path>/<consumer_name> " );
149
231
}
150
232
151
233
void TCommandDescribe::Parse (TConfig& config) {
@@ -161,6 +243,9 @@ int TCommandDescribe::Run(TConfig& config) {
161
243
Path,
162
244
FillSettings (NScheme::TDescribePathSettings ())
163
245
).GetValueSync ();
246
+ if (!result.IsSuccess ()) {
247
+ return TryTopicConsumerDescribeOrFail (driver, result);
248
+ }
164
249
ThrowOnError (result);
165
250
return PrintPathResponse (driver, result);
166
251
}
@@ -294,50 +379,6 @@ int TCommandDescribe::PrintTopicResponsePretty(const NYdb::NTopic::TTopicDescrip
294
379
return EXIT_SUCCESS;
295
380
}
296
381
297
- template <typename T>
298
- static int PrintProtoJsonBase64 (const T& msg) {
299
- using namespace google ::protobuf::util;
300
-
301
- TString json;
302
- JsonPrintOptions opts;
303
- opts.preserve_proto_field_names = true ;
304
- const auto status = MessageToJsonString (msg, &json, opts);
305
-
306
- if (!status.ok ()) {
307
- #if PROTOBUF_VERSION >= 4022005
308
- Cerr << " Error occurred while converting proto to json: " << status.message () << Endl;
309
- #else
310
- Cerr << " Error occurred while converting proto to json: " << status.message ().ToString () << Endl;
311
- #endif
312
- return EXIT_FAILURE;
313
- }
314
-
315
- Cout << json << Endl;
316
- return EXIT_SUCCESS;
317
- }
318
-
319
- template <typename T>
320
- using TPrettyPrinter = int (TCommandDescribe::*)(const T&) const ;
321
-
322
- template <typename T>
323
- static int PrintDescription (TCommandDescribe* self, EOutputFormat format, const T& value, TPrettyPrinter<T> prettyFunc) {
324
- switch (format) {
325
- case EOutputFormat::Default:
326
- case EOutputFormat::Pretty:
327
- return std::invoke (prettyFunc, self, value);
328
- case EOutputFormat::Json:
329
- Cerr << " Warning! Option --json is deprecated and will be removed soon. "
330
- << " Use \" --format proto-json-base64\" option instead." << Endl;
331
- [[fallthrough]];
332
- case EOutputFormat::ProtoJsonBase64:
333
- return PrintProtoJsonBase64 (TProtoAccessor::GetProto (value));
334
- default :
335
- throw TMisuseException () << " This command doesn't support " << format << " output format" ;
336
- }
337
-
338
- return EXIT_SUCCESS;
339
- }
340
-
341
382
int TCommandDescribe::DescribeTopic (TDriver& driver) {
342
383
NYdb::NTopic::TTopicClient topicClient (driver);
343
384
NYdb::NTopic::TDescribeTopicSettings settings;
@@ -861,6 +902,43 @@ int TCommandDescribe::PrintTableResponsePretty(const NTable::TTableDescription&
861
902
return EXIT_SUCCESS;
862
903
}
863
904
905
+ std::pair<TString, TString> TCommandDescribe::ParseTopicConsumer () const {
906
+ const size_t slashPos = Path.find_last_of (' /' );
907
+ std::pair<TString, TString> result;
908
+ if (slashPos != TString::npos && slashPos != Path.size () - 1 ) {
909
+ result.first = Path.substr (0 , slashPos);
910
+ result.second = Path.substr (slashPos + 1 );
911
+ }
912
+ return result;
913
+ }
914
+
915
+ int TCommandDescribe::TryTopicConsumerDescribeOrFail (TDriver& driver, const NScheme::TDescribePathResult& result) {
916
+ auto [topic, consumer] = ParseTopicConsumer ();
917
+ if (!topic || !consumer) {
918
+ ThrowOnError (result); // no consumer can be found
919
+ }
920
+
921
+ NScheme::TSchemeClient client (driver);
922
+ NScheme::TDescribePathResult topicDescribeResult = client.DescribePath (
923
+ topic,
924
+ FillSettings (NScheme::TDescribePathSettings ())
925
+ ).GetValueSync ();
926
+ if (!topicDescribeResult.IsSuccess () || topicDescribeResult.GetEntry ().Type != NScheme::ESchemeEntryType::Topic && topicDescribeResult.GetEntry ().Type != NScheme::ESchemeEntryType::PqGroup) {
927
+ ThrowOnError (result); // return previous error, this is not topic
928
+ }
929
+
930
+ // OK, this is topic, check the consumer
931
+ NYdb::NTopic::TTopicClient topicClient (driver);
932
+ auto consumerDescription = topicClient.DescribeConsumer (topic, consumer, NYdb::NTopic::TDescribeConsumerSettings ().IncludeStats (ShowPartitionStats)).GetValueSync ();
933
+ ThrowOnError (consumerDescription);
934
+
935
+ return PrintDescription (this , OutputFormat, consumerDescription.GetConsumerDescription (), &TCommandDescribe::PrintConsumerResponsePretty);
936
+ }
937
+
938
+ int TCommandDescribe::PrintConsumerResponsePretty (const NYdb::NTopic::TConsumerDescription& description) const {
939
+ return PrintPrettyDescribeConsumerResult (description, ShowPartitionStats);
940
+ }
941
+
864
942
void TCommandDescribe::WarnAboutTableOptions () {
865
943
if (ShowKeyShardBoundaries || ShowStats || ShowPartitionStats || OutputFormat != EOutputFormat::Default) {
866
944
TVector<TString> options;
0 commit comments