Make netty 4.0 attributes definition compatible with Apache Atlas 1.1.0

The issue we are trying to fix here seems to be a result of two concurrent issues.

Cassandra driver's used by atlas 1.1.0 brings in a old version of netty < 4.0.23.

In version 4.0.25 netty raised exception in `UniqueName` class for duplicate name

```
if (map.putIfAbsent(name, Boolean.TRUE) != null) {
            throw new IllegalArgumentException(String.format("'%s' is already in use", name));
        }
```

This changed in 4.0.26 where exception was not thrown, instead the duplicate key was just ignored.

```
    protected UniqueName(String name) {
        this.name = checkNotNull(name, "name");
        id = nextId.incrementAndGet();
    }
```

Now this alone would not be enough. The only way I was able to reproduce is if two different class loaders tries to load the
same class `AttributeKeys` twice.
I tried to look into Atlas source code (which is huge) and I could not find a clear path of how this would be possible.

Further more, looking at some build scripts that a user experiencing this issue provided,
it looks like they are doing everything correctly and there is nothing they should change.

In conclusion, I believe that even if this is not a very clean approach,
we should be resilient to this use case, when a customer using netty <4.0.25 in an
older version of Apache Atlas (or with some sort of class loader behavior).
This commit is contained in:
Luca Abbati 2019-07-05 14:29:42 +02:00
parent 3884cc088e
commit fac3a93445
No known key found for this signature in database
GPG Key ID: 74DBB952D9BA17F2
2 changed files with 45 additions and 3 deletions

View File

@ -7,13 +7,42 @@ import io.netty.util.AttributeKey;
import io.opentracing.Span; import io.opentracing.Span;
public class AttributeKeys { public class AttributeKeys {
public static final AttributeKey<TraceScope.Continuation> public static final AttributeKey<TraceScope.Continuation>
PARENT_CONNECT_CONTINUATION_ATTRIBUTE_KEY = PARENT_CONNECT_CONTINUATION_ATTRIBUTE_KEY =
new AttributeKey<>("datadog.trace.instrumentation.netty40.parent.connect.continuation"); new AttributeKey<>(
buildContextSpecificKey(
"datadog.trace.instrumentation.netty40.parent.connect.continuation"));
public static final AttributeKey<Span> SERVER_ATTRIBUTE_KEY = public static final AttributeKey<Span> SERVER_ATTRIBUTE_KEY =
new AttributeKey<>(HttpServerTracingHandler.class.getName() + ".span"); new AttributeKey<>(
buildContextSpecificKey(HttpServerTracingHandler.class.getName() + ".span"));
public static final AttributeKey<Span> CLIENT_ATTRIBUTE_KEY = public static final AttributeKey<Span> CLIENT_ATTRIBUTE_KEY =
new AttributeKey<>(HttpClientTracingHandler.class.getName() + ".span"); new AttributeKey<>(
buildContextSpecificKey(HttpClientTracingHandler.class.getName() + ".span"));
/**
* Netty 4.0 before 4.0.26 handled differently how unique attributes where handled, with 4.0.26+
* being more lenient with duplicates. We found a use case in Apache Atlas 1.1.0 where for some
* reason, this class gets loaded by multiple class loaders generating an error in 4.0.25- before
* an exception was thrown if that attribute was already defined.
*
* @param simpleKey The logical key assigned.
* @return A key scoped to the current class loader in use, if not null.
*/
private static String buildContextSpecificKey(final String simpleKey) {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
String key = simpleKey;
if (contextClassLoader != null) {
key =
"ClassLoader."
+ contextClassLoader.getClass().getName()
+ "."
+ contextClassLoader.hashCode()
+ "."
+ key;
}
return key;
}
} }

View File

@ -0,0 +1,13 @@
import datadog.trace.instrumentation.netty40.AttributeKeys
import spock.lang.Specification
class ApacheAtlas1_1_0CompatibilityTest extends Specification {
def "Netty 4.0 Attributes can be loaded by multiple class loaders in different threads as in Apache Atlas 1.1.0"() {
expect:
AttributeKeys.PARENT_CONNECT_CONTINUATION_ATTRIBUTE_KEY.name().matches(/ClassLoader\..*.datadog\.trace\.instrumentation\.netty40\.parent\.connect\.continuation/)
AttributeKeys.SERVER_ATTRIBUTE_KEY.name().matches(/ClassLoader\..*\.datadog\.trace\.instrumentation\.netty40\.server\.HttpServerTracingHandler\.span/)
AttributeKeys.CLIENT_ATTRIBUTE_KEY.name().matches(/ClassLoader\..*\.datadog\.trace\.instrumentation\.netty40\.client\.HttpClientTracingHandler\.span/)
}
}