Double closing a Transaction in a try-with-resource?

Hello I have the following WARN log:

WARN co.elastic.apm.agent.impl.transaction.AbstractSpan - End has already been called:...

And it's coming from this block of code:

final Transaction transaction = ElasticApm.startTransactionWithRemoteParent(httpHeaders);

try (final Scope scope = transaction.activate()) {
 // fun stuff here ...
  return Response.ok(Object);
} catch (final Exception e) {
  transaction.captureException(e);
  throw e;
} finally {
  transaction.end()
}

I removed the finally part of the code and the WARN log doesn't appear anymore. From what I understand, close() is called on the Scope object when the try block is done. And from the Elastic documentation, regarding the Transaction.activate(), method:

Makes this transaction the active transaction on the current thread until Scope.close() has been called.

Now my question is. Does declaring the Transaction object as final and then calling the .activate() method on it, qualifies it as a resource to be automatically closed by the try-with-resource code block? If this is not the case, then does is the Transaction ended when close() is called on Scope Object?

  • Using Java SDK 11

Thanks!

Hi and welcome to our forum :tada:

Declaring the transaction local variable as final means it cannot point to anything else after being initialized, but it doesn't say anything about the internal state of the Object it points to.
More importantly, the AutoCloseable resource in this try block is the Scope object, which is closed when the block is exited, and which, when being closed, does not end the underlying transaction. The Scope's closing only means that the related transaction is deactivated on the thread. You can potentially activate and close scopes of the same transaction on multiple threads or multiple times at the same thread.

So, removing the finally block is not the right way to fix your issue. It should be restored here and you need to find where else this transaction is being ended. Note that since you activate the transaction here, it is accessible across everything that happens on that thread during your fun stuff execution, so anything can get it and end it.
Maybe you are creating a transaction where it is not needed? What framework are you using here? Isn't it covered by our builtin instrumentation?

Hey, thanks for your reply!

  • I believe this might be a race condition? That same block of code appears multiple times in my class; one for each call: GET, PUT, DELETE. Maybe one call closes the transaction and then when the other tries to close it, that's when the WARN log is thrown?
  • The framework I am using here is JAX-RS.
  • I am not 100% sure of what the question Isn't it covered by our builtin instrumentation? is referring to.

Why are you manually instrumenting these methods? Did you try running your app without any manual instrumentation and the agent did not create transactions?