[improve][client] Add Builder Methods to Create Message-based TableView - #24809
[improve][client] Add Builder Methods to Create Message-based TableView#24809namest504 wants to merge 11 commits into
Conversation
…raw Pulsar message
…agement by adding retain() before storing message to rawMessages - Prevent premature message release that causes getRawMessage to return null keys. - Ensure message reference count is balanced by retaining message before storing and releasing old message after replacement. - Keep message release in finally block for safety.
There was a problem hiding this comment.
Thanks for the contribution!
Since this changes the Pulsar API, we'd need to first go through the PIP process to modify the public API.
Instead of adding a separate ConcurrentMap for storing the messages, I'd suggest going with an approach where the TableViewBuilder would have separate methods for creating a view for messages.
TableView<Message<T>> createForMessages() throws PulsarClientException;
CompletableFuture<TableView<Message<T>>> createForMessagesAsync() throws PulsarClientException;I'm not exactly sure if there's any obstacles with this approach, but it seems that it could be a better way forward so that the existing runtime behavior of TableView implementation wouldn't change (like it does with the current PR changes).
|
Hi @lhotari, Thanks for the review and the great suggestion about using the I completely agree that we should avoid impacting the runtime behavior for existing users. As you recommended, I've created a PIP for this API change, which you can find here: #24842 Let's continue the design discussion over on the PIP PR. |
@namest504 you seemed to ignore the suggestion in the comment. I'd suggest revisiting the PIP accordingly and renaming it. You can reply to the comment on the PIP PR, #24842 (review). |
Signed-off-by: namest504 <namest504@gmail.com>
Signed-off-by: namest504 <namest504@gmail.com>
Signed-off-by: namest504 <namest504@gmail.com>
… method Signed-off-by: namest504 <namest504@gmail.com>
Resolve conflicts by rebuilding the PIP-445 changes on top of current master: - Keep the original TableViewImpl class name; move the shared logic to AbstractTableViewImpl - Rename MessageMapperTableView to MessageMapperTableViewImpl - Drop the unused MessageTableView class and the import/FQN churn
…r argument - The mapper may retain the Message instance (e.g. Function.identity()), so the reader must not use pooled messages for mapped views; the payload buffer would otherwise be released or leaked. The classic TableView keeps using pooled messages and releasing them as before. - Reject a null mapper with IllegalArgumentException (failed future in the async variant) instead of failing later in the async pipeline. - Add unit tests for createMapped/createMappedAsync in TableViewBuilderImplTest.
|
@lhotari I've addressed the review comments and resolved the conflicts with master (rebuilt the refactoring on top of the current Gradle/slog codebase). Main changes since your last review:
The vote result for PIP-445 has been posted on the dev mailing list (3 binding +1s), and I've also updated the PIP document in #24842 to match the final class names. PTAL when you have a chance. |
…bleViewImpl FieldUtils.readDeclaredField only looks at the exact class, so reading the reader/compactionStrategy fields from TableViewImpl fails now that they live in AbstractTableViewImpl. Use readField, which traverses the class hierarchy.
Fixes: #24744
Main Issue: #24744
PIP: 445 #24842
Motivation
The current
TableViewAPI only exposes the deserialized message value, which limits access to essential message metadata like properties, event time, or the raw message object itself.This PR introduces a flexible, non-breaking mechanism to create value-mapped views over a topic. It replaces the initial proposal of adding a
getRawMessage()method, which would have performance implications for all users. Instead, it adds acreateMappedmethod to theTableViewBuilder, allowing users to transform a fullMessage<T>into any custom objectVthat suits their needs. This provides maximum flexibility, from accessing the full raw message to creating custom, memory-efficient objects.Modifications
createMappedandcreateMappedAsynctoTableViewBuilder: These methods accept aFunction<Message<T>, V>mapper, letting users define their own mapping from a message to the value stored in theTableView. Anullmapper is rejected withIllegalArgumentException(failed future in the async variant), and anullreturn value from the mapper is treated as a tombstone.AbstractTableViewImpl<T, V>:TableViewImplkeeps its name and becomes a thin subclass, so the behavior for existing users (including message pooling and release) is unchanged. The class naming follows the structure from @lhotari's experiment branch.MessageMapperTableViewImpl<T, V>: Implements the mapped views. Message pooling is disabled for mapped views since the mapper may retain theMessageinstance (e.g. withFunction.identity()), so the pooled payload buffer must not be reused or released.Verifying this change
Unit tests:
TableViewBuilderImplTest.testCreateMapped*(mapped view creation, null mapper validation for both sync and async variants).Integration tests in
TableViewTest:testCreateMapped(custom mapping, value updates, tombstone vianullmapping) andtestCreateMappedWithIdentityMapper(TableView<Message<T>>with metadata access).Verified locally with
./gradlew quickCheckand the test classes above.Make sure that the change passes the CI checks.
Does this pull request potentially affect one of the following parts:
If the box was checked, please highlight the changes
Documentation
docdoc-requireddoc-not-neededdoc-completeMatching PR in forked repository
PR in forked repository: fork-repo