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  }