-
Notifications
You must be signed in to change notification settings - Fork 860
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Make it possible to use InstrumentationContext (now VirtualField) fro… #4218
Make it possible to use InstrumentationContext (now VirtualField) fro… #4218
Conversation
* | ||
* @return The old field value if it was present, or the passed {@code fieldValue}. | ||
*/ | ||
public abstract F setIfAbsentAndGet(T object, F fieldValue); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps call it just setIfAbsent
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted this method to use a similar wording to AtomicReference
/VarHandle
methods, even if it's a bit explicit.
public abstract void set(T object, @Nullable F fieldValue); | ||
|
||
/** | ||
* Sets the new value of this virtual field if the current value is {@code null} or absent. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would remove the or absent part as we really only check whether the field is null
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point.
Now I'm starting to wonder whether the method should be named setIfNullAndGet
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like setIfNull
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i was thinking setIfNull
without the AndGet
, similar to Map.putIfAbsent
, but now i realize the potential confusion because putIfAbsent
returns the previous value or null
if there was none, while this method always returns the stored value.
maybe:
void setIfNull(T object, F fieldValue)
and
F computeIfNull(T object, Supplier<F> fieldValueSupplier)
as it looks like we don't (currently at least) use the return value from the first method, though now I'm wondering if that method is even needed, i.e. will anything break if those callers just use set
(and if the "set if null" behavior really does matter, they could call get
first, as there's nothing atomic anyways if they aren't using the return value)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
though now I'm wondering if that method is even needed, i.e. will anything break if those callers just use
set
(and if the "set if null" behavior really does matter, they could callget
first, as there's nothing atomic anyways if they aren't using the return value)
Most of the setIfNull
usages look like this method is used instead of plain set
"just in case something happens"; even though there seems to be no real chance of the field being non-null. I can take a look at them and maybe remove the method in a follow-up PR.
/** | ||
* Represents a "virtual" field of type {@code F} that is added to type {@code T} in the runtime. | ||
* | ||
* <p>Field values are weakly referenced and will be garbage collected when their owner instance is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My understanding is that field values are not weakly referenced, if they were they could be collected if nobody else has a reference to them.
If I understand this correctly the happy path is that we transform the class and add a field to it so we really have a regular strong reference to the value. Things get messy when class is not transformed for some reason. Then we put the values into a weak map where key is the object on which field is set. This has the annoying property that when value references key then we immediately have a memory leak because now key can be never collected. We could get around this by also making values weak which would mean that something else needs to keep the values alive. Basically afaik no matter what we do we can't get the same behaviour as when the value is just stored in a field.
Perhaps we could address this somehow. Maybe we could drop the support for using fallback map? Now that lambda classes are also transformed is there a case where it could be needed? There is a flag to disable field injection and there is hack that skips filed injection on classes annotated with javax.decorator.Decorator
so might not be that easy to completely get rid of it. An alternative would be to have something in VirtualField
api that lets user figure out whether it is field or map based. Or could add methods like setWeak
that doesn't change anything in field backed implementation but for map based implementation makes the value also weak. This would allow using VirtualField
in netty FutureListenerWrappers
without leaking memory even when map based store is used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My understanding is that field values are not weakly referenced, if they were they could be collected if nobody else has a reference to them.
Yeah that's true, I copied this Javadoc and didn't really think too deeply about it.
Basically afaik no matter what we do we can't get the same behaviour as when the value is just stored in a field.
Perhaps we could address this somehow. Maybe we could drop the support for using fallback map?
We probably have to keep the fallback implementation; I suppose it's better to have it in place and install a few more instrumentations than not to have it and skip instrumenting some pieces of code.
Perhaps we could fix the Javadoc so that it outright says that this has similar semantics to a weak-keys strong-values map and keeping values that might reference the original key is discouraged?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we could drop the support for using fallback map?
that would be nice, the memory leak has bit us before (at least #441 and #3027 I think)
Now that lambda classes are also transformed is there a case where it could be needed? There is a flag to disable field injection and there is hack that skips filed injection on classes annotated with javax.decorator.Decorator so might not be that easy to completely get rid of it
I wonder if the javax.decorator.Decorator
is needed since #3948
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the original DD PR that introduced this mathcer: DataDog/dd-trace-java#1045
In CDI (Contexts and Dependency Injection) environments, classes annotated with
@Decorator
wrap other classes.
Decorators implement the same interfaces but delegate to another class. See the Oracle docs.In general, the tracer should instrument the delegate (decorated class) and not the decorator. When the tracer instruments the decorator, it cause the CDI container to be confused about the interfaces of the decorator and crash on startup.
If we can reproduce that problem and verify that #3948 solves it then we can start thinking of phasing out the weak map implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
opened #4241 to track/discuss further
05fc121
to
f298609
Compare
ContextStore<Runnable, Context> contextStore = | ||
InstrumentationContext.get(Runnable.class, Context.class); | ||
VirtualField<Runnable, Context> virtualField = | ||
VirtualField.get(Runnable.class, Context.class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ❤️ reducing ContextStore
and InstrumentationContext
down to just one concept VirtualField
(and that doesn't have the word Context in it)
...st/groovy/io/opentelemetry/javaagent/tooling/muzzle/InstrumentationClassPredicateTest.groovy
Outdated
Show resolved
Hide resolved
public static VirtualField<Connection, DbInfo> connectionInfo = | ||
VirtualField.find(Connection.class, DbInfo.class); | ||
public static VirtualField<PreparedStatement, String> preparedStatement = | ||
VirtualField.find(PreparedStatement.class, String.class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💯
/** | ||
* Represents a "virtual" field of type {@code F} that is added to type {@code T} in the runtime. | ||
* | ||
* <p>Field values are weakly referenced and will be garbage collected when their owner instance is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we could drop the support for using fallback map?
that would be nice, the memory leak has bit us before (at least #441 and #3027 I think)
Now that lambda classes are also transformed is there a case where it could be needed? There is a flag to disable field injection and there is hack that skips filed injection on classes annotated with javax.decorator.Decorator so might not be that easy to completely get rid of it
I wonder if the javax.decorator.Decorator
is needed since #3948
f298609
to
fd7e830
Compare
…m library instrumentation
Resolves #4081
ContextStore
toVirtualField
in public APIsInstrumentationContext#get()
toVirtualField#find()
VirtualField
tointstrumentation-api
There's still several mentions of
ContextStore
/InstrumentationContext
remaining, but they're all located somewhere in the agent internals (or muzzle). I'll rename them in a follow-up PR (once muzzleInstrumenterConfigBuilder
PR gets merged)