Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/Pulsar.Client/Api/PulsarClientException.fs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ type IncompatibleSchemaException(msg) = inherit Exception(msg)
type LookupException(msg) = inherit Exception(msg)
type TooManyRequestsException(msg) = inherit Exception(msg)
type ConnectException(msg) = inherit Exception(msg)
type AlreadyClosedException(msg) = inherit Exception(msg)
type AlreadyClosedException(msg: string, innerException: exn) =
inherit Exception(msg, innerException)
new(msg: string) = AlreadyClosedException(msg, null)
type TopicTerminatedException(msg) = inherit Exception(msg)
type AuthenticationException(msg) = inherit Exception(msg)
type AuthorizationException(msg) = inherit Exception(msg)
Expand All @@ -19,7 +21,9 @@ type BrokerPersistenceException(msg) = inherit Exception(msg)
type BrokerMetadataException(msg) = inherit Exception(msg)
type ProducerBusyException(msg) = inherit Exception(msg)
type ConsumerBusyException(msg) = inherit Exception(msg)
type NotConnectedException(msg) = inherit Exception(msg)
type NotConnectedException(msg: string, innerException: exn) =
inherit Exception(msg, innerException)
new(msg: string) = NotConnectedException(msg, null)
type InvalidMessageException(msg) = inherit Exception(msg)
type InvalidTopicNameException(msg) = inherit Exception(msg)
type NotSupportedException(msg) = inherit Exception(msg)
Expand Down
12 changes: 9 additions & 3 deletions src/Pulsar.Client/Internal/ConnectionHandler.fs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ type internal ConnectionHandler( parentPrefix: string,
backoff: Backoff) as this =

