docs: private void scheduleTasksCleanupForWorker(final String worker, final List<String> tasksToFail)
    {
      // This method is only called from the PathChildrenCache event handler, so this may look like a race,
      // but is actually not.
      cancelWorkerCleanup(worker);
  
      final ListenableScheduledFuture<?> cleanupTask = cleanupExec.schedule(
          new Runnable()
          {
            @Override
            public void run()
            {
              log.info("Running scheduled cleanup for Worker[%s]", worker);
              try {
                for (String assignedTask : tasksToFail) {
                  String taskPath = JOINER.join(indexerZkConfig.getTasksPath(), worker, assignedTask);
                  String statusPath = JOINER.join(indexerZkConfig.getStatusPath(), worker, assignedTask);
                  if (cf.checkExists().forPath(taskPath) != null) {
                    cf.delete().guaranteed().forPath(taskPath);
                  }
  
                  if (cf.checkExists().forPath(statusPath) != null) {
                    cf.delete().guaranteed().forPath(statusPath);
                  }
  
                  log.info("Failing task[%s]", assignedTask);
                  RemoteTaskRunnerWorkItem taskRunnerWorkItem = runningTasks.remove(assignedTask);
                  if (taskRunnerWorkItem != null) {
                    taskRunnerWorkItem.setResult(TaskStatus.failure(assignedTask));
                    TaskRunnerUtils.notifyStatusChanged(listeners, assignedTask, TaskStatus.failure(assignedTask));
                  } else {
                    log.warn("RemoteTaskRunner has no knowledge of task[%s]", assignedTask);
                  }
                }
  
                // worker is gone, remove worker task status announcements path.
                String workerStatusPath = JOINER.join(indexerZkConfig.getStatusPath(), worker);
                if (cf.checkExists().forPath(workerStatusPath) != null) {
                  cf.delete().guaranteed().forPath(JOINER.join(indexerZkConfig.getStatusPath(), worker));
                }
              }
              catch (Exception e) {
                log.makeAlert("Exception while cleaning up worker[%s]", worker).emit();
                throw new RuntimeException(e);
              }
            }
          },
          config.getTaskCleanupTimeout().toStandardDuration().getMillis(),
          TimeUnit.MILLISECONDS
      );
  
      removedWorkerCleanups.put(worker, cleanupTask);
  
      // Remove this entry from removedWorkerCleanups when done, if it's actually the one in there.
      Futures.addCallback(
          cleanupTask,
          new FutureCallback<Object>()
          {
            @Override
            public void onSuccess(Object result)
            {
              removedWorkerCleanups.remove(worker, cleanupTask);
            }
  
            @Override
            public void onFailure(Throwable t)
            {
              removedWorkerCleanups.remove(worker, cleanupTask);
            }
          }
      );
    }