1

Summarize

Goal

I have an application that is written in Java using the Spring framework. There is a service that is being used as the handler for grabbing and releasing locks in the database (InnoDB). My goal is to be able to log the grabbing and releasing of the locks to create a lock history. For each lock interaction, I would like to know not only the name of the lock involved, but also where this request is coming from in the code (if possible, class name, method name, and line number).

My expected database entry will look something like this:

id lock_name clazz method line lock_date unlock_date unlock_type
0 tb_member MemberTools createMember 123 2021-12-23 10:16:00 2021-12-23 10:16:01 COMMIT
1 tb_member MemberTools editMember 234 2021-12-23 10:16:01 2021-12-23 10:16:02 COMMIT

I would like to know if there is an easy way to obtain this given that I am using the Spring framework.

Describe

So far, I have tried two things:

  1. Forcing the caller to pass a reference to itself or its current StackTraceElement (using Thread.currentThread().getStackTrace()[1]). This is not only extremely repetitive, but it also is prone to human error, as a developer might not realize that they need to pass in some reference to themselves.
  2. Inside of the lock service, use the getStackTrace method and walk through the elements to find the "correct" one. This is made very hard by Spring and the fact that before a call actually reaches the inside of a class with the @Service annotation, the call stack is muddled by numbers of calls between proxies and generated classes and such. Unless there is a deterministic way to find the number of calls in between the Service and the caller, then this doesn't seem like a good way either.

I have referenced this stack overflow question while working, but these do not take into account the usage of the Spring framework.

Show

A reproducible example will look something like this. First, the structure:

root\
    LockService.java
        getLock()
    MemberTools.java
        createMember()

LockService.java:

@Service
public class LockService {
  @Transactional
  public Lock getLock(String key) {
    Lock searchLock = new Lock();
    searchLock.setKey(key);
    lockMapper.getLock(searchLock);
    
    LockHistory lockHistory = new LockHistory();
    // Fill out lockHistory object...
    lockMapper.markAsLocked(lockHistory);
    attachTransactionCompletedListener(lockHistory);
  }

  private void attachTransactionCompletedListener(LockHistory lockHistory) {
    /* Attach a listener onto the current spring transaction so that we
     * can update the database entry when the transaction finishes and
     * the lock is released.
     */
  }
}

MemberTools.java:

public class MemberTools {
  @Autowired
  LockService lockService;
  
  @Transactional(propagation = Propagation.REQUIRES_NEW)
  public void createMember() {
    lockService.getLock("tb_member");
    /* Do create member stuff...
     * When this returns, the lock will be released
     * (either from COMMIT, ROLLBACK, or UNKNOWN Spring error)
     */
  }
}

By the time the getLock() method is reached, the stack trace is muddled with many calls that Spring inserts (proxies, reflections, etc.). Putting a breakpoint in this function and examining Thread.currentThread().getStackTrace() will show this.

notphilphil
  • 385
  • 5
  • 16

0 Answers0