Предупреждения Арджуны в журналах (активно несколько потоков) - PullRequest
1 голос
/ 07 августа 2020

У меня есть следующий сервис, который вызывается с помощью планировщика quarkus с использованием аннотации @Scheduled.

Основная идея c состоит в том, что он перечисляет ожидающие задачи, а затем использует ExecutorService для запускать отдельные задачи.

Я получаю следующие журналы предупреждений:

[co.ar.at.arjuna] checkChildren ARJUNA012094: Commit of action id 0:ffffc0a80e70:8b9b:5f2d31f1:5b invoked while multiple threads active within it.
WARN  [co.ar.at.arjuna] check ARJUNA012107: CheckedAction::check - atomic action 0:ffffc0a80e70:8b9b:5f2d31f1:5b commiting with 2 threads active!  

Какова причина этого и что я могу сделать, чтобы это исправить? Моя основная цель - как можно скорее выйти из потока Планировщика и позволить обновлению задачи происходить в фоновом режиме

@RequestScoped
    @Transactional(REQUIRED)
    public class TaskService {
     @Inject
        ManagedExecutor executor;
    
    public void runPendingTasks() {
            final List<Task> taskList = Task.list("pending=?1 ",true);
            logger.debug("Found  " + taskList.size() + "  tasks pending ");
            for (final Task task : taskList) {
                executor.execute(() -> {
                    final boolean status = doTask(task);
                });
            }
        }
    
    @Transactional(REQUIRED)
        private boolean doTask(final Task task) {
            logger.debug("Going to run task " + task.getId() );
    //Do some DB updation here
            return true;
        }

}

1 Ответ

2 голосов
/ 11 августа 2020

Когда вы аннотируете класс как @Transactional, как в вашем примере, вы определяете в каждом методе класса границу транзакции.

В вашем примере аннотация будет применяться к runPendingTasks method.

Вы не можете разместить аннотацию @Transactional в частных методах (связанных с тем, что предлагает @crizzis), это не влияет на них.

Вся эта информация объясняет, почему появляются предупреждения в журналах: у вас есть одна транзакция, инициированная runPendingTasks, и в ней несколько потоков, обрабатывающих метод doTask. Вызов метода runPendingTasks заканчивается, но эти потоки все еще живы.

Один из подходов к решению проблемы может заключаться в завершении sh всех задач, связанных с базой данных, в границах транзакции.

Здесь , в Stackoverflow, у вас есть поток, который показывает вам, как вы можете завершить sh список задач асинхронно.

Я рекомендую вам прочитать некоторые статьи из блога Томаша Нуркевича , он даст вам - по крайней мере, для меня - отличное понимание асинхронных вычислений и Java параллелизма в целом.

Следуя его совету , вы можете определить свой код следующим образом:

@RequestScoped
@Transactional(REQUIRED)
public class TaskService {
  
  @Inject
  ManagedExecutor executor;
    
  public void runPendingTasks() {
    final List<Task> taskList = Task.list("pending=?1 ",true);
    logger.debug("Found  " + taskList.size() + "  tasks pending ");

    final List<CompletableFuture<boolean>> futures = taskList.stream().
      map(task -> CompletableFuture.supplyAsync(() -> doTask(task), executor)).
      collect(Collectors.<CompletableFuture<boolean>>toList())
    ;

    final CompletableFuture<Void> allDoneFuture =
      CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));
    
    final CompletableFuture<List<boolean>> allDone = allDoneFuture.thenApply(v ->
      futures.stream().
        map(future -> future.join()).
        collect(Collectors.<boolean>toList())
    );

    final boolean result = allDone.thenAccept(results ->
      results.stream().
        reduce((a, b) -> a || b)
    );
  }
    
  private boolean doTask(final Task task) {
    logger.debug("Going to run task " + task.getId() );
    //Do some DB updation here
    return true;
  }

}
...