github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/internal/topic/topicwriterinternal/message.go (about)

     1  package topicwriterinternal
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"time"
     9  
    10  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/grpcwrapper/rawtopic/rawtopiccommon"
    11  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/grpcwrapper/rawtopic/rawtopicwriter"
    12  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/tx"
    13  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
    14  )
    15  
    16  var errNoRawContent = xerrors.Wrap(errors.New("ydb: internal state error - no raw message content"))
    17  
    18  type PublicMessage struct {
    19  	SeqNo     int64
    20  	CreatedAt time.Time
    21  	Data      io.Reader
    22  	Metadata  map[string][]byte
    23  
    24  	tx tx.Transaction
    25  
    26  	// partitioning at level message available by protocol, but doesn't available by current server implementation
    27  	// the field hidden from public access for prevent runtime errors.
    28  	// it will be published after implementation on server side.
    29  	futurePartitioning PublicFuturePartitioning
    30  }
    31  
    32  // PublicFuturePartitioning will be published in feature, after server implementation completed.
    33  type PublicFuturePartitioning struct {
    34  	messageGroupID string
    35  	partitionID    int64
    36  	hasPartitionID bool
    37  }
    38  
    39  func (p PublicFuturePartitioning) ToRaw() rawtopicwriter.Partitioning {
    40  	if p.hasPartitionID {
    41  		return rawtopicwriter.NewPartitioningPartitionID(p.partitionID)
    42  	}
    43  
    44  	return rawtopicwriter.NewPartitioningMessageGroup(p.messageGroupID)
    45  }
    46  
    47  func NewPartitioningWithMessageGroupID(id string) PublicFuturePartitioning {
    48  	return PublicFuturePartitioning{
    49  		messageGroupID: id,
    50  	}
    51  }
    52  
    53  func NewPartitioningWithPartitionID(id int64) PublicFuturePartitioning {
    54  	return PublicFuturePartitioning{
    55  		partitionID:    id,
    56  		hasPartitionID: true,
    57  	}
    58  }
    59  
    60  type messageWithDataContent struct {
    61  	PublicMessage
    62  
    63  	dataWasRead         bool
    64  	hasRawContent       bool
    65  	hasEncodedContent   bool
    66  	metadataCached      bool
    67  	bufCodec            rawtopiccommon.Codec
    68  	bufEncoded          bytes.Buffer
    69  	rawBuf              bytes.Buffer
    70  	encoders            *EncoderMap
    71  	BufUncompressedSize int
    72  }
    73  
    74  func (m *messageWithDataContent) GetEncodedBytes(codec rawtopiccommon.Codec) ([]byte, error) {
    75  	if codec == rawtopiccommon.CodecRaw {
    76  		return m.getRawBytes()
    77  	}
    78  
    79  	return m.getEncodedBytes(codec)
    80  }
    81  
    82  func (m *messageWithDataContent) cacheMetadata() {
    83  	if m.metadataCached {
    84  		return
    85  	}
    86  
    87  	// ensure message metadata can't be changed by external code
    88  	if len(m.Metadata) > 0 {
    89  		ownCopy := make(map[string][]byte, len(m.Metadata))
    90  		for key, val := range m.Metadata {
    91  			ownCopy[key] = bytes.Clone(val)
    92  		}
    93  		m.Metadata = ownCopy
    94  	} else {
    95  		m.Metadata = nil
    96  	}
    97  	m.metadataCached = true
    98  }
    99  
   100  func (m *messageWithDataContent) CacheMessageData(codec rawtopiccommon.Codec) error {
   101  	m.cacheMetadata()
   102  	_, err := m.GetEncodedBytes(codec)
   103  
   104  	return err
   105  }
   106  
   107  func (m *messageWithDataContent) encodeRawContent(codec rawtopiccommon.Codec) ([]byte, error) {
   108  	if !m.hasRawContent {
   109  		return nil, xerrors.WithStackTrace(errNoRawContent)
   110  	}
   111  
   112  	m.bufEncoded.Reset()
   113  
   114  	writer, err := m.encoders.CreateLazyEncodeWriter(codec, &m.bufEncoded)
   115  	if err != nil {
   116  		return nil, xerrors.WithStackTrace(xerrors.Wrap(fmt.Errorf(
   117  			"ydb: failed create encoder for message, codec '%v': %w",
   118  			codec,
   119  			err,
   120  		)))
   121  	}
   122  	_, err = writer.Write(m.rawBuf.Bytes())
   123  	if err == nil {
   124  		err = writer.Close()
   125  	}
   126  	if err != nil {
   127  		return nil, xerrors.WithStackTrace(xerrors.Wrap(fmt.Errorf(
   128  			"ydb: failed to compress message, codec '%v': %w",
   129  			codec,
   130  			err,
   131  		)))
   132  	}
   133  
   134  	m.bufCodec = codec
   135  
   136  	return m.bufEncoded.Bytes(), nil
   137  }
   138  
   139  func (m *messageWithDataContent) readDataToRawBuf() error {
   140  	m.rawBuf.Reset()
   141  	m.hasRawContent = true
   142  	if m.Data != nil {
   143  		writtenBytes, err := io.Copy(&m.rawBuf, m.Data)
   144  		if err != nil {
   145  			return xerrors.WithStackTrace(err)
   146  		}
   147  		m.BufUncompressedSize = int(writtenBytes)
   148  		m.Data = nil
   149  	}
   150  
   151  	return nil
   152  }
   153  
   154  func (m *messageWithDataContent) readDataToTargetCodec(codec rawtopiccommon.Codec) error {
   155  	m.dataWasRead = true
   156  	m.hasEncodedContent = true
   157  	m.bufCodec = codec
   158  	m.bufEncoded.Reset()
   159  
   160  	encoder, err := m.encoders.CreateLazyEncodeWriter(codec, &m.bufEncoded)
   161  	if err != nil {
   162  		return err
   163  	}
   164  
   165  	reader := m.Data
   166  	if reader == nil {
   167  		reader = &bytes.Reader{}
   168  	}
   169  	bytesCount, err := io.Copy(encoder, reader)
   170  	if err == nil {
   171  		err = encoder.Close()
   172  	}
   173  	if err != nil {
   174  		return xerrors.WithStackTrace(xerrors.Wrap(fmt.Errorf(
   175  			"ydb: failed compress message with codec '%v': %w",
   176  			codec,
   177  			err,
   178  		)))
   179  	}
   180  	m.BufUncompressedSize = int(bytesCount)
   181  	m.Data = nil
   182  
   183  	return nil
   184  }
   185  
   186  func (m *messageWithDataContent) getRawBytes() ([]byte, error) {
   187  	if m.hasRawContent {
   188  		return m.rawBuf.Bytes(), nil
   189  	}
   190  	if m.dataWasRead {
   191  		return nil, xerrors.WithStackTrace(errNoRawContent)
   192  	}
   193  
   194  	if err := m.readDataToRawBuf(); err != nil {
   195  		return nil, err
   196  	}
   197  
   198  	return m.rawBuf.Bytes(), nil
   199  }
   200  
   201  func (m *messageWithDataContent) getEncodedBytes(codec rawtopiccommon.Codec) ([]byte, error) {
   202  	switch {
   203  	case m.hasEncodedContent && m.bufCodec == codec:
   204  		return m.bufEncoded.Bytes(), nil
   205  	case m.hasRawContent:
   206  		return m.encodeRawContent(codec)
   207  	case m.dataWasRead:
   208  		return nil, errNoRawContent
   209  	default:
   210  		err := m.readDataToTargetCodec(codec)
   211  		if err != nil {
   212  			return nil, err
   213  		}
   214  
   215  		return m.bufEncoded.Bytes(), nil
   216  	}
   217  }
   218  
   219  func newMessageDataWithContent(
   220  	message PublicMessage, //nolint:gocritic
   221  	encoders *EncoderMap,
   222  ) messageWithDataContent {
   223  	return messageWithDataContent{
   224  		PublicMessage: message,
   225  		encoders:      encoders,
   226  	}
   227  }