Skip to content

Support multiple event listener plugins#2305

Closed
jdintruff wants to merge 1 commit intotrinodb:masterfrom
jdintruff:multiple-event-listeners
Closed

Support multiple event listener plugins#2305
jdintruff wants to merge 1 commit intotrinodb:masterfrom
jdintruff:multiple-event-listeners

Conversation

@jdintruff
Copy link
Contributor

Per #2299 it would be helpful to be able to declare both multiple instances of the same event listener as well as multiple instances of different event listeners. I've tested this locally with two simple logging plugins but I still need to write proper tests.

@cla-bot
Copy link

cla-bot bot commented Dec 18, 2019

Thank you for your pull request and welcome to our community. We require contributors to sign our Contributor License Agreement, and we don't seem to have you on file. In order for us to review and merge your code, please submit the signed CLA to cla@prestosql.io. For more information, see https://github.com/prestosql/cla.

@martint
Copy link
Member

martint commented Dec 18, 2019

@cla-bot check

@cla-bot cla-bot bot added the cla-signed label Dec 18, 2019
@cla-bot
Copy link

cla-bot bot commented Dec 18, 2019

The cla-bot has been summoned, and re-checked this pull request!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should do this like the recent access control change, where each file name is listed. Unlike catalogs, there’s no concept of “instance name” used by Presto, so we can reference the files directly. See https://github.com/prestosql/presto/blob/master/presto-main/src/main/java/io/prestosql/security/AccessControlConfig.java and https://github.com/prestosql/presto/blob/master/presto-main/src/main/java/io/prestosql/security/AccessControlManager.java

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the doc here to reflect the new conf changes

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please model the loading code and logging after AccessControlManager

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated this to be more like the AccessControlManager

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m thinking we should catch exceptions and log a warning, so that one listener doesn’t affect others (especially since there is no defined order they will be invoked)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a warning for when exceptions are thrown from the event listeners but I believe this would still allow event listeners to block one another

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, being slow or hanging is a problem, but there's not much that can be done. We'd need separate executors for each listener with a fixed queue size that eventually discards events.

@jdintruff jdintruff force-pushed the multiple-event-listeners branch 2 times, most recently from 58876c2 to 63805ff Compare January 3, 2020 08:19
@jdintruff jdintruff marked this pull request as ready for review January 3, 2020 08:19
@jdintruff jdintruff force-pushed the multiple-event-listeners branch from 63805ff to d9aaf09 Compare January 3, 2020 11:31
Copy link
Member

@electrum electrum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies for the late follow up. A few minor comments, otherwise ready to go. Thanks for implementing this feature.

private final List<File> configFiles;
private final Map<String, EventListenerFactory> eventListenerFactories = new ConcurrentHashMap<>();
private final AtomicReference<Optional<EventListener>> configuredEventListener = new AtomicReference<>(Optional.empty());
private final AtomicReference<List<EventListener>> configuredEventListeners =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This wrapping shouldn't change


if (eventListenerFactories.putIfAbsent(eventListenerFactory.getName(), eventListenerFactory) != null) {
throw new IllegalArgumentException(format("Event listener '%s' is already registered", eventListenerFactory.getName()));
throw new IllegalArgumentException(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

checkState(!isNullOrEmpty(name), "Access control configuration %s does not contain '%s'", configFile, NAME_PROPERTY);

setConfiguredEventListener(name, properties);
List<EventListener> eventListeners =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Formatting nit: don't wrap the assignment, but do wrap the stream operations. Also, prefer the immutable collectors.

List<EventListener> eventListeners = configFiles.stream()
        .map(this::createEventListener)
        .collect(toImmutableList());

}
configFiles = ImmutableList.of(CONFIG_FILE);
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to be safe, let's add an AtomicBoolean so that we can't accidentally load twice

checkState(loading.compareAndSet(false, true), "Event listeners already loaded");

log.info("-- Loaded event listener %s --", name);
String name = properties.remove(EVENT_LISTENER_NAME_PROPERTY);
checkArgument(!isNullOrEmpty(name), "EventListener plugin configuration for %s does not contain %s", configFile,
EVENT_LISTENER_NAME_PROPERTY);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: don't wrap here

try {
listener.queryCompleted(queryCompletedEvent);
}
catch (Exception e) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be RuntimeException since the event listener doesn't throw any checked exceptions. However, I'm thinking we should make this Throwable. While we hope plugins don't throw Error, if they do, there's nothing we can do, so we might as well log it and continue.

Same for the other events.

{
configBinder(binder).bindConfig(EventListenerConfig.class);
binder.bind(EventListenerManager.class).in(Scopes.SINGLETON);
newExporter(binder).export(EventListenerManager.class).withGeneratedName();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to add this since EventListenerManager doesn't have any @Managed annotations.

extends EventListenerManager
{
private final AtomicReference<Optional<EventListener>> configuredEventListener = new AtomicReference<>(Optional.empty());
private final AtomicReference<Set<EventListener>> configuredEventListeners = new AtomicReference(new HashSet());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use new HashSet<>() so that it's not a raw type.

We don't need to change this class at all, since it still only supports a single event listener. If we want to change it, we should make it add each new listener (instead of overwriting the set). However, I feel changing the test implementation is somewhat unrelated to the rest of the PR (which is allow multiple listeners for the main server).

@Test
public void testDefaults()
{
ConfigAssertions.assertRecordedDefaults(ConfigAssertions.recordDefaults(EventListenerConfig.class)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: static import all the ConfigAssertions methods

.build();

EventListenerConfig expected = new EventListenerConfig()
.setEventListenerFiles("a,b,c");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call the List version of the setter in the test, in order to verify the splitting workers correctly. (the value from the map will go through the string version)

@tooptoop4
Copy link
Contributor

tooptoop4 commented Mar 1, 2020

some conflicts @jdintruff

@ankitdixit
Copy link
Member

@jdintruff just wanted to ask if you plan to look at this in near future.

@kokosing
Copy link
Member

@jdintruff If you don't have time to work on this, I can take over this.

@kokosing
Copy link
Member

Replaced with #3128

@kokosing kokosing closed this Mar 17, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

6 participants