Skip to content

Commit

Permalink
Add lifecycle management to LookupDataAdapter and subclasses
Browse files Browse the repository at this point in the history
Some data adapters need lifecycle management to handle thread pools,
background jobs etc.
  • Loading branch information
bernd committed Apr 26, 2017
1 parent dbc5175 commit 5b67097
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 4 deletions.
Expand Up @@ -35,6 +35,7 @@
import javax.inject.Singleton;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
Expand Down Expand Up @@ -69,7 +70,18 @@ public LookupTableService(MongoLutService mongoLutService,
}

private void activateTable(String name, @Nullable LookupTable existingTable, LookupTable newTable) {
// Always start the new data adapter before taking it live, it's a no-op if the adapter is already started
newTable.dataAdapter().start();

lookupTables.put(name, newTable);

if (existingTable != null) {
// If the new table has a new data adapter, stop the old one to free up resources
// This needs to happen after the new table is live
if (!Objects.equals(existingTable.dataAdapter(), newTable.dataAdapter())) {
existingTable.dataAdapter().stop();
}
}
}

private void initialize() {
Expand Down
Expand Up @@ -44,7 +44,15 @@ public RandomDataAdapter(@Assisted LookupDataAdapterConfiguration config) {
}

@Override
public LookupResult get(Object key) {
public void doStart() {
}

@Override
public void doStop() {
}

@Override
public LookupResult doGet(Object key) {
return LookupResult.single(key, secureRandom.ints(config.lowerBound(), config.upperBound()).findAny().orElse(0));
}

Expand Down
Expand Up @@ -17,23 +17,73 @@
package org.graylog2.plugin.lookup;

import com.fasterxml.jackson.annotation.JsonProperty;

import org.graylog2.lookup.LookupTable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;
import java.util.concurrent.locks.ReentrantLock;

import static com.google.common.base.Preconditions.checkState;

public abstract class LookupDataAdapter {
private static final Logger LOG = LoggerFactory.getLogger(LookupDataAdapter.class);

private String id;

private volatile boolean started = false;
private volatile boolean failed = false;
private LookupTable lookupTable;
private ReentrantLock lock = new ReentrantLock();

private final LookupDataAdapterConfiguration config;

protected LookupDataAdapter(LookupDataAdapterConfiguration config) {
this.config = config;
}

public boolean isStarted() {
return started;
}

public boolean isFailed() {
return failed;
}

public void start() {
lock.lock();
if (started) {
return;

This comment has been minimized.

Copy link
@kroepke

kroepke Apr 26, 2017

Member

this will never unlock the lock and probably deadlocks when stop() is called

This comment has been minimized.

Copy link
@bernd

bernd Apr 26, 2017

Author Member

Fixed it in c461c47

}
try {
doStart();
started = true;
failed = false;
} catch (Exception e) {
LOG.error("Couldn't start data adapter", e);
failed = true;
} finally {
lock.unlock();
}
}
protected abstract void doStart() throws Exception;

public void stop() {
lock.lock();
if (!started) {
return;
}
try {
doStop();
started = false;
} catch (Exception e) {
LOG.error("Couldn't stop data adapter", e);
failed = true;
} finally {
lock.unlock();
}
}
protected abstract void doStop() throws Exception;

@Nullable
public String id() {
return id;
Expand All @@ -52,7 +102,14 @@ public void setLookupTable(LookupTable lookupTable) {
this.lookupTable = lookupTable;
}

public abstract LookupResult get(Object key);
public LookupResult get(Object key) {
if (failed) {
return LookupResult.empty();
}
checkState(started, "Data adapter needs to be started before it can be used");
return doGet(key);
}
protected abstract LookupResult doGet(Object key);

public abstract void set(Object key, Object value);

Expand Down

0 comments on commit 5b67097

Please sign in to comment.