Skip to content

Commit ae138c3

Browse files
committed
Added describe_replication method for embedded UI (ydb-platform#16059)
1 parent 614dbb5 commit ae138c3

File tree

6 files changed

+338
-1
lines changed

6 files changed

+338
-1
lines changed

ydb/core/grpc_services/rpc_replication.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,4 +246,11 @@ void DoDescribeReplication(std::unique_ptr<IRequestOpCtx> p, const IFacilityProv
246246
f.RegisterActor(new TDescribeReplicationRPC(p.release()));
247247
}
248248

249+
using TEvDescribeReplicationRequest = TGrpcRequestOperationCall<Ydb::Replication::DescribeReplicationRequest, Ydb::Replication::DescribeReplicationResponse>;
250+
251+
template<>
252+
IActor* TEvDescribeReplicationRequest::CreateRpcActor(NKikimr::NGRpcService::IRequestOpCtx* msg) {
253+
return new TDescribeReplicationRPC(msg);
254+
}
255+
249256
}

ydb/core/viewer/json_handlers_viewer.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "viewer_counters.h"
1111
#include "viewer_describe_consumer.h"
1212
#include "viewer_describe.h"
13+
#include "viewer_describe_replication.h"
1314
#include "viewer_describe_topic.h"
1415
#include "viewer_feature_flags.h"
1516
#include "viewer_topic_data.h"
@@ -156,6 +157,10 @@ void InitViewerDescribeJsonHandler(TJsonHandlers& jsonHandlers) {
156157
jsonHandlers.AddHandler("/viewer/describe", new TJsonHandler<TJsonDescribe>(TJsonDescribe::GetSwagger()));
157158
}
158159

