@@ -70,58 +70,91 @@ public actor KafkaProducer {
70
70
/// Mechanism that polls the Kafka cluster for updates periodically.
71
71
private let pollingSystem : KafkaPollingSystem <
72
72
Result < KafkaAcknowledgedMessage , KafkaAcknowledgedMessageError >
73
- >
73
+ > ?
74
74
/// Used for handling the connection to the Kafka cluster.
75
75
private let client : KafkaClient
76
76
77
- // Private. `KafkaProducer.newProducer` should be used to create a new producer.
77
+ // Private initializer, use factory methods to create KafkaProducer
78
78
/// Initialize a new ``KafkaProducer``.
79
79
/// - Parameter config: The ``KafkaProducerConfig`` for configuring the ``KafkaProducer``.
80
80
/// - Parameter topicConfig: The ``KafkaTopicConfig`` used for newly created topics.
81
81
/// - Parameter logger: A logger.
82
82
/// - Throws: A ``KafkaError`` if initializing the producer failed.
83
83
private init (
84
- config: KafkaProducerConfig ,
84
+ client: KafkaClient ,
85
+ pollingSystem: KafkaPollingSystem < Result < KafkaAcknowledgedMessage , KafkaAcknowledgedMessageError > > ? = nil ,
85
86
topicConfig: KafkaTopicConfig ,
86
87
logger: Logger
87
88
) async throws {
89
+ self . client = client
90
+ self . pollingSystem = pollingSystem
88
91
self . topicConfig = topicConfig
89
- self . logger = logger
90
92
self . topicHandles = [ : ]
93
+ self . logger = logger
91
94
self . state = . started
95
+ }
92
96
93
- self . pollingSystem = KafkaPollingSystem < Result < KafkaAcknowledgedMessage , KafkaAcknowledgedMessageError > > ( )
94
-
95
- self . client = try RDKafka . createClient (
97
+ /// Initialize a new ``KafkaProducer`` that ignores incoming message acknowledgements.
98
+ /// - Parameter config: The ``KafkaProducerConfig`` for configuring the ``KafkaProducer``.
99
+ /// - Parameter topicConfig: The ``KafkaTopicConfig`` used for newly created topics.
100
+ /// - Parameter logger: A logger.
101
+ /// - Returns: The newly created ``KafkaProducer``.
102
+ /// - Throws: A ``KafkaError`` if initializing the producer failed.
103
+ public static func newProducer(
104
+ config: KafkaProducerConfig = KafkaProducerConfig ( ) ,
105
+ topicConfig: KafkaTopicConfig = KafkaTopicConfig ( ) ,
106
+ logger: Logger
107
+ ) async throws -> KafkaProducer {
108
+ let client = try RDKafka . createClient (
96
109
type: . producer,
97
110
configDictionary: config. dictionary,
98
- callback: { [ logger , pollingSystem ] messageResult in
99
- guard let messageResult else {
100
- logger . error ( " Could not resolve acknowledged message " )
101
- return
102
- }
111
+ // Having no callback will discard any incoming acknowlegement messages
112
+ // Ref: rdkafka_broker.c:rd_kafka_dr_msgq
113
+ callback : nil ,
114
+ logger : logger
115
+ )
103
116
104
- pollingSystem. yield ( messageResult)
105
- } ,
106
- logger: self . logger
117
+ let producer = try await KafkaProducer (
118
+ client: client,
119
+ pollingSystem: nil , // we don't receive acknowlegements so no pollingSystem needed
120
+ topicConfig: topicConfig,
121
+ logger: logger
107
122
)
123
+
124
+ return producer
108
125
}
109
126
110
- /// Initialize a new ``KafkaProducer`` alongside a ``KafkaAsyncSequence` ` that can be used
127
+ /// Initialize a new ``KafkaProducer`` alongside a ``KafkaMessageAcknowledgements`` `AsyncSequence ` that can be used
111
128
/// to receive message acknowlegements.
112
129
/// - Parameter config: The ``KafkaProducerConfig`` for configuring the ``KafkaProducer``.
113
130
/// - Parameter topicConfig: The ``KafkaTopicConfig`` used for newly created topics.
114
131
/// - Parameter logger: A logger.
115
- /// - Returns: A tuple containing the created ``KafkaProducer`` and the ``KafkaAsyncSequence ``
116
- /// used for receiving message acknowledgements.
132
+ /// - Returns: A tuple containing the created ``KafkaProducer`` and the ``KafkaMessageAcknowledgements ``
133
+ /// `AsyncSequence` used for receiving message acknowledgements.
117
134
/// - Throws: A ``KafkaError`` if initializing the producer failed.
118
- public static func newProducer (
135
+ public static func newProducerWithAcknowledgements (
119
136
config: KafkaProducerConfig = KafkaProducerConfig ( ) ,
120
137
topicConfig: KafkaTopicConfig = KafkaTopicConfig ( ) ,
121
138
logger: Logger
122
- ) async throws -> ( KafkaProducer , KafkaAsyncSequence < Acknowledgement > ) {
139
+ ) async throws -> ( KafkaProducer , KafkaMessageAcknowledgements ) {
140
+ let pollingSystem = KafkaPollingSystem < Result < KafkaAcknowledgedMessage , KafkaAcknowledgedMessageError > > ( )
141
+ let client = try RDKafka . createClient (
142
+ type: . producer,
143
+ configDictionary: config. dictionary,
144
+ callback: { [ logger, pollingSystem] messageResult in
145
+ guard let messageResult else {
146
+ logger. error ( " Could not resolve acknowledged message " )
147
+ return
148
+ }
149
+
150
+ pollingSystem. yield ( messageResult)
151
+ } ,
152
+ logger: logger
153
+ )
154
+
123
155
let producer = try await KafkaProducer (
124
- config: config,
156
+ client: client,
157
+ pollingSystem: pollingSystem,
125
158
topicConfig: topicConfig,
126
159
logger: logger
127
160
)
@@ -132,8 +165,7 @@ public actor KafkaProducer {
132
165
highWatermark: 50
133
166
)
134
167
135
- let client = producer. client
136
- let sequence = producer. pollingSystem. initialize (
168
+ let _sequence = pollingSystem. initialize (
137
169
backPressureStrategy: backPressureStrategy,
138
170
pollClosure: { [ client] in
139
171
client. withKafkaHandlePointer { handle in
@@ -142,8 +174,9 @@ public actor KafkaProducer {
142
174
return
143
175
}
144
176
)
177
+ let acknowlegementsSequence = KafkaMessageAcknowledgements ( wrappedSequence: _sequence)
145
178
146
- return ( producer, sequence )
179
+ return ( producer, acknowlegementsSequence )
147
180
}
148
181
149
182
/// Method to shutdown the ``KafkaProducer``.
@@ -163,15 +196,15 @@ public actor KafkaProducer {
163
196
164
197
private func _shutDownGracefully( timeout: Int32 ) async {
165
198
await withCheckedContinuation { ( continuation: CheckedContinuation < Void , Never > ) in
166
- // Wait 10 seconds for outstanding messages to be sent and callbacks to be called
199
+ // Wait `timeout` seconds for outstanding messages to be sent and callbacks to be called
167
200
self . client. withKafkaHandlePointer { handle in
168
201
rd_kafka_flush ( handle, timeout)
169
202
continuation. resume ( )
170
203
}
171
204
}
172
205
173
206
// Kill poll loop in polling system
174
- self . pollingSystem. terminate ( )
207
+ self . pollingSystem? . terminate ( )
175
208
176
209
for (_, topicHandle) in self . topicHandles {
177
210
rd_kafka_topic_destroy ( topicHandle)
@@ -186,7 +219,10 @@ public actor KafkaProducer {
186
219
/// - Returns: An awaitable task representing the execution of the poll loop.
187
220
public func run( pollInterval: Duration = . milliseconds( 100 ) ) async throws {
188
221
// TODO(felix): make pollInterval part of config -> easier to adapt to Service protocol (service-lifecycle)
189
- try await self . pollingSystem. run ( pollInterval: pollInterval)
222
+ guard let pollingSystem else {
223
+ fatalError ( " Method \( #function) should only be used with the acknowledgement receiving producer. " )
224
+ }
225
+ try await pollingSystem. run ( pollInterval: pollInterval)
190
226
}
191
227
192
228
/// Send messages to the Kafka cluster asynchronously, aka "fire and forget".
0 commit comments