-
Notifications
You must be signed in to change notification settings - Fork 358
Create PolarisEvent only when event type is actually supported by listener #3442
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
base: main
Are you sure you want to change the base?
Create PolarisEvent only when event type is actually supported by listener #3442
Conversation
adutra
left a comment
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.
Thanks @olsoloviov for this PR!
The solution looks nice and simple. I have some concerns though.
First off, we are still creating a closure for every event. Since it must capture variables from outside the closure (e.g. the requests and responses), this creates an anonymous class that gets instantiated every time. The cost is low, but is not zero.
But my main concern is that your supplier is not memoized. For now we only have one listener active at a time, but as soon as we get more than one, your proposed design would result in each listener getting a different PolarisEvent instance, which wouldn't be correct and would also be quite expensive.
Polling the listener before emitting the event does not align well with future plans to support multiple active event listeners
In fact, I think it could align, but I agree that it requires some work. One way to solve this problem is as follows:
Declare a new supportedEventTypes() method:
public interface PolarisEventListener {
default EnumSet<PolarisEventType> supportedEventTypes() {
return EnumSet.noneOf(PolarisEventType.class);
}
default void onEvent(PolarisEvent event) {}
}Then:
// For single listener
if (listener.supportedEventTypes().contains(type)) {
listener.onEvent(new PolarisEvent(...));
}
// For multiple listeners - create event once if any listener supports it
Set<PolarisEventListener> interestedListeners = listeners.stream()
.filter(l -> l.supportedEventTypes().contains(type))
.collect(toSet());
if (!interestedListeners.isEmpty()) {
PolarisEvent event = new PolarisEvent(...);
interestedListeners.forEach(l -> l.onEvent(event));
}The above still creates a Set<PolarisEventListener> every time. We can go further and create an intermediate component that manages the event dispatching logic to all subscribers. Then this component could memoize which event types have interested listeners and which don't. This would result in a near-zero cost in the case the event type is not wanted by any listener:
@ApplicationScoped
public class PolarisEventDispatcher {
private final List<PolarisEventListener> listeners;
private final EnumSet<PolarisEventType> supportedTypes;
@Inject
public PolarisEventDispatcher(@Any Instance<PolarisEventListener> listeners) {
this.listeners = listeners.stream().toList();
// Memoize at startup: which event types have at least one interested listener
this.supportedTypes = EnumSet.noneOf(PolarisEventType.class);
for (PolarisEventListener listener : this.listeners) {
this.supportedTypes.addAll(listener.supportedEventTypes());
}
}
public boolean hasListeners(PolarisEventType type) {
return supportedTypes.contains(type);
}
public void fireEvent(PolarisEvent event) {
for (PolarisEventListener listener : listeners) {
if (listener.supportedEventTypes().contains(event.type())) {
listener.onEvent(event);
}
}
}
}| switch (type) { | ||
| case AFTER_CREATE_TABLE -> handleAfterCreateTable(eventSupplier.get()); | ||
| case AFTER_CREATE_CATALOG -> handleAfterCreateCatalog(eventSupplier.get()); | ||
| default -> {} |
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.
Maybe keep the comment?
| public interface PolarisEventListener { | ||
|
|
||
| default void onEvent(PolarisEvent event) {} | ||
| default void onEvent(PolarisEventType type, Supplier<PolarisEvent> eventSupplier) {} |
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.
Unfortunately this is not binary-compatible. We would need to keep the old method around. e.g.
default void onEvent(PolarisEventType type, Supplier<PolarisEvent> supplier) { onEvent(supplier.get()); }
@Deprecated
default void onEvent(PolarisEventType type, Supplier<PolarisEvent> eventSupplier) {}
This PR implements @adutra's idea from his comment in #3293: #3293 (comment).
I used a bit different approach with Supplier instead of conditional calls to OnEvent(). Polling the listener before emitting the event does not align well with future plans to support multiple active event listeners.
Checklist
CHANGELOG.md(if needed)site/content/in-dev/unreleased(if needed)