github.com/ydb-platform/ydb-go-sdk/v3@v3.57.0/internal/topic/topicreaderinternal/message.go (about) 1 package topicreaderinternal 2 3 import ( 4 "bytes" 5 "context" 6 "errors" 7 "fmt" 8 "time" 9 10 "github.com/ydb-platform/ydb-go-sdk/v3/internal/empty" 11 "github.com/ydb-platform/ydb-go-sdk/v3/internal/grpcwrapper/rawtopic/rawtopiccommon" 12 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors" 13 ) 14 15 var errMessageWasReadEarly = xerrors.Wrap(errors.New("ydb: message was read early")) 16 17 // ErrPublicUnexpectedCodec return when try to read message content with unknown codec 18 var ErrPublicUnexpectedCodec = errors.New("unexpected codec") 19 20 // PublicMessage is representation of topic message 21 type PublicMessage struct { 22 empty.DoNotCopy 23 24 SeqNo int64 25 CreatedAt time.Time 26 MessageGroupID string 27 WriteSessionMetadata map[string]string 28 Offset int64 29 WrittenAt time.Time 30 ProducerID string 31 Metadata map[string][]byte // Metadata, nil if no metadata 32 33 commitRange commitRange 34 data oneTimeReader 35 rawDataLen int 36 bufferBytesAccount int 37 UncompressedSize int // as sent by sender, server/sdk doesn't check the field. It may be empty or wrong. 38 dataConsumed bool 39 } 40 41 func (m *PublicMessage) Context() context.Context { 42 return m.commitRange.session().Context() 43 } 44 45 func (m *PublicMessage) Topic() string { 46 return m.commitRange.session().Topic 47 } 48 49 func (m *PublicMessage) PartitionID() int64 { 50 return m.commitRange.session().PartitionID 51 } 52 53 func (m *PublicMessage) getCommitRange() PublicCommitRange { 54 return m.commitRange.getCommitRange() 55 } 56 57 // UnmarshalTo can call most once per message, it read all data from internal reader and 58 // call PublicMessageContentUnmarshaler.UnmarshalYDBTopicMessage with uncompressed content 59 func (m *PublicMessage) UnmarshalTo(dst PublicMessageContentUnmarshaler) error { 60 if m.dataConsumed { 61 return xerrors.WithStackTrace(errMessageWasReadEarly) 62 } 63 64 m.dataConsumed = true 65 66 return callbackOnReaderContent(globalReadMessagePool, m, m.UncompressedSize, dst) 67 } 68 69 // Read implements io.Reader 70 // Read uncompressed message content 71 // return topicreader.UnexpectedCodec if message compressed with unknown codec 72 func (m *PublicMessage) Read(p []byte) (n int, err error) { 73 m.dataConsumed = true 74 75 return m.data.Read(p) 76 } 77 78 // PublicMessageContentUnmarshaler is interface for unmarshal message content 79 type PublicMessageContentUnmarshaler interface { 80 // UnmarshalYDBTopicMessage MUST NOT use data after return. 81 // If you need content after return from Consume - copy data content to 82 // own slice with copy(dst, data) 83 UnmarshalYDBTopicMessage(data []byte) error 84 } 85 86 func createReader(decoders decoderMap, codec rawtopiccommon.Codec, rawBytes []byte) oneTimeReader { 87 reader, err := decoders.Decode(codec, bytes.NewReader(rawBytes)) 88 if err != nil { 89 reader = errorReader{ 90 err: fmt.Errorf("failed to decode message with codec '%v': %w", codec, err), 91 } 92 } 93 94 return newOneTimeReader(reader) 95 } 96 97 type errorReader struct { 98 err error 99 } 100 101 func (u errorReader) Read(p []byte) (n int, err error) { 102 return 0, u.err 103 } 104 105 type PublicMessageBuilder struct { 106 mess *PublicMessage 107 } 108 109 func NewPublicMessageBuilder() *PublicMessageBuilder { 110 res := &PublicMessageBuilder{} 111 res.initMessage() 112 113 return res 114 } 115 116 func (pmb *PublicMessageBuilder) initMessage() { 117 pmb.mess = &PublicMessage{ 118 commitRange: commitRange{partitionSession: newPartitionSession( 119 context.Background(), 120 "", 121 0, 122 0, 123 "", 124 0, 125 0, 126 )}, 127 } 128 } 129 130 // Seqno set message Seqno 131 func (pmb *PublicMessageBuilder) Seqno(seqNo int64) *PublicMessageBuilder { 132 pmb.mess.SeqNo = seqNo 133 134 return pmb 135 } 136 137 // CreatedAt set message CreatedAt 138 func (pmb *PublicMessageBuilder) CreatedAt(createdAt time.Time) *PublicMessageBuilder { 139 pmb.mess.CreatedAt = createdAt 140 141 return pmb 142 } 143 144 func (pmb *PublicMessageBuilder) Metadata(metadata map[string][]byte) *PublicMessageBuilder { 145 pmb.mess.Metadata = make(map[string][]byte, len(metadata)) 146 for key, val := range metadata { 147 pmb.mess.Metadata[key] = bytes.Clone(val) 148 } 149 150 return pmb 151 } 152 153 // MessageGroupID set message MessageGroupID 154 func (pmb *PublicMessageBuilder) MessageGroupID(messageGroupID string) *PublicMessageBuilder { 155 pmb.mess.MessageGroupID = messageGroupID 156 157 return pmb 158 } 159 160 // WriteSessionMetadata set message WriteSessionMetadata 161 func (pmb *PublicMessageBuilder) WriteSessionMetadata(writeSessionMetadata map[string]string) *PublicMessageBuilder { 162 pmb.mess.WriteSessionMetadata = writeSessionMetadata 163 164 return pmb 165 } 166 167 // Offset set message Offset 168 func (pmb *PublicMessageBuilder) Offset(offset int64) *PublicMessageBuilder { 169 pmb.mess.Offset = offset 170 171 return pmb 172 } 173 174 // WrittenAt set message WrittenAt 175 func (pmb *PublicMessageBuilder) WrittenAt(writtenAt time.Time) *PublicMessageBuilder { 176 pmb.mess.WrittenAt = writtenAt 177 178 return pmb 179 } 180 181 // ProducerID set message ProducerID 182 func (pmb *PublicMessageBuilder) ProducerID(producerID string) *PublicMessageBuilder { 183 pmb.mess.ProducerID = producerID 184 185 return pmb 186 } 187 188 // DataAndUncompressedSize set message uncompressed content and field UncompressedSize 189 func (pmb *PublicMessageBuilder) DataAndUncompressedSize(data []byte) *PublicMessageBuilder { 190 copyData := make([]byte, len(data)) 191 copy(copyData, data) 192 pmb.mess.data = oneTimeReader{reader: bytes.NewReader(data)} 193 pmb.mess.dataConsumed = false 194 pmb.mess.rawDataLen = len(copyData) 195 pmb.mess.UncompressedSize = len(copyData) 196 197 return pmb 198 } 199 200 // UncompressedSize set message UncompressedSize 201 func (pmb *PublicMessageBuilder) UncompressedSize(uncompressedSize int) *PublicMessageBuilder { 202 pmb.mess.UncompressedSize = uncompressedSize 203 204 return pmb 205 } 206 207 // Context set message Context 208 func (pmb *PublicMessageBuilder) Context(ctx context.Context) { 209 pmb.mess.commitRange.partitionSession.ctx = ctx 210 } 211 212 // Topic set message Topic 213 func (pmb *PublicMessageBuilder) Topic(topic string) { 214 pmb.mess.commitRange.partitionSession.Topic = topic 215 } 216 217 // PartitionID set message PartitionID 218 func (pmb *PublicMessageBuilder) PartitionID(partitionID int64) { 219 pmb.mess.commitRange.partitionSession.PartitionID = partitionID 220 } 221 222 // Build return builded message and reset internal state for create new message 223 func (pmb *PublicMessageBuilder) Build() *PublicMessage { 224 mess := pmb.mess 225 pmb.initMessage() 226 227 return mess 228 }