getappmap / getappmap/appmap-java

Recordings show when control is transferred into a thread by an ExecutorService

Ouverte
#268 6 commentaires 0 réactions 1 personne assignée Voir sur GitHub

@apotterri y travaille déjà.

Depuis le 20/3/2024.

enhancement
Langage dominant
Java
Étoiles
88
Forks
17
Merge moyen
2 j 2 h
PR mergées (30 j)
1

Description

Currently, if an HTTP endpoint (e.g. a Spring Controller) uses an ExecutorService to run a task, none of that processing shows up in the AppMap. For example:

  @DeleteMapping("dolphins")
  public String dolphins() throws ExecutionException, InterruptedException {
    ExecutorService executorService = Executors.newCachedThreadPool();
    Future<Object> future1 = executorService.submit((Callable<Object>)() -> {
      DolphinChat dc = new DolphinChat();
      dc.say("So");
      dc.say("long,");
    });
    Future<Object> future2 = executorService.submit((Callable<Object>)() -> {
      DolphinChat dc = new DolphinChat();
      dc.say("and thanks for");
      dc.say("all the fish");
    });

    NextUp nu = new NextUp();
    nu.say("Mostly Harmless");

    return future1.get() + " " + future2.get();
  }

the calls to DolphinChat methods won't show up in the AppMap.

Instead, the (pseudo) events should look like this:

{http_server_request, thread: 1}
  {call, method: ExecutorService.submit, thread:1}
    {call, method: Runnable.run, thread: 1, task:true}
      {call, method: DolphinChat.say("So"), thread: 1}
      {return, parent: DolphinChat.say, thread: 1}
      {call, method: DolphinChat.say("long"), thread: 1}
      {return, parent: DolphinChat.say, thread: 1}
    {return, parent:Runnable.run, thread: 1}
  {return, parent: ExecutorService.submit, thread: 1} // when get is called on the Future returned by submit
  {call, method: ExecutorService.submit, thread:1}
    {call, method: Runnable.run, thread: 1, task:true}
      {call, method: DolphinChat.say("and thanks for"), thread: 1}
      {return, parent: DolphinChat.say, thread: 1}
      {call, method: DolphinChat.say("all the fish"), thread: 1}
      {return, parent: DolphinChat.say, thread: 1}
    {return, parent:Runnable.run, thread: 1}
  {return, parent: ExecutorService.submit, thread: 1} // when get is called on the Future returned by submit
  {call, method: NextUp.say("Mostly Harmless"), thread: 1}
  {return, parent: NextUp.say, thread: 1}
{http_server_response, thread: 1}

This says they all have the same thread id (that matches the thread that creates the ExecutorService). The ordering of the call and return events show that the calls to DolphinChat.say happened in a Runnable/Callable submitted to an ExecutorService. Note that the order of the complete Runnable.run call chains may not match the order in the code, of course, because they were run on separate threads.

Test cases:

  • end-to-end, for an endpoint like dolphins, generates an AppMap
  • checkpoint is disabled once ExecutorService.submit has been called
  • classes referenced only in a task show up in the classmap of the final AppMap
  • both a Callable and Runnable passed to ExecutorService.submit are handled correctly
  • process recording for a call to dolphins generates an AppMap
  • remote recording that spans a call to the dolphins endpoint generates an AppMap
  • task recording stops when Future.cancel is called. [What should happen to the task's recording?]
  • task recording stops when the method that called ExecutorService.submit returns. [What should happen if there are outstanding Futures?]

Guide de contribution

Ouvrir le guide de contribution

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Évaluation

Cette issue n'a pas encore été évaluée.

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.