let mutable connectionState = Uninitialized
// why the handler moved to Failed, so later requests can report it
let mutable failureCause: exn = null
let mutable lastDisconnectedTimestamp = 0L
let mutable maxMessageSize = Commands.DEFAULT_MAX_MESSAGE_SIZE
let mutable epoch = %0UL
Expand Down Expand Up @@ -106,7 +108,7 @@ type internal ConnectionHandler( parentPrefix: string,
if t.IsFaulted then
let (Flatten ex) = t.Exception
Log.Logger.LogCritical(ex, "{0} mailbox failure", prefix)
this.ConnectionState <- Failed
this.Failed(ex)
else
Log.Logger.LogInformation("{0} mailbox has stopped normally", prefix))
|> ignore
Expand All @@ -125,7 +127,8 @@ type internal ConnectionHandler( parentPrefix: string,
member this.Closed() =
this.ConnectionState <- Closed

member this.Failed() =
member this.Failed(?cause: exn) =
failureCause <- defaultArg cause null
this.ConnectionState <- Failed

member this.Closing() =
Expand All @@ -151,12 +154,15 @@ type internal ConnectionHandler( parentPrefix: string,
with get() : TimeStamp = %(Volatile.Read(&lastDisconnectedTimestamp))
and private set(value: TimeStamp) = Volatile.Write(&lastDisconnectedTimestamp, %value)

member this.FailureCause = failureCause

member this.CheckIfActive() =
match this.ConnectionState with
| Ready _ | Connecting -> null
| Closing | Closed -> AlreadyClosedException(prefix + "already closed") :> exn
| Terminated -> AlreadyClosedException(prefix + " topic was terminated") :> exn
| Failed | Uninitialized -> NotConnectedException(prefix + " not connected") :> exn
| Failed -> NotConnectedException(prefix + " not connected", failureCause) :> exn
| Uninitialized -> NotConnectedException(prefix + " not connected") :> exn

member this.Close() =
post mb Close
23 changes: 16 additions & 7 deletions src/Pulsar.Client/Internal/ConsumerImpl.fs
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,8 @@ type internal ConsumerImpl<'T> (consumerConfig: ConsumerConfiguration<'T>, clien

let stopConsumer () =
closeConsumerTasks()
failWaiters <| AlreadyClosedException "Consumer is already closed"
// a failed consumer passes on why it stopped, e.g. to a multi-topic parent
failWaiters <| AlreadyClosedException("Consumer is already closed", connectionHandler.FailureCause)
Log.Logger.LogInformation("{0} stopped", prefix)

let isInvalidConsumerEpoch = isInvalidConsumerEpoch consumerConfig.SubscriptionType
Expand Down Expand Up @@ -904,7 +905,7 @@ type internal ConsumerImpl<'T> (consumerConfig: ConsumerConfiguration<'T>, clien
let stoppedMailboxException () =
match connectionHandler.ConnectionState with
| Closing | Closed | Terminated -> AlreadyClosedException(prefix + " already closed") :> exn
| _ -> NotConnectedException(prefix + " not connected") :> exn
| _ -> NotConnectedException(prefix + " not connected", connectionHandler.FailureCause) :> exn

let failMailbox (ex: exn) =
Log.Logger.LogCritical(ex, "{0} mailbox failure", prefix)
Expand All @@ -928,7 +929,7 @@ type internal ConsumerImpl<'T> (consumerConfig: ConsumerConfiguration<'T>, clien
()
with closeEx ->
Log.Logger.LogWarning(closeEx, "{0} failed to close consumer at the broker after mailbox failure", prefix)
connectionHandler.Failed()
connectionHandler.Failed(ex)
stopConsumer()
try
currentMessage |> Option.iter (replyFromStoppedMailbox ex)
Expand Down Expand Up @@ -1017,7 +1018,7 @@ type internal ConsumerImpl<'T> (consumerConfig: ConsumerConfiguration<'T>, clien
connectionHandler.ReconnectLater ex
| _ when not subscribeTsc.Task.IsCompleted ->
// unable to create new consumer, fail operation
connectionHandler.Failed()
connectionHandler.Failed(ex)
subscribeTsc.SetException(ex)
stopConsumer()
continueLoop <- false
Expand All @@ -1028,8 +1029,11 @@ type internal ConsumerImpl<'T> (consumerConfig: ConsumerConfiguration<'T>, clien
// * Regular consumer after topic is manually delete and with
// auto-topic-creation set to false
// No more retries are needed in this case.
connectionHandler.Failed()
connectionHandler.Failed(ex)
closeConsumerTasks()
// pending receives would otherwise wait forever, and a multi-topic parent
// only learns that this child has stopped when its receive completes
failWaiters <| NotConnectedException(prefix + " topic does not exist anymore", ex)
Log.Logger.LogWarning("{0} Closed consumer because topic does not exist anymore. {1}", prefix, ex.Message)
continueLoop <- false
| _ ->
Expand Down Expand Up @@ -1408,7 +1412,7 @@ type internal ConsumerImpl<'T> (consumerConfig: ConsumerConfiguration<'T>, clien
if ((nonRetriableError || timeout) && subscribeTsc.TrySetException(ex)) then
Log.Logger.LogInformation("{0} creation failed {1}", prefix,
if nonRetriableError then "with unretriableError" else "after timeout")
connectionHandler.Failed()
connectionHandler.Failed(ex)
stopConsumer()
continueLoop <- false

Expand Down Expand Up @@ -1590,11 +1594,15 @@ type internal ConsumerImpl<'T> (consumerConfig: ConsumerConfiguration<'T>, clien
connectionHandler.GrabCnx()
subscribeTsc.Task

// only before InitInternal, so that the first subscribe already uses it
member internal this.SetInitialConsumerEpoch(consumerEpoch: ConsumerEpoch) =
currentConsumerEpoch <- consumerEpoch

static member Init(consumerConfig: ConsumerConfiguration<'T>, clientConfig: PulsarClientConfiguration,
topicName: TopicName, connectionPool: ConnectionPool, partitionIndex: int,
hasParent: bool, startMessageId: MessageId option, startMessageRollbackDuration: TimeSpan, lookup: ILookupService,
createTopicIfDoesNotExist: bool, schema: ISchema<'T>, schemaProvider: MultiVersionSchemaInfoProvider option,
interceptors: ConsumerInterceptors<'T>, cleanup: ConsumerImpl<'T> -> unit) =
interceptors: ConsumerInterceptors<'T>, cleanup: ConsumerImpl<'T> -> unit, ?consumerEpoch: ConsumerEpoch) =
backgroundTask {
let consumer =
if consumerConfig.ReceiverQueueSize > 0 then
Expand All @@ -1606,6 +1614,7 @@ type internal ConsumerImpl<'T> (consumerConfig: ConsumerConfiguration<'T>, clien
startMessageId, lookup, createTopicIfDoesNotExist,
schema, schemaProvider, interceptors, cleanup) :> ConsumerImpl<'T>

consumerEpoch |> Option.iter (fun epoch -> consumer.SetInitialConsumerEpoch epoch)
do! consumer.InitInternal()
return consumer
}
Expand Down
18 changes: 12 additions & 6 deletions src/Pulsar.Client/Internal/ConsumerInterceptors.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@ open Microsoft.Extensions.Logging
open Pulsar.Client.Api
open Pulsar.Client.Common

type internal ConsumerInterceptors<'T>(interceptors: IConsumerInterceptor<'T> array) =
type internal ConsumerInterceptors<'T>(interceptors: IConsumerInterceptor<'T> array, ownsInterceptors: bool) =
new(interceptors: IConsumerInterceptor<'T> array) = ConsumerInterceptors<'T>(interceptors, true)
member this.Interceptors = interceptors
static member Empty with get() = ConsumerInterceptors<'T>([||])

// the same interceptors for a consumer that doesn't own them, e.g. a child of a multi-topic consumer,
// which can stop and be replaced while the owner keeps using them
member this.Borrow() = ConsumerInterceptors<'T>(interceptors, false)

member this.BeforeConsume (consumer: IConsumer<'T>, msg: Message<'T>) =
let mutable interceptorMessage = msg
Expand Down Expand Up @@ -50,8 +55,9 @@ type internal ConsumerInterceptors<'T>(interceptors: IConsumerInterceptor<'T> ar
Log.Logger.LogWarning(e, "Error executing interceptor OnAckTimeoutSend callback");

member this.Close() =
for interceptor in interceptors do
try
interceptor.Dispose()
with e ->
Log.Logger.LogWarning(e, "Fail to close consumer interceptor");
if ownsInterceptors then
for interceptor in interceptors do
try
interceptor.Dispose()
with e ->
Log.Logger.LogWarning(e, "Fail to close consumer interceptor");
Loading
Loading