1
1
package p2p
2
2
3
3
import (
4
+ "context"
5
+ "fmt"
6
+ "reflect"
7
+
8
+ "github.com/gogo/protobuf/proto"
4
9
"github.com/tendermint/tendermint/libs/service"
5
10
"github.com/tendermint/tendermint/p2p/conn"
11
+ "github.com/tendermint/tendermint/pkg/trace/schema"
6
12
)
7
13
14
+ // ProcessorFunc is the message processor function type.
15
+ type ProcessorFunc func (context.Context , <- chan UnprocessedEnvelope ) error
16
+
8
17
// Reactor is responsible for handling incoming messages on one or more
9
18
// Channel. Switch calls GetChannels when reactor is added to it. When a new
10
19
// peer joins our node, InitPeer and AddPeer are called. RemovePeer is called
@@ -51,6 +60,15 @@ type Reactor interface {
51
60
// Deprecated: Reactors looking to receive data from a peer should implement ReceiveEnvelope.
52
61
// Receive will be deprecated in favor of ReceiveEnvelope in v0.37.
53
62
Receive (chID byte , peer Peer , msgBytes []byte )
63
+
64
+ // QueueUnprocessedEnvelope is called by the switch when an unprocessed
65
+ // envelope is received. Unprocessed envelopes are immediately buffered in a
66
+ // queue to avoid blocking. Incoming messages are then passed to a
67
+ // processing function. The default processing function unmarshals the
68
+ // messages in the order the sender sent them and then calls Receive on the
69
+ // reactor. The queue size and the processing function can be changed via
70
+ // passing options to the base reactor.
71
+ QueueUnprocessedEnvelope (e UnprocessedEnvelope )
54
72
}
55
73
56
74
type EnvelopeReceiver interface {
@@ -68,18 +86,147 @@ type EnvelopeReceiver interface {
68
86
type BaseReactor struct {
69
87
service.BaseService // Provides Start, Stop, .Quit
70
88
Switch * Switch
89
+
90
+ incoming chan UnprocessedEnvelope
91
+
92
+ ctx context.Context
93
+ cancel context.CancelFunc
94
+ // processor is called with the incoming channel and is responsible for
95
+ // unmarshalling the messages and calling Receive on the reactor.
96
+ processor ProcessorFunc
71
97
}
72
98
73
- func NewBaseReactor (name string , impl Reactor ) * BaseReactor {
74
- return & BaseReactor {
99
+ type ReactorOptions func (* BaseReactor )
100
+
101
+ func NewBaseReactor (name string , impl Reactor , opts ... ReactorOptions ) * BaseReactor {
102
+ ctx := context .Background ()
103
+ ctx , cancel := context .WithCancel (ctx )
104
+ base := & BaseReactor {
105
+ ctx : ctx ,
106
+ cancel : cancel ,
75
107
BaseService : * service .NewBaseService (nil , name , impl ),
76
108
Switch : nil ,
109
+ incoming : make (chan UnprocessedEnvelope , 100 ),
110
+ processor : DefaultProcessor (impl ),
111
+ }
112
+ for _ , opt := range opts {
113
+ opt (base )
114
+ }
115
+
116
+ go func () {
117
+ err := base .processor (ctx , base .incoming )
118
+ if err != nil {
119
+ err = base .Stop ()
120
+ if err != nil {
121
+ panic (err )
122
+ }
123
+ }
124
+ }()
125
+
126
+ return base
127
+ }
128
+
129
+ // WithProcessor sets the processor function for the reactor. The processor
130
+ // function is called with the incoming channel and is responsible for
131
+ // unmarshalling the messages and calling Receive on the reactor.
132
+ func WithProcessor (processor ProcessorFunc ) ReactorOptions {
133
+ return func (br * BaseReactor ) {
134
+ br .processor = processor
135
+ }
136
+ }
137
+
138
+ // WithIncomingQueueSize sets the size of the incoming message queue for a
139
+ // reactor.
140
+ func WithIncomingQueueSize (size int ) ReactorOptions {
141
+ return func (br * BaseReactor ) {
142
+ br .incoming = make (chan UnprocessedEnvelope , size )
77
143
}
78
144
}
79
145
80
146
func (br * BaseReactor ) SetSwitch (sw * Switch ) {
81
147
br .Switch = sw
82
148
}
149
+
150
+ // QueueUnprocessedEnvelope is called by the switch when an unprocessed
151
+ // envelope is received. Unprocessed envelopes are immediately buffered in a
152
+ // queue to avoid blocking. The size of the queue can be changed by passing
153
+ // options to the base reactor.
154
+ func (br * BaseReactor ) QueueUnprocessedEnvelope (e UnprocessedEnvelope ) {
155
+ select {
156
+ // if the context is done, do nothing.
157
+ case <- br .ctx .Done ():
158
+ // if not, add the item to the channel.
159
+ case br .incoming <- e :
160
+ }
161
+ }
162
+
163
+ func (br * BaseReactor ) OnStop () {
164
+ br .cancel ()
165
+ close (br .incoming )
166
+ }
167
+
168
+ // DefaultProcessor unmarshalls the message and calls Receive on the reactor.
169
+ // This preserves the sender's original order for all messages.
170
+ func DefaultProcessor (impl Reactor ) func (context.Context , <- chan UnprocessedEnvelope ) error {
171
+ implChannels := impl .GetChannels ()
172
+
173
+ chIDs := make (map [byte ]proto.Message , len (implChannels ))
174
+ for _ , chDesc := range implChannels {
175
+ chIDs [chDesc .ID ] = chDesc .MessageType
176
+ }
177
+ return func (ctx context.Context , incoming <- chan UnprocessedEnvelope ) error {
178
+ for {
179
+ select {
180
+ case <- ctx .Done ():
181
+ return nil
182
+ case ue , ok := <- incoming :
183
+ if ! ok {
184
+ // this means the channel was closed.
185
+ return nil
186
+ }
187
+ mt := chIDs [ue .ChannelID ]
188
+
189
+ if mt == nil {
190
+ return fmt .Errorf ("no message type registered for channel %d" , ue .ChannelID )
191
+ }
192
+
193
+ msg := proto .Clone (mt )
194
+
195
+ err := proto .Unmarshal (ue .Message , msg )
196
+ if err != nil {
197
+ return fmt .Errorf ("unmarshaling message: %v into type: %s resulted in error %w" , msg , reflect .TypeOf (mt ), err )
198
+ }
199
+
200
+ if w , ok := msg .(Unwrapper ); ok {
201
+ msg , err = w .Unwrap ()
202
+ if err != nil {
203
+ return fmt .Errorf ("unwrapping message: %v" , err )
204
+ }
205
+ }
206
+
207
+ labels := []string {
208
+ "peer_id" , string (ue .Src .ID ()),
209
+ "chID" , fmt .Sprintf ("%#x" , ue .ChannelID ),
210
+ }
211
+
212
+ ue .Src .Metrics ().PeerReceiveBytesTotal .With (labels ... ).Add (float64 (len (ue .Message )))
213
+ ue .Src .Metrics ().MessageReceiveBytesTotal .With (append (labels , "message_type" , ue .Src .ValueToMetricLabel (msg ))... ).Add (float64 (len (ue .Message )))
214
+ schema .WriteReceivedBytes (ue .Src .TraceClient (), string (ue .Src .ID ()), ue .ChannelID , len (ue .Message ))
215
+
216
+ if nr , ok := impl .(EnvelopeReceiver ); ok {
217
+ nr .ReceiveEnvelope (Envelope {
218
+ ChannelID : ue .ChannelID ,
219
+ Src : ue .Src ,
220
+ Message : msg ,
221
+ })
222
+ } else {
223
+ impl .Receive (ue .ChannelID , ue .Src , ue .Message )
224
+ }
225
+ }
226
+ }
227
+ }
228
+ }
229
+
83
230
func (* BaseReactor ) GetChannels () []* conn.ChannelDescriptor { return nil }
84
231
func (* BaseReactor ) AddPeer (peer Peer ) {}
85
232
func (* BaseReactor ) RemovePeer (peer Peer , reason interface {}) {}
0 commit comments