-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add approx. transactional api #98
Draft
blindspotbounty
wants to merge
9
commits into
swift-server:main
Choose a base branch
from
ordo-one:feature/sc-2764/gsoc-support-for-transactions
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 6 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
b42b3f9
add approx. transactional api
blindspotbounty f5bfbe2
add state for initalized transactions
blindspotbounty b2c1619
Merge branch 'main' into feature/sc-2764/gsoc-support-for-transactions
blindspotbounty 4f9520f
Merge remote-tracking branch 'origin/main' into feature/sc-2764/gsoc-…
blindspotbounty 7da4901
address some review comments
blindspotbounty ce702f3
use shared config with producer
blindspotbounty 8118757
Merge remote-tracking branch 'origin/main' into feature/sc-2764/gsoc-…
blindspotbounty 6501aaa
remove merge artifact
blindspotbounty 21a3cd7
Merge branch 'main' into feature/sc-2764/gsoc-support-for-transactions
blindspotbounty File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
142 changes: 142 additions & 0 deletions
142
Sources/SwiftKafka/Configuration/KafkaProducerSharedProperties.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the swift-kafka-gsoc open source project | ||
// | ||
// Copyright (c) 2022 Apple Inc. and the swift-kafka-gsoc project authors | ||
// Licensed under Apache License v2.0 | ||
// | ||
// See LICENSE.txt for license information | ||
// See CONTRIBUTORS.txt for the list of swift-kafka-gsoc project authors | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
internal protocol KafkaProducerSharedProperties: Sendable, Hashable { | ||
// MARK: - SwiftKafka-specific Config properties | ||
|
||
/// The time between two consecutive polls. | ||
/// Effectively controls the rate at which incoming events are consumed. | ||
/// Default: `.milliseconds(100)` | ||
var pollInterval: Duration { get } | ||
|
||
/// Maximum timeout for flushing outstanding produce requests when the ``KakfaProducer`` is shutting down. | ||
/// Default: `10000` | ||
var flushTimeoutMilliseconds: Int { get } | ||
|
||
// MARK: - Producer-specific Config Properties | ||
|
||
/// When set to true, the producer will ensure that messages are successfully produced exactly once and in the original produce order. The following configuration properties are adjusted automatically (if not modified by the user) when idempotence is enabled: max.in.flight.requests.per.connection=5 (must be less than or equal to 5), retries=INT32_MAX (must be greater than 0), acks=all, queuing.strategy=fifo. Producer instantation will fail if user-supplied configuration is incompatible. | ||
/// Default: `false` | ||
var enableIdempotence: Bool { get } | ||
|
||
/// Producer queue options. | ||
var queue: KafkaConfiguration.QueueOptions { get } | ||
|
||
/// How many times to retry sending a failing Message. Note: retrying may cause reordering unless enable.idempotence is set to true. | ||
/// Default: `2_147_483_647` | ||
var messageSendMaxRetries: Int { get } | ||
|
||
/// Allow automatic topic creation on the broker when producing to non-existent topics. | ||
/// The broker must also be configured with auto.create.topics.enable=true for this configuration to take effect. | ||
/// Default: `true` | ||
var allowAutoCreateTopics: Bool { get } | ||
|
||
// MARK: - Common Client Config Properties | ||
|
||
/// Client identifier. | ||
/// Default: `"rdkafka"` | ||
var clientID: String { get } | ||
|
||
/// Initial list of brokers. | ||
/// Default: `[]` | ||
var bootstrapServers: [KafkaConfiguration.Broker] { get } | ||
|
||
/// Message options. | ||
var message: KafkaConfiguration.MessageOptions { get } | ||
|
||
/// Maximum Kafka protocol response message size. This serves as a safety precaution to avoid memory exhaustion in case of protocol hickups. This value must be at least fetch.max.bytes + 512 to allow for protocol overhead; the value is adjusted automatically unless the configuration property is explicitly set. | ||
/// Default: `100_000_000` | ||
var receiveMessageMaxBytes: Int { get } | ||
|
||
/// Maximum number of in-flight requests per broker connection. This is a generic property applied to all broker communication, however it is primarily relevant to produce requests. In particular, note that other mechanisms limit the number of outstanding consumer fetch request per broker to one. | ||
/// Default: `1_000_000` | ||
var maxInFlightRequestsPerConnection: Int { get } | ||
|
||
/// Metadata cache max age. | ||
/// Default: `900_000` | ||
var metadataMaxAgeMilliseconds: Int { get } | ||
|
||
/// Topic metadata options. | ||
var topicMetadata: KafkaConfiguration.TopicMetadataOptions { get } | ||
|
||
/// Topic denylist. | ||
/// Default: `[]` | ||
var topicDenylist: [String] { get } | ||
|
||
/// Debug options. | ||
/// Default: `[]` | ||
var debug: [KafkaConfiguration.DebugOption] { get } | ||
|
||
/// Socket options. | ||
var socket: KafkaConfiguration.SocketOptions { get } | ||
|
||
/// Broker options. | ||
var broker: KafkaConfiguration.BrokerOptions { get } | ||
|
||
/// Reconnect options. | ||
var reconnect: KafkaConfiguration.ReconnectOptions { get } | ||
|
||
/// Security protocol to use (plaintext, ssl, sasl_plaintext, sasl_ssl). | ||
/// Default: `.plaintext` | ||
var securityProtocol: KafkaConfiguration.SecurityProtocol { get } | ||
|
||
var dictionary: [String: String] { get } | ||
} | ||
|
||
extension KafkaProducerSharedProperties { | ||
internal var sharedPropsDictionary: [String: String] { | ||
var resultDict: [String: String] = [:] | ||
|
||
resultDict["enable.idempotence"] = String(self.enableIdempotence) | ||
resultDict["queue.buffering.max.messages"] = String(self.queue.bufferingMaxMessages) | ||
resultDict["queue.buffering.max.kbytes"] = String(self.queue.bufferingMaxKBytes) | ||
resultDict["queue.buffering.max.ms"] = String(self.queue.bufferingMaxMilliseconds) | ||
resultDict["message.send.max.retries"] = String(self.messageSendMaxRetries) | ||
resultDict["allow.auto.create.topics"] = String(self.allowAutoCreateTopics) | ||
|
||
resultDict["client.id"] = self.clientID | ||
resultDict["bootstrap.servers"] = self.bootstrapServers.map(\.description).joined(separator: ",") | ||
resultDict["message.max.bytes"] = String(self.message.maxBytes) | ||
resultDict["message.copy.max.bytes"] = String(self.message.copyMaxBytes) | ||
resultDict["receive.message.max.bytes"] = String(self.receiveMessageMaxBytes) | ||
resultDict["max.in.flight.requests.per.connection"] = String(self.maxInFlightRequestsPerConnection) | ||
resultDict["metadata.max.age.ms"] = String(self.metadataMaxAgeMilliseconds) | ||
resultDict["topic.metadata.refresh.interval.ms"] = String(self.topicMetadata.refreshIntervalMilliseconds) | ||
resultDict["topic.metadata.refresh.fast.interval.ms"] = String(self.topicMetadata.refreshFastIntervalMilliseconds) | ||
resultDict["topic.metadata.refresh.sparse"] = String(self.topicMetadata.refreshSparse) | ||
resultDict["topic.metadata.propagation.max.ms"] = String(self.topicMetadata.propagationMaxMilliseconds) | ||
resultDict["topic.blacklist"] = self.topicDenylist.joined(separator: ",") | ||
if !self.debug.isEmpty { | ||
resultDict["debug"] = self.debug.map(\.description).joined(separator: ",") | ||
} | ||
resultDict["socket.timeout.ms"] = String(self.socket.timeoutMilliseconds) | ||
resultDict["socket.send.buffer.bytes"] = String(self.socket.sendBufferBytes) | ||
resultDict["socket.receive.buffer.bytes"] = String(self.socket.receiveBufferBytes) | ||
resultDict["socket.keepalive.enable"] = String(self.socket.keepaliveEnable) | ||
resultDict["socket.nagle.disable"] = String(self.socket.nagleDisable) | ||
resultDict["socket.max.fails"] = String(self.socket.maxFails) | ||
resultDict["socket.connection.setup.timeout.ms"] = String(self.socket.connectionSetupTimeoutMilliseconds) | ||
resultDict["broker.address.ttl"] = String(self.broker.addressTTL) | ||
resultDict["broker.address.family"] = self.broker.addressFamily.description | ||
resultDict["reconnect.backoff.ms"] = String(self.reconnect.backoffMilliseconds) | ||
resultDict["reconnect.backoff.max.ms"] = String(self.reconnect.backoffMaxMilliseconds) | ||
|
||
// Merge with SecurityProtocol configuration dictionary | ||
resultDict.merge(self.securityProtocol.dictionary) { _, _ in | ||
fatalError("securityProtocol and \(#file) should not have duplicate keys") | ||
} | ||
|
||
return resultDict | ||
} | ||
} |
134 changes: 134 additions & 0 deletions
134
Sources/SwiftKafka/Configuration/KafkaTransactionalProducerConfiguration.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the swift-kafka-gsoc open source project | ||
// | ||
// Copyright (c) 2022 Apple Inc. and the swift-kafka-gsoc project authors | ||
// Licensed under Apache License v2.0 | ||
// | ||
// See LICENSE.txt for license information | ||
// See CONTRIBUTORS.txt for the list of swift-kafka-gsoc project authors | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
// FIXME: should we really duplicate `KafkaProducerConfiguration` | ||
// FIXME: after public api updated? | ||
public struct KafkaTransactionalProducerConfiguration { | ||
// MARK: - SwiftKafka-specific Config properties | ||
|
||
/// The time between two consecutive polls. | ||
/// Effectively controls the rate at which incoming events are consumed. | ||
/// Default: `.milliseconds(100)` | ||
public var pollInterval: Duration = .milliseconds(100) | ||
|
||
/// Maximum timeout for flushing outstanding produce requests when the ``KakfaProducer`` is shutting down. | ||
/// Default: `10000` | ||
public var flushTimeoutMilliseconds: Int = 10000 { | ||
didSet { | ||
precondition( | ||
0...Int(Int32.max) ~= self.flushTimeoutMilliseconds, | ||
"Flush timeout outside of valid range \(0...Int32.max)" | ||
) | ||
} | ||
} | ||
|
||
// MARK: - Producer-specific Config Properties | ||
|
||
/// When set to true, the producer will ensure that messages are successfully produced exactly once and in the original produce order. The following configuration properties are adjusted automatically (if not modified by the user) when idempotence is enabled: max.in.flight.requests.per.connection=5 (must be less than or equal to 5), retries=INT32_MAX (must be greater than 0), acks=all, queuing.strategy=fifo. Producer instantation will fail if user-supplied configuration is incompatible. | ||
/// Default: `false` | ||
internal let enableIdempotence: Bool = true | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The difference between
|
||
|
||
/// Producer queue options. | ||
public var queue: KafkaConfiguration.QueueOptions = .init() | ||
|
||
/// How many times to retry sending a failing Message. Note: retrying may cause reordering unless enable.idempotence is set to true. | ||
/// Default: `2_147_483_647` | ||
public var messageSendMaxRetries: Int = 2_147_483_647 | ||
|
||
/// Allow automatic topic creation on the broker when producing to non-existent topics. | ||
/// The broker must also be configured with auto.create.topics.enable=true for this configuration to take effect. | ||
/// Default: `true` | ||
public var allowAutoCreateTopics: Bool = true | ||
|
||
// MARK: - Common Client Config Properties | ||
|
||
/// Client identifier. | ||
/// Default: `"rdkafka"` | ||
public var clientID: String = "rdkafka" | ||
|
||
/// Initial list of brokers. | ||
/// Default: `[]` | ||
public var bootstrapServers: [KafkaConfiguration.Broker] = [] | ||
|
||
/// Message options. | ||
public var message: KafkaConfiguration.MessageOptions = .init() | ||
|
||
/// Maximum Kafka protocol response message size. This serves as a safety precaution to avoid memory exhaustion in case of protocol hickups. This value must be at least fetch.max.bytes + 512 to allow for protocol overhead; the value is adjusted automatically unless the configuration property is explicitly set. | ||
/// Default: `100_000_000` | ||
public var receiveMessageMaxBytes: Int = 100_000_000 | ||
|
||
/// Maximum number of in-flight requests per broker connection. This is a generic property applied to all broker communication, however it is primarily relevant to produce requests. In particular, note that other mechanisms limit the number of outstanding consumer fetch request per broker to one. | ||
/// Default: `1_000_000` | ||
public var maxInFlightRequestsPerConnection: Int = 5 { | ||
didSet { | ||
precondition( | ||
0...5 ~= self.maxInFlightRequestsPerConnection, | ||
"Transactional producer can have no more than 5 in flight requests" | ||
) | ||
} | ||
} | ||
|
||
/// Metadata cache max age. | ||
/// Default: `900_000` | ||
public var metadataMaxAgeMilliseconds: Int = 900_000 | ||
|
||
/// Topic metadata options. | ||
public var topicMetadata: KafkaConfiguration.TopicMetadataOptions = .init() | ||
|
||
/// Topic denylist. | ||
/// Default: `[]` | ||
public var topicDenylist: [String] = [] | ||
|
||
/// Debug options. | ||
/// Default: `[]` | ||
public var debug: [KafkaConfiguration.DebugOption] = [] | ||
|
||
/// Socket options. | ||
public var socket: KafkaConfiguration.SocketOptions = .init() | ||
|
||
/// Broker options. | ||
public var broker: KafkaConfiguration.BrokerOptions = .init() | ||
|
||
/// Reconnect options. | ||
public var reconnect: KafkaConfiguration.ReconnectOptions = .init() | ||
|
||
/// Security protocol to use (plaintext, ssl, sasl_plaintext, sasl_ssl). | ||
/// Default: `.plaintext` | ||
public var securityProtocol: KafkaConfiguration.SecurityProtocol = .plaintext | ||
|
||
// TODO: add Docc | ||
var transactionalId: String | ||
var transactionsTimeout: Duration = .seconds(60) // equal to socket TODO: add didSet | ||
|
||
public init(transactionalId: String) { | ||
self.transactionalId = transactionalId | ||
} | ||
} | ||
|
||
// MARK: - KafkaProducerConfiguration + Hashable | ||
|
||
extension KafkaTransactionalProducerConfiguration: Hashable {} | ||
|
||
// MARK: - KafkaProducerConfiguration + Sendable | ||
|
||
extension KafkaTransactionalProducerConfiguration: Sendable {} | ||
|
||
extension KafkaTransactionalProducerConfiguration: KafkaProducerSharedProperties { | ||
internal var dictionary: [String: String] { | ||
var resultDict: [String: String] = sharedPropsDictionary | ||
resultDict["transactional.id"] = self.transactionalId | ||
resultDict["transaction.timeout.ms"] = String(self.transactionsTimeout.totalMilliseconds) | ||
return resultDict | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've tried to use at least internal shared protocol to unite mostly all properties in producer/transactional producer instead of copying them