Skip to content

Commit 68d4894

Browse files
authored
Merge pull request #1638 Added log grpc messages metadata on trace log level for topic writer
2 parents 7ec7b21 + c66c90a commit 68d4894

File tree

11 files changed

+297
-35
lines changed

11 files changed

+297
-35
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
* Added log grpc messages metadata on trace log level for topic writer
2+
13
## v3.99.5
24
* Fixed error `Empty query text` using prepared statements and `ydb.WithExecuteDataQueryOverQueryClient(true)` option
35
* Prepared statements always send query text on Execute call from now (previous behaviour - send query ID)

internal/grpcwrapper/rawtopic/client.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ import (
44
"context"
55
"fmt"
66

7+
"github.com/google/uuid"
78
"github.com/ydb-platform/ydb-go-genproto/Ydb_Topic_V1"
89

910
"github.com/ydb-platform/ydb-go-sdk/v3/internal/grpcwrapper/rawtopic/rawtopicreader"
1011
"github.com/ydb-platform/ydb-go-sdk/v3/internal/grpcwrapper/rawtopic/rawtopicwriter"
1112
"github.com/ydb-platform/ydb-go-sdk/v3/internal/grpcwrapper/rawydb"
1213
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
14+
"github.com/ydb-platform/ydb-go-sdk/v3/trace"
1315
)
1416

