1
- using System . Diagnostics ;
1
+ using System . Diagnostics ;
2
2
using Amqp ;
3
3
using Amqp . Framing ;
4
4
using Trace = Amqp . Trace ;
@@ -8,14 +8,13 @@ namespace RabbitMQ.AMQP.Client.Impl;
8
8
9
9
public class AmqpPublisher : AbstractReconnectLifeCycle , IPublisher
10
10
{
11
- private SenderLink ? _senderLink = null ;
12
-
13
- private readonly ManualResetEvent _pausePublishing = new ( true ) ;
14
11
private readonly AmqpConnection _connection ;
15
12
private readonly TimeSpan _timeout ;
16
13
private readonly string _address ;
17
14
private readonly int _maxInFlight ;
18
15
16
+ private SenderLink ? _senderLink = null ;
17
+
19
18
public AmqpPublisher ( AmqpConnection connection , string address , TimeSpan timeout , int maxInFlight )
20
19
{
21
20
_address = address ;
@@ -75,33 +74,6 @@ await base.OpenAsync()
75
74
76
75
private string Id { get ; } = Guid . NewGuid ( ) . ToString ( ) ;
77
76
78
- private void PausePublishing ( )
79
- {
80
- _pausePublishing . Reset ( ) ;
81
- }
82
-
83
- private void MaybeResumePublishing ( )
84
- {
85
- if ( State is State . Open )
86
- {
87
- // Can be resumed only if the publisher is open and the in-flight messages are less than the max allowed
88
- // In case the publisher is closed, closing or reconnecting, the publishing will be paused
89
- _pausePublishing . Set ( ) ;
90
- }
91
- }
92
-
93
- private void MaybeBackPressure ( )
94
- {
95
- if ( _currentInFlight >= _maxInFlight )
96
- {
97
- PausePublishing ( ) ;
98
- }
99
- else
100
- {
101
- MaybeResumePublishing ( ) ;
102
- }
103
- }
104
-
105
77
// TODO: Consider implementing this method with the send method
106
78
// a way to send a batch of messages
107
79
@@ -114,59 +86,37 @@ private void MaybeBackPressure()
114
86
// return batch;
115
87
// }
116
88
117
- private int _currentInFlight = 0 ;
118
-
119
- public Task Publish ( IMessage message , OutcomeDescriptorCallback outcomeCallback )
89
+ public async Task < PublishResult > PublishAsync ( IMessage message , CancellationToken cancellationToken = default )
120
90
{
121
91
ThrowIfClosed ( ) ;
92
+
93
+ if ( _senderLink is null )
94
+ {
95
+ // TODO create "internal bug" exception type?
96
+ throw new InvalidOperationException ( "_senderLink is null, report via https://github.com/rabbitmq/rabbitmq-amqp-dotnet-client/issues" ) ;
97
+ }
98
+
122
99
try
123
100
{
124
- _pausePublishing . WaitOne ( _timeout ) ;
125
- Interlocked . Increment ( ref _currentInFlight ) ;
126
-
127
- var nMessage = ( ( AmqpMessage ) message ) . NativeMessage ;
128
- _senderLink ? . Send ( nMessage ,
129
- ( sender , outMessage , outcome , state ) =>
130
- {
131
- Interlocked . Decrement ( ref _currentInFlight ) ;
132
- MaybeBackPressure ( ) ;
133
-
134
- if ( outMessage == nMessage &&
135
- outMessage . GetEstimatedMessageSize ( ) == nMessage . GetEstimatedMessageSize ( ) )
136
- {
137
- if ( outcome is Rejected rejected )
138
- {
139
- outcomeCallback ( message , new OutcomeDescriptor ( rejected . Descriptor . Code ,
140
- rejected . Descriptor . ToString ( ) ,
141
- OutcomeState . Failed , Utils . ConvertError ( rejected ? . Error ) ) ) ;
142
- }
143
- else
144
- {
145
- outcomeCallback ( message , new OutcomeDescriptor ( outcome . Descriptor . Code ,
146
- outcome . Descriptor . ToString ( ) ,
147
- OutcomeState . Accepted , null ) ) ;
148
- }
149
- }
150
- else
151
- {
152
- Trace . WriteLine ( TraceLevel . Error , $ "{ ToString ( ) } Message not sent. Killing the process.") ;
153
- Process . GetCurrentProcess ( ) . Kill ( ) ;
154
- }
155
-
156
- // is it correct to dispose the message here?
157
- // maybe we should expose a method to dispose the message
158
- nMessage . Dispose ( ) ;
159
- } , this ) ;
101
+ Debug . Assert ( false == _senderLink . IsClosed ) ;
102
+ Message nativeMessage = ( ( AmqpMessage ) message ) . NativeMessage ;
103
+ await _senderLink . SendAsync ( nativeMessage )
104
+ . ConfigureAwait ( false ) ;
105
+
106
+ var publishOutcome = new PublishOutcome ( OutcomeState . Accepted , null ) ;
107
+ return new PublishResult ( message , publishOutcome ) ;
108
+ }
109
+ catch ( AmqpException ex )
110
+ {
111
+ var publishOutcome = new PublishOutcome ( OutcomeState . Failed , Utils . ConvertError ( ex . Error ) ) ;
112
+ return new PublishResult ( message , publishOutcome ) ;
160
113
}
161
114
catch ( Exception e )
162
115
{
163
116
throw new PublisherException ( $ "{ ToString ( ) } Failed to publish message, { e } ") ;
164
117
}
165
-
166
- return Task . CompletedTask ;
167
118
}
168
119
169
-
170
120
public override async Task CloseAsync ( )
171
121
{
172
122
if ( State == State . Closed )
@@ -183,8 +133,6 @@ public override async Task CloseAsync()
183
133
await _senderLink . CloseAsync ( )
184
134
. ConfigureAwait ( false ) ;
185
135
}
186
-
187
- _pausePublishing . Dispose ( ) ;
188
136
}
189
137
catch ( Exception e )
190
138
{
@@ -195,9 +143,64 @@ await _senderLink.CloseAsync()
195
143
_connection . Publishers . TryRemove ( Id , out _ ) ;
196
144
}
197
145
198
-
199
146
public override string ToString ( )
200
147
{
201
148
return $ "Publisher{{Address='{ _address } ', id={ Id } ConnectionName='{ _connection } ', State='{ State } '}}";
202
149
}
203
150
}
151
+
152
+ /*
153
+ Task.Run(() =>
154
+ {
155
+ // TODO timeout / cancellation
156
+ // TODO LRB I removed the nativeMessage == outMessage check because outMessage came in as NULL
157
+ // which I didn't think possible 🤔
158
+ void OutcomeCallback(ILink sender, Message outMessage, Outcome outcome, object state)
159
+ {
160
+ try
161
+ {
162
+ Interlocked.Decrement(ref _currentInFlight);
163
+ MaybeBackPressure();
164
+
165
+ PublishOutcome publishOutcome;
166
+ if (outcome is Rejected rejected)
167
+ {
168
+ publishOutcome = new PublishOutcome(rejected.Descriptor.Code, rejected.Descriptor.ToString(),
169
+ OutcomeState.Failed, Utils.ConvertError(rejected?.Error));
170
+
171
+ }
172
+ else
173
+ {
174
+ publishOutcome = new PublishOutcome(outcome.Descriptor.Code, outcome.Descriptor.ToString(),
175
+ OutcomeState.Accepted, null);
176
+ }
177
+
178
+ var result = new PublishResult(message, publishOutcome);
179
+ publishedTcs.SetResult(result);
180
+ }
181
+ catch (Exception ex)
182
+ {
183
+ if (false == publishedTcs.TrySetException(ex))
184
+ {
185
+ // TODO log it at debug level?
186
+ }
187
+ }
188
+ finally
189
+ {
190
+ // is it correct to dispose the message here?
191
+ // maybe we should expose a method to dispose the message
192
+ nativeMessage.Dispose();
193
+ }
194
+ }
195
+
196
+ try
197
+ {
198
+ Debug.Assert(false == _senderLink.IsClosed);
199
+ _senderLink.Send(nativeMessage, OutcomeCallback, this);
200
+ }
201
+ catch (Exception ex)
202
+ {
203
+ publishedTcs.SetException(ex);
204
+ }
205
+ }, cancellationToken);
206
+ */
0 commit comments