[QTL] Allows RegisteredLookupExtractionFn to find its lookups lazily (#2971)

* Allows RegisteredLookupExtractionFn to find its lookups lazily

* Use raw variables instead of AtomicReference

* Make sure to use volatile

* Remove extra local variable.

* Move from BAOS to ByteBuffer
This commit is contained in:
Charles Allen 2016-05-17 11:29:39 -07:00
parent e79284da59
commit fb01db4db7
2 changed files with 97 additions and 57 deletions

View File

@ -23,23 +23,25 @@ import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Preconditions;
import com.metamx.common.StringUtils;
import io.druid.query.extraction.ExtractionFn;
import java.nio.ByteBuffer;
import javax.annotation.Nullable;
public class RegisteredLookupExtractionFn implements ExtractionFn
{
private final LookupExtractionFn delegate;
private final String name;
RegisteredLookupExtractionFn(LookupExtractionFn delegate, String name)
{
this.delegate = delegate;
this.name = name;
}
// Protected for moving to not-null by `delegateLock`
private volatile LookupExtractionFn delegate = null;
private final Object delegateLock = new Object();
private final LookupReferencesManager manager;
private final String lookup;
private final boolean retainMissingValue;
private final String replaceMissingValueWith;
private final boolean injective;
private final boolean optimize;
@JsonCreator
public static RegisteredLookupExtractionFn create(
public RegisteredLookupExtractionFn(
@JacksonInject LookupReferencesManager manager,
@JsonProperty("lookup") String lookup,
@JsonProperty("retainMissingValue") final boolean retainMissingValue,
@ -49,93 +51,105 @@ public class RegisteredLookupExtractionFn implements ExtractionFn
)
{
Preconditions.checkArgument(lookup != null, "`lookup` required");
final LookupExtractorFactory factory = manager.get(lookup);
Preconditions.checkNotNull(factory, "lookup [%s] not found", lookup);
return new RegisteredLookupExtractionFn(
new LookupExtractionFn(
factory.get(),
retainMissingValue,
replaceMissingValueWith,
injective,
optimize
),
lookup
);
this.manager = manager;
this.replaceMissingValueWith = replaceMissingValueWith;
this.retainMissingValue = retainMissingValue;
this.injective = injective;
this.optimize = optimize;
this.lookup = lookup;
}
@JsonProperty("lookup")
public String getLookup()
{
return name;
return lookup;
}
@JsonProperty("retainMissingValue")
public boolean isRetainMissingValue()
{
return delegate.isRetainMissingValue();
return retainMissingValue;
}
@JsonProperty("replaceMissingValueWith")
public String getReplaceMissingValueWith()
{
return delegate.getReplaceMissingValueWith();
return replaceMissingValueWith;
}
@JsonProperty("injective")
public boolean isInjective()
{
return delegate.isInjective();
return injective;
}
@JsonProperty("optimize")
public boolean isOptimize()
{
return delegate.isOptimize();
return optimize;
}
@Override
public byte[] getCacheKey()
{
return delegate.getCacheKey();
final byte[] keyPrefix = StringUtils.toUtf8(getClass().getCanonicalName());
final byte[] lookupName = StringUtils.toUtf8(getLookup());
final byte[] delegateKey = ensureDelegate().getCacheKey();
return ByteBuffer
.allocate(keyPrefix.length + 1 + lookupName.length + 1 + delegateKey.length)
.put(keyPrefix).put((byte) 0xFF)
.put(lookupName).put((byte) 0xFF)
.put(delegateKey)
.array();
}
@Override
public String apply(Object value)
{
return delegate.apply(value);
return ensureDelegate().apply(value);
}
@Override
public String apply(String value)
{
return delegate.apply(value);
return ensureDelegate().apply(value);
}
@Override
public String apply(long value)
{
return delegate.apply(value);
return ensureDelegate().apply(value);
}
@Override
public boolean preservesOrdering()
{
return delegate.preservesOrdering();
return ensureDelegate().preservesOrdering();
}
@Override
public ExtractionType getExtractionType()
{
return delegate.getExtractionType();
return ensureDelegate().getExtractionType();
}
@Override
public String toString()
private LookupExtractionFn ensureDelegate()
{
return "RegisteredLookupExtractionFn{" +
"delegate=" + delegate +
", name='" + name + '\'' +
'}';
if (null == delegate) {
// http://www.javamex.com/tutorials/double_checked_locking.shtml
synchronized (delegateLock) {
if (null == delegate) {
delegate = new LookupExtractionFn(
Preconditions.checkNotNull(manager.get(getLookup()), "Lookup [%s] not found", getLookup()).get(),
isRetainMissingValue(),
getReplaceMissingValueWith(),
isInjective(),
isOptimize()
);
}
}
}
return delegate;
}
@Override
@ -150,18 +164,44 @@ public class RegisteredLookupExtractionFn implements ExtractionFn
RegisteredLookupExtractionFn that = (RegisteredLookupExtractionFn) o;
if (!delegate.equals(that.delegate)) {
if (isRetainMissingValue() != that.isRetainMissingValue()) {
return false;
}
return name.equals(that.name);
if (isInjective() != that.isInjective()) {
return false;
}
if (isOptimize() != that.isOptimize()) {
return false;
}
if (!getLookup().equals(that.getLookup())) {
return false;
}
return getReplaceMissingValueWith() != null
? getReplaceMissingValueWith().equals(that.getReplaceMissingValueWith())
: that.getReplaceMissingValueWith() == null;
}
@Override
public int hashCode()
{
int result = delegate.hashCode();
result = 31 * result + name.hashCode();
int result = getLookup().hashCode();
result = 31 * result + (isRetainMissingValue() ? 1 : 0);
result = 31 * result + (getReplaceMissingValueWith() != null ? getReplaceMissingValueWith().hashCode() : 0);
result = 31 * result + (isInjective() ? 1 : 0);
result = 31 * result + (isOptimize() ? 1 : 0);
return result;
}
@Override
public String toString()
{
return "RegisteredLookupExtractionFn{" +
"delegate=" + delegate +
", lookup='" + lookup + '\'' +
", retainMissingValue=" + retainMissingValue +
", replaceMissingValueWith='" + replaceMissingValueWith + '\'' +
", injective=" + injective +
", optimize=" + optimize +
'}';
}
}

View File

@ -52,7 +52,7 @@ public class RegisteredLookupExtractionFnTest
final LookupReferencesManager manager = EasyMock.createStrictMock(LookupReferencesManager.class);
managerReturnsMap(manager);
EasyMock.replay(manager);
final RegisteredLookupExtractionFn fn = RegisteredLookupExtractionFn.create(
final RegisteredLookupExtractionFn fn = new RegisteredLookupExtractionFn(
manager,
LOOKUP_NAME,
true,
@ -75,16 +75,16 @@ public class RegisteredLookupExtractionFnTest
EasyMock.expect(manager.get(EasyMock.eq(LOOKUP_NAME))).andReturn(null).once();
EasyMock.replay(manager);
expectedException.expectMessage("lookup [some lookup] not found");
expectedException.expectMessage("Lookup [some lookup] not found");
try {
RegisteredLookupExtractionFn.create(
new RegisteredLookupExtractionFn(
manager,
LOOKUP_NAME,
true,
null,
true,
false
);
).apply("foo");
}
finally {
EasyMock.verify(manager);
@ -95,7 +95,7 @@ public class RegisteredLookupExtractionFnTest
public void testNullLookup()
{
expectedException.expectMessage("`lookup` required");
RegisteredLookupExtractionFn.create(
new RegisteredLookupExtractionFn(
null,
null,
true,
@ -113,7 +113,7 @@ public class RegisteredLookupExtractionFnTest
final LookupReferencesManager manager = EasyMock.createStrictMock(LookupReferencesManager.class);
managerReturnsMap(manager);
EasyMock.replay(manager);
final RegisteredLookupExtractionFn fn = RegisteredLookupExtractionFn.create(
final RegisteredLookupExtractionFn fn = new RegisteredLookupExtractionFn(
manager,
LOOKUP_NAME,
true,
@ -141,7 +141,7 @@ public class RegisteredLookupExtractionFnTest
final LookupReferencesManager manager = EasyMock.createStrictMock(LookupReferencesManager.class);
managerReturnsMap(manager);
EasyMock.replay(manager);
final RegisteredLookupExtractionFn fn = RegisteredLookupExtractionFn.create(
final RegisteredLookupExtractionFn fn = new RegisteredLookupExtractionFn(
manager,
LOOKUP_NAME,
false,
@ -151,7 +151,7 @@ public class RegisteredLookupExtractionFnTest
);
Assert.assertEquals(
fn,
RegisteredLookupExtractionFn.create(
new RegisteredLookupExtractionFn(
manager,
LOOKUP_NAME,
false,
@ -162,7 +162,7 @@ public class RegisteredLookupExtractionFnTest
);
Assert.assertNotEquals(
fn,
RegisteredLookupExtractionFn.create(
new RegisteredLookupExtractionFn(
manager,
LOOKUP_NAME,
true,
@ -174,7 +174,7 @@ public class RegisteredLookupExtractionFnTest
Assert.assertNotEquals(
fn,
RegisteredLookupExtractionFn.create(
new RegisteredLookupExtractionFn(
manager,
LOOKUP_NAME,
false,
@ -187,7 +187,7 @@ public class RegisteredLookupExtractionFnTest
Assert.assertNotEquals(
fn,
RegisteredLookupExtractionFn.create(
new RegisteredLookupExtractionFn(
manager,
LOOKUP_NAME,
false,
@ -199,7 +199,7 @@ public class RegisteredLookupExtractionFnTest
Assert.assertNotEquals(
fn,
RegisteredLookupExtractionFn.create(
new RegisteredLookupExtractionFn(
manager,
LOOKUP_NAME,
false,
@ -212,7 +212,7 @@ public class RegisteredLookupExtractionFnTest
Assert.assertNotEquals(
fn,
RegisteredLookupExtractionFn.create(
new RegisteredLookupExtractionFn(
manager,
LOOKUP_NAME,
false,