github.com/ydb-platform/ydb-go-sdk/v3@v3.57.0/topic/topicreader/reader.go (about) 1 package topicreader 2 3 import ( 4 "context" 5 "sync/atomic" 6 7 "github.com/ydb-platform/ydb-go-sdk/v3/internal/topic/topicreaderinternal" 8 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors" 9 ) 10 11 // Reader allow to read message from YDB topics. 12 // ReadMessage or ReadMessageBatch can call concurrency with Commit, other concurrency call is denied. 13 // 14 // In other words you can have one goroutine for read messages and one goroutine for commit messages. 15 // 16 // Concurrency table 17 // | Method | ReadMessage | ReadMessageBatch | Commit | Close | 18 // | ReadMessage | - | - | + | - | 19 // | ReadMessageBatch | - | - | + | - | 20 // | Commit | + | + | - | - | 21 // | Close | - | - | - | - | 22 type Reader struct { 23 reader topicreaderinternal.Reader 24 readInFlyght atomic.Bool 25 commitInFlyght atomic.Bool 26 } 27 28 // NewReader 29 // create new reader, used internally only. 30 func NewReader(internalReader topicreaderinternal.Reader) *Reader { 31 return &Reader{reader: internalReader} 32 } 33 34 // WaitInit waits until the reader is initialized 35 // or an error occurs 36 func (r *Reader) WaitInit(ctx context.Context) error { 37 return r.reader.WaitInit(ctx) 38 } 39 40 // ReadMessage read exactly one message 41 // exactly one of message, error is nil 42 func (r *Reader) ReadMessage(ctx context.Context) (*Message, error) { 43 if err := r.inCall(&r.readInFlyght); err != nil { 44 return nil, err 45 } 46 defer r.outCall(&r.readInFlyght) 47 48 return r.reader.ReadMessage(ctx) 49 } 50 51 // Message contains data and metadata, readed from the server 52 type Message = topicreaderinternal.PublicMessage 53 54 // MessageContentUnmarshaler is interface for unmarshal message content to own struct 55 type MessageContentUnmarshaler = topicreaderinternal.PublicMessageContentUnmarshaler 56 57 // Commit receive Message, Batch of single offset 58 // It can be fast (by default) or sync and waite response from server 59 // see topicoptions.CommitMode for details 60 // 61 // for topicoptions.CommitModeSync mode sync the method can return ErrCommitToExpiredSession 62 // it means about the message/batch was not committed because connection broken or partition routed to 63 // other reader by server. 64 // Client code should continue work normally 65 func (r *Reader) Commit(ctx context.Context, obj CommitRangeGetter) error { 66 if err := r.inCall(&r.commitInFlyght); err != nil { 67 return err 68 } 69 defer r.outCall(&r.commitInFlyght) 70 71 return r.reader.Commit(ctx, obj) 72 } 73 74 // CommitRangeGetter interface for get commit offsets 75 type CommitRangeGetter = topicreaderinternal.PublicCommitRangeGetter 76 77 // ReadMessageBatch 78 // Deprecated: (was experimental) will be removed soon. 79 // Use ReadMessagesBatch instead. 80 func (r *Reader) ReadMessageBatch(ctx context.Context, opts ...ReadBatchOption) (*Batch, error) { 81 if err := r.inCall(&r.readInFlyght); err != nil { 82 return nil, err 83 } 84 defer r.outCall(&r.readInFlyght) 85 86 return r.reader.ReadMessageBatch(ctx, opts...) 87 } 88 89 // ReadMessagesBatch read batch of messages 90 // Batch is ordered message group from one partition 91 // exactly one of Batch, err is nil 92 // if Batch is not nil - reader guarantee about all Batch.Messages are not nil 93 func (r *Reader) ReadMessagesBatch(ctx context.Context, opts ...ReadBatchOption) (*Batch, error) { 94 if err := r.inCall(&r.readInFlyght); err != nil { 95 return nil, err 96 } 97 defer r.outCall(&r.readInFlyght) 98 99 return r.reader.ReadMessageBatch(ctx, opts...) 100 } 101 102 // Batch is ordered group of messages from one partition 103 type Batch = topicreaderinternal.PublicBatch 104 105 // ReadBatchOption is type for options of read batch 106 type ReadBatchOption = topicreaderinternal.PublicReadBatchOption 107 108 // Close stop work with reader 109 // return when reader complete internal works, flush commit buffer, ets 110 // or when ctx cancelled 111 func (r *Reader) Close(ctx context.Context) error { 112 // close must be non-concurrent with read and commit 113 114 if err := r.inCall(&r.readInFlyght); err != nil { 115 return err 116 } 117 defer r.outCall(&r.readInFlyght) 118 119 if err := r.inCall(&r.commitInFlyght); err != nil { 120 return err 121 } 122 defer r.outCall(&r.commitInFlyght) 123 124 return r.reader.Close(ctx) 125 } 126 127 func (r *Reader) inCall(inFlight *atomic.Bool) error { 128 if inFlight.CompareAndSwap(false, true) { 129 return nil 130 } 131 132 return xerrors.WithStackTrace(ErrConcurrencyCall) 133 } 134 135 func (r *Reader) outCall(inFlight *atomic.Bool) { 136 if inFlight.CompareAndSwap(true, false) { 137 return 138 } 139 140 panic("ydb: topic reader out call without in call, must be never") 141 }