22
33#include < ydb/public/lib/json_value/ydb_json_value.h>
44#include < ydb/public/lib/ydb_cli/common/pretty_table.h>
5- #include < ydb/public/lib/ydb_cli/common/print_utils.h>
65#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>
97
108#include < util/string/join.h>
119
@@ -125,6 +123,90 @@ void PrintAllPermissions(
125123 PrintPermissions (effectivePermissions);
126124}
127125
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+
128210TCommandDescribe::TCommandDescribe ()
129211 : TYdbOperationCommand(" describe" , std::initializer_list<TString>(), " Show information about object at given object" )
130212{}
@@ -138,14 +220,14 @@ void TCommandDescribe::Config(TConfig& config) {
138220 config.Opts ->AddLongOption (" partition-boundaries" , " [Table] Show partition key boundaries" ).StoreTrue (&ShowKeyShardBoundaries)
139221 .AddLongName (" shard-boundaries" );
140222 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);
142224
143225 AddDeprecatedJsonOption (config, " (Deprecated, will be removed soon. Use --format option instead) [Table] Output in json format" );
144226 AddFormats (config, { EOutputFormat::Pretty, EOutputFormat::ProtoJsonBase64 });
145227 config.Opts ->MutuallyExclusive (" json" , " format" );
146228
147229 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> " );
149231}
150232
151233void TCommandDescribe::Parse (TConfig& config) {
@@ -161,6 +243,9 @@ int TCommandDescribe::Run(TConfig& config) {
161243 Path,
162244 FillSettings (NScheme::TDescribePathSettings ())
163245 ).GetValueSync ();
246+ if (!result.IsSuccess ()) {
247+ return TryTopicConsumerDescribeOrFail (driver, result);
248+ }
164249 ThrowOnError (result);
165250 return PrintPathResponse (driver, result);
166251}
@@ -294,50 +379,6 @@ int TCommandDescribe::PrintTopicResponsePretty(const NYdb::NTopic::TTopicDescrip
294379 return EXIT_SUCCESS;
295380}
296381
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-
341382int TCommandDescribe::DescribeTopic (TDriver& driver) {
342383 NYdb::NTopic::TTopicClient topicClient (driver);
343384 NYdb::NTopic::TDescribeTopicSettings settings;
@@ -861,6 +902,43 @@ int TCommandDescribe::PrintTableResponsePretty(const NTable::TTableDescription&
861902 return EXIT_SUCCESS;
862903}
863904
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+
864942void TCommandDescribe::WarnAboutTableOptions () {
865943 if (ShowKeyShardBoundaries || ShowStats || ShowPartitionStats || OutputFormat != EOutputFormat::Default) {
866944 TVector<TString> options;
0 commit comments