Skip to content
This repository has been archived by the owner on Aug 12, 2024. It is now read-only.

Support for watching SRV records and getting update notifications #9

Merged
merged 30 commits into from
Mar 9, 2015

Conversation

rouzwawi
Copy link
Member

@rouzwawi rouzwawi commented Mar 4, 2015

PR for #8

Features

  • Give a mechanism for getting notified when the set of SRV records for a fqdn change
  • Make it possible to use derivative values of LookupResult as the current set, with notifications only happening if that derived set changes

API

interface DnsSrvWatcher<T> extends Closeable {
  ChangeNotifier<T> watch(String fqdn);
}

interface ChangeNotifier<T> {
  Set<T> current();
  void setListener(Listener<T> listener, boolean fire);
  void close();

  interface Listener<T> {
    void endpointsChanged(ChangeNotification<T> changeNotification);
  }

  interface ChangeNotification<T> {
    Set<T> current();
    Set<T> previous();
  }
}

And the construction API:

DnsSrvResolver resolver = DnsSrvResolvers.newBuilder()
    // configure
    .build();

DnsSrvWatcher<String> watcher =
    DnsSrvResolvers.newWatcherBuilder(resolver, (lookup) -> lookupToString(lookup))
        .polling(1, TimeUnit.SECONDS)
        .usingExecutor(myScheduledExecutorService)
        .withErrorHandler(myErrorHandler)
        .build();


protected abstract void closeImplementation();

protected void fireEndpointsUpdated(ChangeNotification<T> changeNotification) {
Copy link
Member

Choose a reason for hiding this comment

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

Should this be final? I'm wondering if it's protected in order to be overridable (doesn't feel like it) or callable by a sub-class or both. Ditto for newChangeNotification below.

@pettermahlen
Copy link
Member

Looks good - I think the term 'endpoints' in quite a few places should be replaced with 'records' or something along those lines.

/**
* A helper for implementing the {@link ChangeNotifier} interface.
*/
abstract class AbstractChangeNotifier<T> implements ChangeNotifier<T> {
Copy link
Member

Choose a reason for hiding this comment

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

I'd replace all of this custom notification code with Guava EventBus.

Copy link
Member Author

Choose a reason for hiding this comment

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

Just tried it out and it only get's marginally simpler: https://gist.github.com/rouzwawi/fc246c40fb176cd3e792

It could be worth it given that it makes dispatch to several listeners a bit easier.

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 not sure using EventBus makes sense to me, from reading https://code.google.com/p/guava-libraries/wiki/EventBusExplained. It seems a little like overkill.

If we do want to use it, I think the gist needs to be made a little more complex: either make the API support multiple listeners, or revert to using an AtomicReference for the single listener.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, and I think setListener should be replaced with addListener. And I don't think AbstractChangeNotifier needs to keep track of the listener. Actually, shouldn't there be one shared eventbus per srv poller? Maybe I need to read the code more closely.

I am also slightly worried about ordering with the initial notification (fire=true). You could have a race condition between an actual event and the initial faked one.

Copy link
Member

Choose a reason for hiding this comment

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

petter: yes, probably a bit overkill if we only want a single listener. shouldn't multiple listeners be able to listen on the same dns updates though? but perhaps that's not a common usecase.

Copy link
Member

Choose a reason for hiding this comment

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

Re: race between notifications, I agree. This is not something that is expected to be updating with super-high frequency, so it should be quite OK to make setListener and fireRecordsUpdated synchronized, I think?

@spkrka
Copy link
Member

spkrka commented Mar 5, 2015

Overall it seems overly complex - can we make it simpler with fewer interfaces and factories without losing functionality?

@rouzwawi
Copy link
Member Author

rouzwawi commented Mar 5, 2015

@spkrka I understand that it looks complex when looking into the details. But the complexity is only ever exposed if someone starts using DnsSrvWatcherBuilder.customTrigger().

If only used with the polling configuration, the exposed api surface is actually pretty simple IMO.

public void setListener(final Listener<T> listener, final boolean fire) {
checkNotNull(listener, "listener");

if (!listenerRef.compareAndSet(null, listener)) {
Copy link
Member

Choose a reason for hiding this comment

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

synchronize around listener set and fire, to avoid race conditions

@rouzwawi
Copy link
Member Author

rouzwawi commented Mar 6, 2015

Fixed the race issues with listener firing (I hope), and fixed commented tweaks.

The comment on propagating lookup/transformer errors to the listener is still open. I'm unsure if that's the right thing to do. The listener should not have to know about errors, just changes. Because of the async nature of the whole thing, I'm thinking it's good enough to just log the errors and be done with it.

@spkrka
Copy link
Member

spkrka commented Mar 6, 2015

I think it depends on the type of error. Is it a temporary network problem? Logging is fine.

Is the transform function throwing errors? The user of the library should probably be notified.

@rouzwawi
Copy link
Member Author

rouzwawi commented Mar 6, 2015

How do you suggest that would happen?

The user supplied function is used by the library, and if it throws an exception, the error is logged.

@spkrka
Copy link
Member

spkrka commented Mar 6, 2015

Error notification could be part of the change listener callback

@rouzwawi
Copy link
Member Author

rouzwawi commented Mar 8, 2015

Things that can go wrong when the notifiers poll for changes:

  • resolver.resolve(fqdn) throws a DnsException which is a RuntimeException
  • resultTransformer.apply(node) throws an exception
  • resultTransformer.apply(node) return null
  • listener.onChange(changeNotification) throws exception

The two transformer related cases are programmer errors. So just failing fast and printing it in the logs should be good enough. There's no good reaction the code can have to such things happening.

The first, resolver error might be good to catch in order to take action when dns lookups are flaky.

The last one should also be a programmer error since listeners should generally never throw.

Sounds reasonable?

* @param changeNotifiers the notifiers to aggregate
*/
AggregatingChangeNotifier(final List<ChangeNotifier<T>> changeNotifiers) {
this.changeNotifiers = ImmutableList.copyOf(checkNotNull(changeNotifiers));
Copy link
Member

Choose a reason for hiding this comment

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

ImmutableList.copyOf() throws NPE if the collection to be copied is null, so no need to use checkNotNull here.

@pettermahlen
Copy link
Member

LGTM, fix the remaining issues if you like, then merge.

rouzwawi added a commit that referenced this pull request Mar 9, 2015
Support for watching SRV records and getting update notifications
@rouzwawi rouzwawi merged commit a75780c into spotify:master Mar 9, 2015
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants