-
Notifications
You must be signed in to change notification settings - Fork 386
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 partitioned transactional source #705
Conversation
Thank you for adding this. Let's ask @seglo to review it. |
I just tried this with some load, starting one instance then another after a few seconds. I'm getting |
@charlibot Thanks for this contribution. I'm looking forward to having this merged!
I think the path forward here would be to listen for revoked partitions and somehow trigger a commit from the revoked partitions handler ( |
c9abe79
to
6160e9f
Compare
@seglo thanks for the pointer. I had to use the handler directly from the sub source logic because the rebalance listener approach with actors is using The transactional sub source now emits a promise along with the topic-partition and source. This promise should be inserted into the At the moment all sub sources are stopped since rebalancing seems to cause all partitions to be revoked. Therefore, on reassignment which partitions are left will have to recreate the source (not sure if this is too much of an issue?). Ideally though, this logic would be moved to the assignment phase, stopping only those that have been reassigned. This would require making Looking forward to your feedback, I'm not fully committed to this promise based approach so any other ideas are more than welcome! |
Great. I'll do a more thorough review on Monday.
That's interesting. IMO this is a bug to not block when using these handlers since their primary use case is to gracefully handle a partition being revoked from a group member during a rebalance. We should either change the rebalance listener to use the From the Kafka consumer javadocs for
https://kafka.apache.org/20/javadoc/org/apache/kafka/clients/consumer/ConsumerRebalanceListener.html |
@seglo I agree that the non-blocking rebalance event notification is not that useful. I did not find a good way to do something about it yet, please see #539 (comment) for the forces on that... |
Thanks for the pointer @ennru . IMO the greatest value of the Partitioned Transactional Source is to easily distribute transactional workloads across consumer groups with more than 1 member. Therefore it's important to find a way to figure out a way we can synchronously handle the group rebalance. Since transactions are involved we could workaround this by using an Akka Streams I suggest we continue the discussion in #539 and come up with a new API that makes this possible and then leverage that for this contribution. |
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.
Hey @charlibot . I changed my mind and decided to do an indepth review before trying to come to consensus on #539, as I suggested earlier.
While reviewing this PR I thought of some fairly major issues that will be left to the user to figure out when they use the partitioned source (such as producer partitioning strategy, see my comments left in the review). I don't think we can provide implementations to protect the user from these issues, otherwise Alpakka Kafka would become more of a heavyweight framework than the lightweight library that it is today. At a certain point, users have to weigh the pros and cons to having some of these things automatically handled for them (Kafka Streams/framework) and having a lot of control (Alpakka Kafka).
core/src/main/scala/akka/kafka/internal/TransactionalSubSourceLogic.scala
Outdated
Show resolved
Hide resolved
core/src/main/scala/akka/kafka/internal/TransactionalSubSourceLogic.scala
Outdated
Show resolved
Hide resolved
tests/src/test/scala/akka/kafka/scaladsl/TransactionsSpec.scala
Outdated
Show resolved
Hide resolved
core/src/main/scala/akka/kafka/internal/TransactionalProducerStage.scala
Outdated
Show resolved
Hide resolved
core/src/main/scala/akka/kafka/internal/TransactionalSubSourceLogic.scala
Outdated
Show resolved
Hide resolved
tests/src/test/scala/akka/kafka/scaladsl/TransactionsSpec.scala
Outdated
Show resolved
Hide resolved
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.
Some questions/suggestions.
2acdef8
to
594a505
Compare
Thanks both for your feedback! I'll hold off on a couple of the items until we resolve the producer partitioning issue since they involve that aspect. |
@seglo I've refactored |
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.
This is looking really good! I made a few minor comments.
Also, do you think it's worthwhile to add a test like "signal rebalance events to actor"
in IntegrationSpec
, which would test the behaviour of the rebalance when some messages have already been produced from a transactional workload with a revoked consumer?
tests/src/test/scala/akka/kafka/scaladsl/TransactionsSpec.scala
Outdated
Show resolved
Hide resolved
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.
A few naming suggestions and questions from me.
core/src/main/scala/akka/kafka/internal/TransactionalSources.scala
Outdated
Show resolved
Hide resolved
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 came up with a few suggestions and questions...
tests/src/test/scala/akka/kafka/scaladsl/TransactionsSpec.scala
Outdated
Show resolved
Hide resolved
Thank you @charlibot and @seglo for your work with this. |
Cool. That's exciting :) Thanks for the heads up. |
Hi both, I tried a slightly different approach that uses the rebalance actor. It required making the ListenerCallbacks sync with an |
Sorry, I didn't have the time to look at your new approach, yet. Will do soon. |
@charlibot I've finally got around to look into your proposal again. We've quite some digging going on as some have discovered duplicates in transactional source/sink flows #756 and #758. |
f710098
to
49f6386
Compare
@ennru I just rebased. Unfortunately since need to block in the |
Thank you. |
Ah I see, thanks for the explanation. Let me know if I need to make any more changes |
I've just opened #761 which explores synchronous callbacks for rebalancing. |
49f6386
to
b9bcf9e
Compare
The work on the transactional flows hasn't come to an end, yet. We'll revisit your PR once that is done -- hopefully very soon. |
Hi @ennru, just updated this PR by removing the promise stuff since can reuse the smarter draining logic that was written for the single transactional source. |
I see you merged |
6c2d96b
to
f5c5c22
Compare
f5c5c22
to
8ece2cf
Compare
Any chance of getting this PR finished and approved? The functionality would be extremely useful. |
I think this PR can be revived now that there's an internal implementation to handle revoked partitions synchronously from #761 |
@brendan-mcadams-exa Thank you for pinging this suggested feature. |
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.
Getting to use the Kafka transaction fencing feature without having to coordinate assigning transactionalId
's to nodes seems really interesting, and running a stream per partition seems like it'd make sense in many cases as well.
Have we checked it's OK to create potentially many Kafka producers?
ProducerConfig.TRANSACTIONAL_ID_CONFIG -> transactionalId, | ||
ProducerConfig.MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION -> 1.toString | ||
) | ||
val txSettingsToProducer = (txid: String) => { |
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.
So this creates a producer (with a unique transactionalId
) for each partition that is assigned to this node. Isn't that expensive, or is the Kafka client smart about pooling the resources for those multiple producers?
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.
@raboof The original idea was to create a producer per partition, but you're right that it's possible that that could have unintended performance problems for large partition subscriptions. AFAIK the separate instances of KafkaProducer
don't share a common resource pool, so they would be managed separately. We don't appear to have a precedent in our docs for using producers per substream with other partitioned sources.
I looked at Kafka Streams impl and the original design doc (https://docs.google.com/document/d/1pGZ8xtOOyGwDYgH5vA6h19zOMMaduFK1DAB8_gBYA2c/edit#heading=h.mki3gltx1zw) and they create 1 KafkaProducer
per Task
. Each TaskId
(1:1 with a Task
) appears to represent one assigned partition. The Kafka Streams Task
transactional.id
for a producer is set with a pattern of the application id (defined by the user across all instances), and a TaskId
. So my initial understanding is that it will indeed create one producer per partition. I think we need to study this implementation more closely to understand if we need to be smarter about the number of producers we spawn.
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 took a look at this again and it does look like a producer is created per task. I'm less clear on whether a task is 1:1 with a partition, but since you can only define one partition per task I don't see how it could be anything else.
This understanding conflicts with the original design doc, so I posed the question to the kafka-users mailing list. Hopefully we'll get an authoritative reply.
I started on rebasing this PR - it now compiles but there are still tests failing (https://travis-ci.org/akka/alpakka-kafka/builds/593117356) |
Thanks @raboof . I've started reviewing your branch. I'll follow up in the next week. |
I've created a new PR #930 to continue this work with the goal of including it in Alpakka Kafka 2.0.0. |
By adding a partitioned transactional source, a new producer will be created per topic-partition when hooked up with the sink or flow. If the
transactional.id
is based on the topic-partition then can get around the restriction of distributing the workload across multiple instances without gettingProducerFencedException
s.This was based on reviewing the comments in this thread: #420 (review)