160+
void InitViewerDescribeReplicationJsonHandler(TJsonHandlers& jsonHandlers) {
161+
jsonHandlers.AddHandler("/viewer/describe_replication", new TJsonHandler<TJsonDescribeReplication>(TJsonDescribeReplication::GetSwagger()));
162+
}
163+
159164
void InitViewerDescribeTopicJsonHandler(TJsonHandlers& jsonHandlers) {
160165
jsonHandlers.AddHandler("/viewer/describe_topic", new TJsonHandler<TJsonDescribeTopic>(TJsonDescribeTopic::GetSwagger()));
161166
}
@@ -299,6 +304,7 @@ void InitViewerJsonHandlers(TJsonHandlers& jsonHandlers) {
299304
InitViewerPDiskInfoJsonHandler(jsonHandlers);
300305
InitViewerTabletInfoJsonHandler(jsonHandlers);
301306
InitViewerDescribeJsonHandler(jsonHandlers);
307+
InitViewerDescribeReplicationJsonHandler(jsonHandlers);
302308
InitViewerDescribeTopicJsonHandler(jsonHandlers);
303309
InitViewerDescribeConsumerJsonHandler(jsonHandlers);
304310
InitViewerHotkeysJsonHandler(jsonHandlers);

ydb/core/viewer/tests/canondata/result.json

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,218 @@
506506
],
507507
"TotalGroups": 5
508508
},
509+
"test.test_topic_data": {
510+
"response_compressed": {
511+
"EndOffset": 21,
512+
"Messages": [
513+
{
514+
"Codec": 1,
515+
"Message": "Y29tcHJlc3NlZC1tZXNzYWdlLTA=",
516+
"Offset": 11,
517+
"OriginalSize": 20,
518+
"SeqNo": 12,
519+
"StorageSize": 38
520+
},
521+
{
522+
"Codec": 1,
523+
"Message": "Y29tcHJlc3NlZC1tZXNzYWdlLTE=",
524+
"Offset": 12,
525+
"OriginalSize": 20,
526+
"SeqNo": 13,
527+
"StorageSize": 38
528+
},
529+
{
530+
"Codec": 1,
531+
"Message": "Y29tcHJlc3NlZC1tZXNzYWdlLTI=",
532+
"Offset": 13,
533+
"OriginalSize": 20,
534+
"SeqNo": 14,
535+
"StorageSize": 38
536+
},
537+
{
538+
"Codec": 1,
539+
"Message": "Y29tcHJlc3NlZC1tZXNzYWdlLTM=",
540+
"Offset": 14,
541+
"OriginalSize": 20,
542+
"SeqNo": 15,
543+
"StorageSize": 38
544+
},
545+
{
546+
"Codec": 1,
547+
"Message": "Y29tcHJlc3NlZC1tZXNzYWdlLTQ=",
548+
"Offset": 15,
549+
"OriginalSize": 20,
550+
"SeqNo": 16,
551+
"StorageSize": 38
552+
}
553+
],
554+
"StartOffset": 0,
555+
"Truncated": true
556+
},
557+
"response_metadata": {
558+
"EndOffset": 21,
559+
"Messages": [
560+
{
561+
"Codec": 1,
562+
"Message": "bWVzc2FnZV93aXRoX21ldGE=",
563+
"MessageMetadata": [
564+
{
565+
"Key": "key1",
566+
"Value": "value1"
567+
},
568+
{
569+
"Key": "key2",
570+
"Value": "value2"
571+
}
572+
],
573+
"Offset": 10,
574+
"OriginalSize": 17,
575+
"SeqNo": 11,
576+
"StorageSize": 37
577+
}
578+
],
579+
"StartOffset": 0,
580+
"Truncated": true
581+
},
582+
"response_not_truncated": {
583+
"EndOffset": 21,
584+
"Messages": [
585+
{
586+
"Codec": 1,
587+
"Message": "Y29tcHJlc3NlZC1tZXNzYWdlLTk=",
588+
"Offset": 20,
589+
"OriginalSize": 20,
590+
"SeqNo": 21,
591+
"StorageSize": 38
592+
}
593+
],
594+
"StartOffset": 0,
595+
"Truncated": false
596+
},
597+
"response_read": {
598+
"EndOffset": 21,
599+
"Messages": [
600+
{
601+
"Codec": 0,
602+
"Message": "bWVzc2FnZS0w",
603+
"Offset": 0,
604+
"OriginalSize": 9,
605+
"SeqNo": 1,
606+
"StorageSize": 9
607+
},
608+
{
609+
"Codec": 0,
610+
"Message": "bWVzc2FnZS0x",
611+
"Offset": 1,
612+
"OriginalSize": 9,
613+
"SeqNo": 2,
614+
"StorageSize": 9
615+
},
616+
{
617+
"Codec": 0,
618+
"Message": "bWVzc2FnZS0y",
619+
"Offset": 2,
620+
"OriginalSize": 9,
621+
"SeqNo": 3,
622+
"StorageSize": 9
623+
},
624+
{
625+
"Codec": 0,
626+
"Message": "bWVzc2FnZS0z",
627+
"Offset": 3,
628+
"OriginalSize": 9,
629+
"SeqNo": 4,
630+
"StorageSize": 9
631+
},
632+
{
633+
"Codec": 0,
634+
"Message": "bWVzc2FnZS00",
635+
"Offset": 4,
636+
"OriginalSize": 9,
637+
"SeqNo": 5,
638+
"StorageSize": 9
639+
}
640+
],
641+
"StartOffset": 0,
642+
"Truncated": true
643+
}
644+
},
645+
646+
"test.test_transfer_describe": {
647+
"connection_params": {
648+
"connection_string": "text",
649+
"database": "/Root/dedicated_db",
650+
"endpoint": "text",
651+
"oauth": {}
652+
},
653+
"error": {
654+
"issues": [
655+
{
656+
"message": "Discovery error: /Root/dedicated_db/TopicNotExists: SCHEME_ERROR ({ <main>: Error: Path not found })",
657+
"severity": 1
658+
}
659+
]
660+
},
661+
"row_consistency": {},
662+
"self": {
663+
"created_at": {
664+
"plan_step": "not-zero-number-text",
665+
"tx_id": "not-zero-number-text"
666+
},
667+
"effective_permissions": [
668+
{
669+
"permission_names": [
670+
"ydb.database.connect"
671+
],
672+
"subject": "USERS"
673+
},
674+
{
675+
"permission_names": [
676+
"ydb.generic.list"
677+
],
678+
"subject": "METADATA-READERS"
679+
},
680+
{
681+
"permission_names": [
682+
"ydb.granular.select_row"
683+
],
684+
"subject": "DATA-READERS"
685+
},
686+
{
687+
"permission_names": [
688+
"ydb.tables.modify"
689+
],
690+
"subject": "DATA-WRITERS"
691+
},
692+
{
693+
"permission_names": [
694+
"ydb.granular.create_directory",
695+
"ydb.granular.write_attributes",
696+
"ydb.granular.create_table",
697+
"ydb.granular.remove_schema",
698+
"ydb.granular.create_queue",
699+
"ydb.granular.alter_schema"
700+
],
701+
"subject": "DDL-ADMINS"
702+
},
703+
{
704+
"permission_names": [
705+
"ydb.access.grant"
706+
],
707+
"subject": "ACCESS-ADMINS"
708+
},
709+
{
710+
"permission_names": [
711+
"ydb.generic.manage"
712+
],
713+
"subject": "DATABASE-ADMINS"
714+
}
715+
],
716+
"name": "TestTransfer",
717+
"owner": "root@builtin",
718+
"type": "TRANSFER"
719+
}
720+
},
509721
"test.test_viewer_acl": {
510722
"/Root": {
511723
"Common": {

ydb/core/viewer/tests/test.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import time
1212

1313

14-
cluster = KiKiMR(KikimrConfigGenerator(enable_alter_database_create_hive_first=True, extra_feature_flags=["enable_resource_pools"]))
14+
cluster = KiKiMR(KikimrConfigGenerator(extra_feature_flags=["enable_alter_database_create_hive_first", "enable_topic_transfer", "enable_resource_pools"]))
1515
cluster.start()
1616
domain_name = '/' + cluster.domain_name
1717
dedicated_db = domain_name + "/dedicated_db"
@@ -402,6 +402,15 @@ def normalize_result_healthcheck(result):
402402
return result
403403

404404

405+
def normalize_result_replication(result):
406+
result = replace_values_by_key(result, ['connection_string',
407+
'endpoint',
408+
'plan_step',
409+
'tx_id'])
410+
delete_keys_recursively(result, ['issue_log'])
411+
return result
412+
413+
405414
def normalize_result(result):
406415
delete_keys_recursively(result, ['Version',
407416
'MemoryUsed',
@@ -421,6 +430,7 @@ def normalize_result(result):
421430
result = normalize_result_pdisks(result)
422431
result = normalize_result_vdisks(result)
423432
result = normalize_result_cluster(result)
433+
result = normalize_result_replication(result)
424434
return result
425435

426436

@@ -680,3 +690,30 @@ def replace_values(resp):
680690
'response_last_offset': replace_values(response_cut_by_last_offset),
681691
}
682692
return result
693+
694+
695+
def test_transfer_describe():
696+
grpc_port = cluster.nodes[1].grpc_port
697+
endpoint = "grpc://localhost:{}/?database={}".format(grpc_port, dedicated_db)
698+
lambd = "($x) -> { RETURN <|Id:$x._offset|>; }"
699+
700+
call_viewer("/viewer/query", {
701+
'database': dedicated_db,
702+
'query': 'CREATE TABLE `TransferTargetTable` ( `Id` Uint64 NOT NULL PRIMARY KEY (Id)) WITH (STORE = COLUMN)',
703+
'schema': 'multi'
704+
})
705+
706+
call_viewer("/viewer/query", {
707+
'database': dedicated_db,
708+
'query': 'CREATE TRANSFER `TestTransfer` FROM `TopicNotExists` TO `Table` USING {} WITH (CONNECTION_STRING = "{}")'.format(lambd, endpoint),
709+
'schema': 'multi'
710+
})
711+
712+
result = get_viewer_normalized("/viewer/describe_replication", {
713+
'database': dedicated_db,
714+
'path': '{}/TestTransfer'.format(dedicated_db),
715+
'include_stats': 'true',
716+
'enums': 'true'
717+
})
718+
719+
return result
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#include "json_handlers.h"
2+
#include "json_local_rpc.h"
3+
4+
#include <ydb/core/grpc_services/rpc_calls.h>
5+
#include <ydb/core/grpc_services/service_replication.h>
6+
#include <ydb/services/replication/grpc_service.h>
7+
8+
namespace NKikimr::NGRpcService {
9+
using TEvDescribeReplicationRequest = TGrpcRequestOperationCall<Ydb::Replication::DescribeReplicationRequest, Ydb::Replication::DescribeReplicationResponse>;
10+
}
11+
12+
namespace NKikimr::NViewer {
13+
14+
using TDescribeReplicationRpc = TJsonLocalRpc<Ydb::Replication::DescribeReplicationRequest,
15+
Ydb::Replication::DescribeReplicationResponse,
16+
Ydb::Replication::DescribeReplicationResult,
17+
Ydb::Replication::V1::ReplicationService,
18+
NKikimr::NGRpcService::TEvDescribeReplicationRequest>;
19+
20+
class TJsonDescribeReplication : public TDescribeReplicationRpc {
21+
public:
22+
using TBase = TDescribeReplicationRpc;
23+
24+
TJsonDescribeReplication(IViewer* viewer, NMon::TEvHttpInfo::TPtr& ev)
25+
: TBase(viewer, ev)
26+
{
27+
AllowedMethods = {HTTP_METHOD_GET};
28+
}
29+
30+
static YAML::Node GetSwagger() {
31+
TSimpleYamlBuilder yaml({
32+
.Method = "get",
33+
.Tag = "viewer",
34+
.Summary = "Replication schema detailed information",
35+
.Description = "Returns detailed information about replication",
36+
});
37+
yaml.AddParameter({
38+
.Name = "database",
39+
.Description = "database name",
40+
.Type = "string",
41+
.Required = true,
42+
});
43+
yaml.AddParameter({
44+
.Name = "path",
45+
.Description = "schema path",
46+
.Type = "string",
47+
.Required = true,
48+
});
49+
yaml.AddParameter({
50+
.Name = "include_stats",
51+
.Description = "include stat flag",
52+
.Type = "bool",
53+
});
54+
yaml.AddParameter({
55+
.Name = "timeout",
56+
.Description = "timeout in ms",
57+
.Type = "integer",
58+
});
59+
yaml.AddParameter({
60+
.Name = "enums",
61+
.Description = "convert enums to strings",
62+
.Type = "boolean",
63+
});
64+
yaml.AddParameter({
65+
.Name = "ui64",
66+
.Description = "return ui64 as number",
67+
.Required = false,
68+
});
69+
yaml.SetResponseSchema(TProtoToYaml::ProtoToYamlSchema<Ydb::Replication::DescribeReplicationResult>());
70+
return yaml;
71+
}
72+
};
73+
74+
}

0 commit comments

Comments
 (0)