1517
type Client struct {
@@ -99,7 +101,10 @@ func (c *Client) StreamRead(ctxStreamLifeTime context.Context) (rawtopicreader.S
99101
return rawtopicreader.StreamReader{Stream: protoResp}, nil
100102
}
101103

102-
func (c *Client) StreamWrite(ctxStreamLifeTime context.Context) (*rawtopicwriter.StreamWriter, error) {
104+
func (c *Client) StreamWrite(
105+
ctxStreamLifeTime context.Context,
106+
tracer *trace.Topic,
107+
) (*rawtopicwriter.StreamWriter, error) {
103108
protoResp, err := c.service.StreamWrite(ctxStreamLifeTime)
104109
if err != nil {
105110
return nil, xerrors.WithStackTrace(
@@ -109,7 +114,11 @@ func (c *Client) StreamWrite(ctxStreamLifeTime context.Context) (*rawtopicwriter
109114
)
110115
}
111116

112-
return &rawtopicwriter.StreamWriter{Stream: protoResp}, nil
117+
return &rawtopicwriter.StreamWriter{
118+
Stream: protoResp,
119+
Tracer: tracer,
120+
InternalStreamID: uuid.New().String(),
121+
}, nil
113122
}
114123

115124
func (c *Client) UpdateOffsetsInTransaction(

internal/grpcwrapper/rawtopic/rawtopicwriter/streamwriter.go

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/ydb-platform/ydb-go-sdk/v3/internal/grpcwrapper/rawtopic/rawtopiccommon"
1515
"github.com/ydb-platform/ydb-go-sdk/v3/internal/grpcwrapper/rawydb"
1616
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
17+
"github.com/ydb-platform/ydb-go-sdk/v3/trace"
1718
)
1819

1920
var errConcurencyReadDenied = xerrors.Wrap(errors.New("ydb: read from rawtopicwriter in parallel"))
@@ -29,8 +30,15 @@ type StreamWriter struct {
2930

3031
sendCloseMtx sync.Mutex
3132
Stream GrpcStream
33+
34+
Tracer *trace.Topic
35+
InternalStreamID string
36+
readMessagesCount int
37+
writtenMessagesCount int
38+
sessionID string
3239
}
3340

41+
//nolint:funlen
3442
func (w *StreamWriter) Recv() (ServerMessage, error) {
3543
readCnt := atomic.AddInt32(&w.readCounter, 1)
3644
defer atomic.AddInt32(&w.readCounter, -1)
@@ -39,20 +47,27 @@ func (w *StreamWriter) Recv() (ServerMessage, error) {
3947
return nil, xerrors.WithStackTrace(errConcurencyReadDenied)
4048
}
4149

42-
grpcMsg, err := w.Stream.Recv()
43-
if err != nil {
44-
if !xerrors.IsErrorFromServer(err) {
45-
err = xerrors.Transport(err)
50+
grpcMsg, sendErr := w.Stream.Recv()
51+
w.readMessagesCount++
52+
defer func() {
53+
// defer needs for set good session id on first init response before trace the message
54+
trace.TopicOnWriterReceiveGRPCMessage(
55+
w.Tracer, w.InternalStreamID, w.sessionID, w.readMessagesCount, grpcMsg, sendErr,
56+
)
57+
}()
58+
if sendErr != nil {
59+
if !xerrors.IsErrorFromServer(sendErr) {
60+
sendErr = xerrors.Transport(sendErr)
4661
}
4762

4863
return nil, xerrors.WithStackTrace(xerrors.Wrap(fmt.Errorf(
4964
"ydb: failed to read grpc message from writer stream: %w",
50-
err,
65+
sendErr,
5166
)))
5267
}
5368

5469
var meta rawtopiccommon.ServerMessageMetadata
55-
if err = meta.MetaFromStatusAndIssues(grpcMsg); err != nil {
70+
if err := meta.MetaFromStatusAndIssues(grpcMsg); err != nil {
5671
return nil, err
5772
}
5873
if !meta.Status.IsSuccess() {
@@ -64,12 +79,13 @@ func (w *StreamWriter) Recv() (ServerMessage, error) {
6479
var res InitResult
6580
res.ServerMessageMetadata = meta
6681
res.mustFromProto(v.InitResponse)
82+
w.sessionID = res.SessionID
6783

6884
return &res, nil
6985
case *Ydb_Topic.StreamWriteMessage_FromServer_WriteResponse:
7086
var res WriteResult
7187
res.ServerMessageMetadata = meta
72-
err = res.fromProto(v.WriteResponse)
88+
err := res.fromProto(v.WriteResponse)
7389
if err != nil {
7490
return nil, err
7591
}
@@ -124,6 +140,8 @@ func (w *StreamWriter) Send(rawMsg ClientMessage) (err error) {
124140
}
125141

126142
err = w.Stream.Send(&protoMsg)
143+
w.writtenMessagesCount++
144+
trace.TopicOnWriterSentGRPCMessage(w.Tracer, w.InternalStreamID, w.sessionID, w.writtenMessagesCount, &protoMsg, err)
127145
if err != nil {
128146
return xerrors.WithStackTrace(xerrors.Wrap(fmt.Errorf("ydb: failed to send grpc message to writer stream: %w", err)))
129147
}

internal/topic/topicclientinternal/client.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -356,11 +356,11 @@ func (c *Client) createWriterConfig(
356356
topicPath string,
357357
opts []topicoptions.WriterOption,
358358
) topicwriterinternal.WriterReconnectorConfig {
359-
var connector topicwriterinternal.ConnectFunc = func(ctx context.Context) (
359+
var connector topicwriterinternal.ConnectFunc = func(ctx context.Context, tracer *trace.Topic) (
360360
topicwriterinternal.RawTopicWriterStream,
361361
error,
362362
) {
363-
return c.rawClient.StreamWrite(ctx)
363+
return c.rawClient.StreamWrite(ctx, tracer)
364364
}
365365

366366
options := []topicoptions.WriterOption{

internal/topic/topicwriterinternal/writer_reconnector.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,7 @@ func (w *WriterReconnector) connectWithTimeout(streamLifetimeContext context.Con
517517
}
518518
}()
519519

520-
stream, err := w.cfg.Connect(connectCtx)
520+
stream, err := w.cfg.Connect(connectCtx, w.cfg.Tracer)
521521
resCh <- resT{stream: stream, err: err}
522522
}()
523523

@@ -789,7 +789,7 @@ func calculateAllowedCodecs(forceCodec rawtopiccommon.Codec, multiEncoder *Multi
789789
return res
790790
}
791791

792-
type ConnectFunc func(ctx context.Context) (RawTopicWriterStream, error)
792+
type ConnectFunc func(ctx context.Context, tracer *trace.Topic) (RawTopicWriterStream, error)
793793

794794
func createPublicCodecsFromRaw(codecs rawtopiccommon.SupportedCodecs) []topictypes.Codec {
795795
res := make([]topictypes.Codec, len(codecs))

internal/topic/topicwriterinternal/writer_reconnector_test.go

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext"
2525
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
2626
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
27+
"github.com/ydb-platform/ydb-go-sdk/v3/trace"
2728
)
2829

2930
var testCommonEncoders = NewMultiEncoder()
@@ -446,7 +447,7 @@ func TestWriterImpl_Reconnect(t *testing.T) {
446447
connectCalled := false
447448
connectCalledChan := make(empty.Chan)
448449

449-
w.cfg.Connect = func(streamCtxArg context.Context) (RawTopicWriterStream, error) {
450+
w.cfg.Connect = func(streamCtxArg context.Context, _ *trace.Topic) (RawTopicWriterStream, error) {
450451
close(connectCalledChan)
451452
connectCalled = true
452453
require.NotEqual(t, ctx, streamCtxArg)
@@ -562,7 +563,7 @@ func TestWriterImpl_Reconnect(t *testing.T) {
562563
}
563564

564565
var connectionAttempt atomic.Int64
565-
w.cfg.Connect = func(ctx context.Context) (RawTopicWriterStream, error) {
566+
w.cfg.Connect = func(ctx context.Context, _ *trace.Topic) (RawTopicWriterStream, error) {
566567
attemptIndex := int(connectionAttempt.Add(1)) - 1
567568
t.Logf("connect with attempt index: %v", attemptIndex)
568569
res := connectsResult[attemptIndex]
@@ -1078,17 +1079,18 @@ func newTestEnv(t testing.TB, options *testEnvOptions) *testEnv {
10781079
partitionID: 14,
10791080
}
10801081

1081-
writerOptions := append(defaultTestWriterOptions(), WithConnectFunc(func(ctx context.Context) (
1082-
RawTopicWriterStream,
1083-
error,
1084-
) {
1085-
connectNum := atomic.AddInt64(&res.connectCount, 1)
1086-
if connectNum > 1 {
1087-
t.Fatalf("test: default env support most one connection")
1088-
}
1082+
writerOptions := append(defaultTestWriterOptions(), WithConnectFunc(
1083+
func(ctx context.Context, _ *trace.Topic) (
1084+
RawTopicWriterStream,
1085+
error,
1086+
) {
1087+
connectNum := atomic.AddInt64(&res.connectCount, 1)
1088+
if connectNum > 1 {
1089+
t.Fatalf("test: default env support most one connection")
1090+
}
10891091

1090-
return res.stream, nil
1091-
}))
1092+
return res.stream, nil
1093+
}))
10921094
writerOptions = append(writerOptions, options.writerOptions...)
10931095

10941096
res.writer = newWriterReconnectorStopped(NewWriterReconnectorConfig(writerOptions...))

log/topic.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ import (
44
"context"
55
"time"
66

7+
"github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Topic"
8+
"google.golang.org/protobuf/encoding/protojson"
9+
"google.golang.org/protobuf/proto"
10+
711
"github.com/ydb-platform/ydb-go-sdk/v3/internal/kv"
812
"github.com/ydb-platform/ydb-go-sdk/v3/trace"
913
)
@@ -885,6 +889,39 @@ func internalTopic(l Logger, d trace.Detailer) (t trace.Topic) {
885889
kv.Version(),
886890
)
887891
}
892+
893+
t.OnWriterSentGRPCMessage = func(info trace.TopicWriterSentGRPCMessageInfo) {
894+
if d.Details()&trace.TopicWriterStreamGrpcMessageEvents == 0 {
895+
return
896+
}
897+
898+
ctx := with(context.Background(), TRACE, "ydb", "topic", "writer", "grpc")
899+
l.Log(
900+
ctx, "topic writer sent grpc message (message body and metadata are removed)",
901+
kv.String("topic_stream_internal_id", info.TopicStreamInternalID),
902+
kv.String("session_id", info.SessionID),
903+
kv.Int("message_number", info.MessageNumber),
904+
kv.Stringer("message", lazyProtoStringifer{info.Message}),
905+
kv.Error(info.Error),
906+
kv.Version(),
907+
)
908+
}
909+
t.OnWriterReceiveGRPCMessage = func(info trace.TopicWriterReceiveGRPCMessageInfo) {
910+
if d.Details()&trace.TopicWriterStreamGrpcMessageEvents == 0 {
911+
return
912+
}
913+
914+
ctx := with(context.Background(), TRACE, "ydb", "topic", "writer", "grpc")
915+
l.Log(
916+
ctx, "topic writer received grpc message (message body and metadata are removed)",
917+
kv.String("topic_stream_internal_id", info.TopicStreamInternalID),
918+
kv.String("session_id", info.SessionID),
919+
kv.Int("message_number", info.MessageNumber),
920+
kv.Stringer("message", lazyProtoStringifer{info.Message}),
921+
kv.Error(info.Error),
922+
kv.Version(),
923+
)
924+
}
888925
t.OnWriterReadUnknownGrpcMessage = func(info trace.TopicOnWriterReadUnknownGrpcMessageInfo) {
889926
if d.Details()&trace.TopicWriterStreamEvents == 0 {
890927
return
@@ -899,3 +936,38 @@ func internalTopic(l Logger, d trace.Detailer) (t trace.Topic) {
899936

900937
return t
901938
}
939+
940+
type lazyProtoStringifer struct {
941+
message proto.Message
942+
}
943+
944+
func (s lazyProtoStringifer) String() string {
945+
// cut message data
946+
if writeRequest, ok := s.message.(*Ydb_Topic.StreamWriteMessage_FromClient); ok {
947+
if data := writeRequest.GetWriteRequest(); data != nil {
948+
type messDataType struct {
949+
Data []byte
950+
Metadata []*Ydb_Topic.MetadataItem
951+
}
952+
storage := make([]messDataType, len(data.GetMessages()))
953+
for i := range data.GetMessages() {
954+
storage[i].Data = data.GetMessages()[i].GetData()
955+
data.Messages[i] = nil
956+
957+
storage[i].Metadata = data.GetMessages()[i].GetMetadataItems()
958+
data.Messages[i].MetadataItems = nil
959+
}
960+
961+
defer func() {
962+
for i := range data.GetMessages() {
963+
data.Messages[i].Data = storage[i].Data
964+
data.Messages[i].MetadataItems = storage[i].Metadata
965+
}
966+
}()
967+
}
968+
}
969+
970+
res := protojson.MarshalOptions{AllowPartial: true}.Format(s.message)
971+
972+
return res
973+
}

tests/integration/topic_read_writer_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,69 @@ import (
2626
"github.com/ydb-platform/ydb-go-sdk/v3/internal/empty"
2727
"github.com/ydb-platform/ydb-go-sdk/v3/internal/version"
2828
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
29+
"github.com/ydb-platform/ydb-go-sdk/v3/log"
2930
"github.com/ydb-platform/ydb-go-sdk/v3/topic/topicoptions"
3031
"github.com/ydb-platform/ydb-go-sdk/v3/topic/topicsugar"
3132
"github.com/ydb-platform/ydb-go-sdk/v3/topic/topictypes"
3233
"github.com/ydb-platform/ydb-go-sdk/v3/topic/topicwriter"
3334
"github.com/ydb-platform/ydb-go-sdk/v3/trace"
3435
)
3536

37+
func TestTopicWriterLogMessagesWithoutData(t *testing.T) {
38+
scope := newScope(t)
39+
40+
producerID := "dkeujsl"
41+
const seqNoInt = 486812497
42+
seqNoString := "486812497"
43+
data := "kdjwkruowe"
44+
metaKey := "gyoeexiufo"
45+
metaValue := "fjedeikeosbv"
46+
47+
logs := &strings.Builder{}
48+
writer, err := scope.Driver().Topic().StartWriter(
49+
scope.TopicPath(),
50+
topicoptions.WithWriterProducerID(producerID),
51+
topicoptions.WithWriterSetAutoSeqNo(false),
52+
topicoptions.WithWriterWaitServerAck(true),
53+
topicoptions.WithWriterTrace(log.Topic(
54+
log.Default(logs, log.WithMinLevel(log.TRACE)), trace.TopicWriterStreamGrpcMessageEvents),
55+
),
56+
)
57+
58+
scope.Require.NoError(err)
59+
err = writer.Write(scope.Ctx,
60+
topicwriter.Message{
61+
SeqNo: seqNoInt,
62+
Data: strings.NewReader(data),
63+
Metadata: map[string][]byte{
64+
metaKey: []byte(metaValue),
65+
},
66+
},
67+
)
68+
scope.Require.NoError(err)
69+
70+
err = writer.Close(scope.Ctx)
71+
scope.Require.NoError(err)
72+
73+
logsString := logs.String()
74+
scope.Require.Contains(logsString, producerID)
75+
scope.Require.Contains(logsString, seqNoString)
76+
scope.Require.NotContains(logsString, metaKey)
77+
scope.Require.NotContains(logsString, metaValue)
78+
scope.Require.NotContains(logsString, data)
79+
80+
mess, err := scope.TopicReader().ReadMessage(scope.Ctx)
81+
scope.Require.NoError(err)
82+
83+
scope.Require.Equal(producerID, mess.ProducerID)
84+
scope.Require.Equal(int64(seqNoInt), mess.SeqNo)
85+
scope.Require.Equal(metaValue, string(mess.Metadata[metaKey]))
86+
87+
messData, err := io.ReadAll(mess)
88+
scope.Require.NoError(err)
89+
scope.Require.Equal(data, string(messData))
90+
}
91+
3692
func TestSendAsyncMessages(t *testing.T) {
3793
ctx := context.Background()
3894
db := connect(t)

0 commit comments

Comments
 (0)