Enable multiple event listers to consume for Polaris events#3973
Enable multiple event listers to consume for Polaris events#3973nandorKollar wants to merge 12 commits intoapache:mainfrom
Conversation
|
@jbonofre @adutra @adnanhemani would you mind share your thoughts about this approach? |
d0242ed to
09c5600
Compare
09c5600 to
aad3caf
Compare
adutra
left a comment
There was a problem hiding this comment.
@nandorKollar this is look promising thanks!
I think we should avoid calling dispatch() altogether (and avoid materializing the PolarisEvent instance on the heap) if we know the event type is not needed. I would like us to consider that, if a user deactivates all listeners, they would get a performance comparable to what they would get if there were no events in Polaris at all.
...ache/polaris/service/events/jsonEventListener/aws/cloudwatch/AwsCloudWatchEventListener.java
Outdated
Show resolved
Hide resolved
...ache/polaris/service/events/jsonEventListener/aws/cloudwatch/AwsCloudWatchEventListener.java
Outdated
Show resolved
Hide resolved
...rvice/src/main/java/org/apache/polaris/service/events/PolarisEventListenerConfiguration.java
Outdated
Show resolved
Hide resolved
...rvice/src/main/java/org/apache/polaris/service/events/PolarisEventListenerConfiguration.java
Outdated
Show resolved
Hide resolved
runtime/service/src/main/java/org/apache/polaris/service/events/PolarisEventListeners.java
Outdated
Show resolved
Hide resolved
8b05fcf to
1c71676
Compare
| public void onStartup(@Observes StartupEvent event) { | ||
| String listerTypes = configuration.types().orElse(""); | ||
| if (listerTypes.isBlank()) { | ||
| return; |
There was a problem hiding this comment.
If we don't have types here, it means we never reach type() merging bellow (line 51).
If a user only set polaris.event-listener.type (the old config) without setting types, the onStartup() method just returns directly, and no listener is registered.
It means it's a breaking change.
Imho, the type() merge logic should be before the early return, or this check should check both types and type (to be backward compatible).
There was a problem hiding this comment.
Good point, thanks! This is indeed broken, fixed in 1a0aaa5
| * identifier. | ||
| */ | ||
| String type(); | ||
| Optional<String> type(); |
There was a problem hiding this comment.
We don't need this one anymore, right?
There was a problem hiding this comment.
I think we need it to read the old config: polaris.event-listener.type. It is optional to allow one to omit. The value of this config (if present), and the new one polaris.event-listener.types should be merged. We should also indicate that polaris.event-listener.type is deprecated, should not be used.
There was a problem hiding this comment.
Hmm indeed our evolution guidelines considers changes to the configuration as breaking changes: https://polaris.apache.org/releases/1.3.0/evolution/
So, I think we indeed need to deprecate this config but keep it around for some time.
There was a problem hiding this comment.
How can we mark it deprecated?
There was a problem hiding this comment.
You should:
- Annotate the method with
@Deprecated - Add a note to the changelog.
There was a problem hiding this comment.
We could probably add a config redirect too.
Cf.
There was a problem hiding this comment.
WDYT about config redirects? My code pointer above may not be an exact match for this use case, but I hope it can be a starting point 🙂
There was a problem hiding this comment.
Well, I’m trying to understand how we can use that. What would be the benefit of a config redirect? The old property is already mapped to the new one, I think. Would ConfigSourceFactory make this mapping cleaner, or is there a specific scenario that wouldn’t work with the current approach?
There was a problem hiding this comment.
Where is the old config mapped to the new one (I might have missed it)?
What I mean is that ideally we should treat the old polaris.event-listener.type values as a single list entry for the new polaris.event-listener.types config (if the new one is not set explicitly). The no-op old type value can be ignored (mapped to empty list).
| * Comma separated list of event listers, each item must be a registered {@link | ||
| * PolarisEventListener} identifier. | ||
| */ | ||
| Optional<String> types(); |
There was a problem hiding this comment.
| Optional<String> types(); | |
| Optional<List<String>> types(); |
It's a bit odd to see an Optional<List<...> – but that's how SmallRye Config wants it 😅
There was a problem hiding this comment.
Update: Optional<Set<String>> types(); is probably better.
48b5fbd to
c9f843b
Compare
|
|
||
| polaris.file-io.type=default | ||
|
|
||
| polaris.event-listener.type=no-op |
There was a problem hiding this comment.
As there's no need to set this property to no-op, should we also delete NoOpPolarisEventListener? It will be a breaking change, because anyone who still refers to no-op with the old polaris.event-listener.type (or even with the new polaris.event-listener.types) will hit the following error: No bean found for required type [interface org.apache.polaris.service.events.listeners.PolarisEventListener] and qualifiers [[@jakarta.enterprise.inject.Any(), @io.smallrye.common.annotation.Identifier(value="no-op")]]
We can mark it deprecated though.
There was a problem hiding this comment.
Yes, I think we could delete the no-op listener. Regarding breaking changes, our evolution guidelines accept that: https://polaris.apache.org/releases/1.3.0/evolution/
…tomatically by smallrye
ee21f22 to
716f6db
Compare
Thanks, this makes sense, however I've further questions related to this. Do you happen to know why does AwsCloudWatchEventListener filter only for a single event type? I think event listeners should accept any event type, and allow users to configure a particular event listener to listen for only a certain events, but the listener code itself should not assume that it is working only with event type xyz. |
Completely agree here. Listeners should NOT filter events, and especially not in a hard-coded fashion – that's a user decision. @adnanhemani may know why the CoudWatch listener was designed that way. Imho it's not very useful – it can only be useful if you are interested in after-refresh-table events, and hence, in certain table optimization use cases. |
runtime/service/src/main/java/org/apache/polaris/service/events/PolarisEventListeners.java
Outdated
Show resolved
Hide resolved
@adutra How about introducing the event listener configuration in a follow-up PR, together with the optimization you mentioned earlier (i.e., avoiding materializing the PolarisEvent)? In that PR, we could address the following points:
What do you think? |
|
Oh and we shouldn't forget #2573 either. We need to find a way to trigger event cleanup, or even set retention period for events persisted in the database. |
I'm all in for breaking the work in many smaller PRs, but I'm a little confused: you said you would like to introduce the event listener configuration in a follow-up PR – but you also wrote you would like to make each event listener configurable in this PR. Could you clarify this small item? |
I recommend to target only focus on making multiple event listeners work in this PR, and rather implement the rest in a second PR. The only configuration parameter for now is deprecating |
That works for me. I think this PR is good to go then, but it would still be nice to add a note to the changelog about the deprecation of |
Should we add it only to the changelog? EDIT: never mind, found the way to deprecate it in the doc too. :) |
dimas-b
left a comment
There was a problem hiding this comment.
Sorry about last minute comments. The PR LGTM overall. My only suggestion is to try and add automatic redirects for the old config name.
|
|
||
| public class EventBusConfigurer { | ||
| private static final String LOCAL_CODEC_NAME = "local"; | ||
| private static final LocalEventBusCodec<PolarisEvent> LOCAL_CODEC = |
There was a problem hiding this comment.
nit: is this constant really required?
| * identifier. | ||
| */ | ||
| String type(); | ||
| Optional<String> type(); |
There was a problem hiding this comment.
We could probably add a config redirect too.
Cf.
|
|
||
| @Inject EventBus eventBus; | ||
|
|
||
| // @Inject PolarisEventListeners polarisEventListeners; |
| this.catalog = initCatalog("my-catalog", ImmutableMap.of()); | ||
| testPolarisEventListener = (TestPolarisEventListener) polarisEventListener; | ||
| testPolarisEventListener = | ||
| (TestPolarisEventListener) polarisEventListener.select(Identifier.Literal.of("test")).get(); |
There was a problem hiding this comment.
Would it work if we had @Identifier on the polarisEventListener field without Intance<>?
There was a problem hiding this comment.
Yes, looks like it works! Thanks, great recommendation!
| "relational-jdbc", | ||
| "polaris.persistence.relational.jdbc.database-type", | ||
| "cockroachdb"); | ||
| public class TestPolarisEventDispatcher implements PolarisEventDispatcher { |
There was a problem hiding this comment.
nit: the Test prefix makes this class look like a JUnit test class, which it is not. How about InMemoryEventCollector?
There was a problem hiding this comment.
InMemoryEventCollector suggests that it can be used outside of tests too no? This class is indented to be used only in tests though.
There was a problem hiding this comment.
The name describes what the class does 🤷 Where it can be used is controlled by the location of the class in the sources tree (ATM under /test).
If we (hypothetically) wanted to expose this functionality to mainstream code, we'd only have to move the class without renaming it.
16170de to
b570a2e
Compare
…er_service_bus # Conflicts: # runtime/service/src/main/java/org/apache/polaris/service/config/ServiceProducers.java
This PR enables multiple event listeners watching for Polaris events.
Checklist
CHANGELOG.md(if needed)site/content/in-dev/unreleased(if needed)