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