KAFKA-2593 Key value stores can use specified serializers and deserializers#255
KAFKA-2593 Key value stores can use specified serializers and deserializers#255rhauch wants to merge 6 commits into
Conversation
|
kafka-trunk-git-pr #584 FAILURE |
|
I updated the PR to correct the checkstyle errors. |
|
kafka-trunk-git-pr #585 SUCCESS |
|
LGTM overall, one general comment: could we put various create() functions of different store classes into a single sharable support / factory class, otherwise future users would need to create those functions for each store? |
|
@guozhangwang, sure, it would be possible to put all of the create methods in a single place. However, I've never been a fan of centralized factory methods because it makes other implementations second-class citizens, since they can't be created using the factory. (Yes, we could use Java's Now, I do agree that the 2 existing implementations (or 3 including #256) all have similar factory methods with identical signatures, and that might suggest value to a common way to build them. But perhaps a builder (rather than a factory class) might be more usable by processors and reusable by other It would be possible to create a base interface and impl for this builder in This will expand the Kafka Streams code a bit, but IMO it is worth it because it will make it even easier (albeit a bit more verbose) to create I'll prototype something up, but should I do that as part of this PR/issue, or under a separate "Simplified approach to create KeyValueStores" issue? |
|
@guozhangwang, it may be difficult or unattractive to use a builder with the generic key and value types, unless somehow they're specified in the The good news is that the above code forces the IDE to type-check the |
|
@guozhangwang, there doesn't appear to be a decent way to use a builder while returning a parameterized type. The example mentioned above is pretty verbose, and IMO the static factory methods on each KeyValueStore implementation class is the most pragmatic way to create instances. It's not ideal, because as you pointed out each class has static methods with identical (or at least nearly identical) signatures. OTOH, I'm not really sure how this might be simplified another way. BTW, I'd like to pull some of the commits from #256 into this PR, which is really where they belong. Specifically, all but the one commit that creates the |
|
@guozhangwang, it occurred to me that your goal might actually be to make the implementations of If this is true, and since builders are relatively difficult to implement for parameterized stores, then I agree that a factory class might be the best way forward. Do you have time to list what you might have been thinking of for an API? Perhaps given some store defined in a the store could be created in or with limited memory (see #256): or to use the database: What do you think? This is somewhat like a builder, but all of the parameters that drive the key and value types have to be passed in at once (e.g., only the |
b816811 to
694d6f4
Compare
|
@guozhangwang, rebased this PR. The Jenkins build seemed to have failed on |
|
@guozhangwang, I just added a commit that replaces all of the static factory methods for the different kinds of key-value stores with a simple builder mechanism. Although a fluent API can be used for some parameters, most still have to be passed in to the final 'create(...)' method -- this keeps the API and the implementation very simple. (We could break up more of the parameters into separate fluent-style methods, but this very quickly makes the builder very complicated. See comments above for detail why.) |
|
Once again, the Jenkins test failures seem irrelevant. |
There was a problem hiding this comment.
What are the benefits of requiring ProcessorContextImpl implementing the Supplier interface rather than do the casting?
I originally think there will be two categories of users of Kafka Streaming: 1) normal users will only be interfacing the public APIs, 2) some small groups of developers that add windowing def / etc themselves will need to be exposed with the internal implementations in order to code, so it is OK to ask them to know about ProcessorContextImpl. But maybe there are something that I missed here.
There was a problem hiding this comment.
@guozhangwang: The primary benefit is that we can use these components in unit tests using a mock ProcessorContext. Without this small abstraction, all unit tests would have to use a real ProcessorContextImpl and everything that requires.
75ec501 to
7f6367b
Compare
|
@guozhangwang, incorporated your comments, rebased upon trunk, successfully ran build locally, and updated this PR. |
Add support for the key value stores to use specified serializers and deserializers (aka, "serdes"). Prior to this change, the stores were limited to only the default serdes specified in the topology's configuration and exposed to the processors via the ProcessorContext. Now, using InMemoryKeyValueStore and RocksDBKeyValueStore are similar: both are parameterized on the key and value types, and both have similar multiple static factory methods. The static factory methods either take explicit key and value serdes, take key and value class types so the serdes can be inferred (only for the built-in serdes for string, integer, long, and byte array types), or use the default serdes on the ProcessorContext.
…orContextImpl The cast was used to access ProcessorContextImpl.recordCollector(), and the cast prevent using MeteredKeyValueStore subclasses, SinkNode, and SlidingWindow objects in tests where something other than ProcessorContextImpl was used. This change introduces a RecordCollector.Supplier interface to define this `recordCollector()` method, and changed ProcessorContextImpl and MockProcessorContext to both implement this interface. Now, MeteredKeyValueStore, SinkNode, and SlidingWindow can cast to the interface to access the record collector, making it possible to use them inside unit tests.
Added unit tests for the existing InMemoryKeyValueStore and RocksDBKeyValueStore implementations. A new KeyValueStoreTestDriver class does most of the work and simplifies the tests.
… directory The ProcessorTopologyTest and AbstractKeyValueStoreTest test classes have methods created a shared directory used by all their tests to store local state. When the computer has multiple processors, the build system executes test in parallel, and they interfere with other tests using the same directory. Solve the problem by adding a utility class to create temporary directories and automatically clean them up.
Added a simple fluent builder API to create key-value stores that use in-memory or a local database. Other kinds of state stores can be added in the future.
|
@guozhangwang, I've updated the PR to use the |
|
@guozhangwang: Is there anything else you need from me before this can be merged? Once it's merged, I can then rebase #256. |
|
LGTM, thanks @rhauch ! |
…nding in-memory stores Added a new `KeyValueStore` implementation called `InMemoryLRUCacheStore` that keeps a maximum number of entries in-memory, and as the size exceeds the capacity the least-recently used entry is removed from the store and the backing topic. Also added unit tests for this new store and the existing `InMemoryKeyValueStore` and `RocksDBKeyValueStore` implementations. A new `KeyValueStoreTestDriver` class simplifies all of the other tests, and can be used by other libraries to help test their own custom implementations. This PR depends upon [KAFKA-2593](https://issues.apache.org/jira/browse/KAFKA-2593) and its PR at #255. Once that PR is merged, I can rebase this PR if desired. Two issues were uncovered when creating these new unit tests, and both are also addressed as separate (small) commits in this PR: * The `RocksDBKeyValueStore` initialization was not creating the file system directory if missing. * `MeteredKeyValueStore` was casting to `ProcessorContextImpl` to access the `RecordCollector`, which prevent using `MeteredKeyValueStore` implementations in tests where something other than `ProcessorContextImpl` was used. The fix was to introduce a `RecordCollector.Supplier` interface to define this `recordCollector()` method, and change `ProcessorContextImpl` and `MockProcessorContext` to both implement this interface. Now, `MeteredKeyValueStore` can cast to the new interface to access the record collector rather than to a single concrete implementation, making it possible to use any and all current stores inside unit tests. Author: Randall Hauch <rhauch@gmail.com> Reviewers: Edward Ribeiro, Guozhang Wang Closes #256 from rhauch/kafka-2594
…e#255) - Pausing within the synchonization will cause metrics collection to be blocked. - Move pause to outside of loop to reduce CPU burn until TierTopicManager is ready.
…#255) Reviewers: Manikumar Reddy <manikumar.reddy@gmail.com>
…#255) Reviewers: Manikumar Reddy <manikumar.reddy@gmail.com>
* Disabled the flaky test testTopicUncleanLeaderElectionEnable and testUncleanLeaderElectionEnabled Co-authored-by: Hao Geng <hgeng@hgeng-mn1.linkedin.biz>
…pache#257) Refreshed snapshot from the kafka format sweep review. Five language-engine PRs landed in this session: - PR apache#253 — Annotation paren anchor for continuation slots - PR apache#254 — StructuralBody2x catalog class for inline lambda bodies - PR apache#255 — Lambda body as continuation enclosing scope - PR apache#256 — Inline-lambda body close brace catalog (+0 / +projectDelta) - PR apache#257 — emittedLineStarts restricted to OWNER's prefix slot Sweep stats (post-apache#257): files scanned : 5,834 files changed : 1,027 (17.6%) [initial PR #6: 1,038] byte delta : -10,877 [initial PR #6: -6,661] reformat time : 7.2s total, 1.23 ms/file The larger byte delta vs. the initial sweep reflects PR apache#257 fixing the AsyncKafkaConsumerTest-style "body at col 48" pattern across multiple files — body statements no longer indent to the inflated close-paren-line column, so each affected line shrinks by 30+ characters. Remaining patterns deferred to subsequent PRs: - Chain-dot over-application on Class.STATIC_FIELD / new X() (AdminClientConfig L273/L282, WriteTxnMarkersRequest L135, TxnOffsetCommitRequest L156/L159) - Partial-shift inconsistencies (StreamsResetter, ClientUtilsTest, RequestResponseTest, LeaveGroupRequest*, AlterReplicaLogDirs) - Trailing-whitespace cleanups on blank lines (incidental noise)
…pache#258) Refreshed snapshot. Six language-engine PRs landed this session: - PR apache#253 — Annotation paren anchor for continuation slots - PR apache#254 — StructuralBody2x catalog class for inline lambda bodies - PR apache#255 — Lambda body as continuation enclosing scope - PR apache#256 — Inline-lambda body close brace catalog (+0 / +projectDelta) - PR apache#257 — emittedLineStarts restricted to OWNER's prefix slot - PR apache#258 — alignmentTargets restricted to OWNER's prefix slot Sweep stats (post-apache#258): files scanned : 5,834 files changed : 1,000 (17.1%) [initial PR #6: 1,038] byte delta : -10,630 [initial PR #6: -6,661] reformat time : 7.1s total, 1.22 ms/file Remaining patterns (deferred): - Outer chain-dot receiver-anchor variations (TxnOffsetCommitRequest L156/L159, RequestResponseTest L3373+, etc.) — separate path from PR apache#258's inner-arg fix - Small continuation normalizations of non-standard source (StreamsResetter, ClientUtilsTest, LeaveGroupRequestTest) - Trailing-whitespace cleanups on blank lines (incidental noise)
…pache#259) Refreshed snapshot. Seven language-engine PRs landed this session: - PR apache#253 — Annotation paren anchor for continuation slots - PR apache#254 — StructuralBody2x catalog class for inline lambda bodies - PR apache#255 — Lambda body as continuation enclosing scope - PR apache#256 — Inline-lambda body close brace catalog - PR apache#257 — emittedLineStarts restricted to OWNER's prefix slot - PR apache#258 — alignmentTargets restricted to OWNER's prefix slot - PR apache#259 — Chain wrap preservation when siblings agree on column Sweep stats (post-apache#259): files scanned : 5,834 files changed : 990 (17.0%) [initial PR #6: 1,038] byte delta : -10,685 [initial PR #6: -6,661] reformat time : ~7s total Most called-out files from the user's original review now fully closed: - ShareConsumerTest (body + close brace) - ProducerIdExpirationTest - DeleteAclsResponse - CustomQuotaCallbackTest - AsyncKafkaConsumerTest (body indent regression) - AdminClientConfig L273/L282 - WriteTxnMarkersRequest L135 - TxnOffsetCommitRequest L156-159 - RequestResponseTest chain shifts - AlterReplicaLogDirsRequestTest (cascade-closed) Remaining residuals (~5 files, small): - StreamsResetter L580 (single `||` continuation normalisation) - ClientUtilsTest L79/L143 (continuation normalisation) - LeaveGroupRequestTest L55 (continuation shift) - LeaveGroupResponseTest (trailing-ws on blank lines) - ReassignPartitionsUnitTest (single line) These are mostly the formatter normalising slightly non-standard source — not really bugs.
…pache#260) Refreshed snapshot. Eight language-engine PRs landed this session: - PR apache#253 — Annotation paren anchor for continuation slots - PR apache#254 — StructuralBody2x catalog class for inline lambda bodies - PR apache#255 — Lambda body as continuation enclosing scope - PR apache#256 — Inline-lambda body close brace catalog - PR apache#257 — emittedLineStarts restricted to OWNER's prefix slot - PR apache#258 — alignmentTargets restricted to OWNER's prefix slot - PR apache#259 — Chain wrap preservation when siblings agree on column - PR apache#260 — Binary OPERATOR_PREFIX consults outermost binary's anchor Sweep stats (post-apache#260): files scanned : 5,834 files changed : 989 (17.0%) [initial PR #6: 1,038] byte delta : -10,663 reformat time : ~7s total Almost all called-out files from the user's original review now fully closed. Remaining residuals (<5 files) are correct normalisations of slightly non-standard source or trailing whitespace inside comment content (intentionally out of scope for this engine version per user design choice).
…pache#261) Refreshed snapshot. Nine language-engine PRs landed this session: - PR apache#253 — Annotation paren anchor for continuation slots - PR apache#254 — StructuralBody2x catalog class for inline lambda bodies - PR apache#255 — Lambda body as continuation enclosing scope - PR apache#256 — Inline-lambda body close brace catalog - PR apache#257 — emittedLineStarts restricted to OWNER's prefix slot - PR apache#258 — alignmentTargets restricted to OWNER's prefix slot - PR apache#259 — Chain wrap preservation when siblings agree on column - PR apache#260 — Binary OPERATOR_PREFIX consults outermost binary's anchor - PR apache#261 — Nested continuation slots use innermost enclosing scope Sweep stats (post-apache#261): files scanned : 5,834 files changed : 986 (16.9%) [initial PR #6: 1,038] byte delta : -11,137 [initial PR #6: -6,661] reformat time : ~7s total PR #7 review comments status: - Comments 1+2 closed (PR apache#261) - Comments 3 + 4/5 deferred (workbench-first investigation; diagnosis captured in language-engine memory project_pr7_deferred_comments_2026_05_17.md)
Add support for the key value stores to use specified serializers and deserializers (aka, "serdes"). Prior to this change, the stores were limited to only the default serdes specified in the topology's configuration and exposed to the processors via the ProcessorContext.
Now, using InMemoryKeyValueStore and RocksDBKeyValueStore are similar: both are parameterized on the key and value types, and both have similar multiple static factory methods. The static factory methods either take explicit key and value serdes, take key and value class types so the serdes can be inferred (only for the built-in serdes for string, integer, long, and byte array types), or use the default serdes on the ProcessorContext.