/** * Submits a batch of cluster state update tasks; submitted updates are guaranteed to be processed * together, potentially with more tasks of the same executor. * * @param source the source of the cluster state update task * @param tasks a map of update tasks and their corresponding listeners * @param config the cluster state update task configuration * @param executor the cluster state update task executor; tasks that share the same executor will * be executed batches on this executor * @param <T> the type of the cluster state update task state */ public <T> void submitStateUpdateTasks( final String source, final Map<T, ClusterStateTaskListener> tasks, final ClusterStateTaskConfig config, final ClusterStateTaskExecutor<T> executor) { if (!lifecycle.started()) { return; } if (tasks.isEmpty()) { return; } try { // convert to an identity map to check for dups based on update tasks semantics of using // identity instead of equal final IdentityHashMap<T, ClusterStateTaskListener> tasksIdentity = new IdentityHashMap<>(tasks); final List<UpdateTask<T>> updateTasks = tasksIdentity .entrySet() .stream() .map( entry -> new UpdateTask<>( source, entry.getKey(), config, executor, safe(entry.getValue(), logger))) .collect(Collectors.toList()); synchronized (updateTasksPerExecutor) { List<UpdateTask> existingTasks = updateTasksPerExecutor.computeIfAbsent(executor, k -> new ArrayList<>()); for (@SuppressWarnings("unchecked") UpdateTask<T> existing : existingTasks) { if (tasksIdentity.containsKey(existing.task)) { throw new IllegalStateException( "task [" + executor.describeTasks(Collections.singletonList(existing.task)) + "] with source [" + source + "] is already queued"); } } existingTasks.addAll(updateTasks); } final UpdateTask<T> firstTask = updateTasks.get(0); if (config.timeout() != null) { updateTasksExecutor.execute( firstTask, threadPool.scheduler(), config.timeout(), () -> threadPool .generic() .execute( () -> { for (UpdateTask<T> task : updateTasks) { if (task.processed.getAndSet(true) == false) { logger.debug( "cluster state update task [{}] timed out after [{}]", source, config.timeout()); task.listener.onFailure( source, new ProcessClusterEventTimeoutException( config.timeout(), source)); } } })); } else { updateTasksExecutor.execute(firstTask); } } catch (EsRejectedExecutionException e) { // ignore cases where we are shutting down..., there is really nothing interesting // to be done here... if (!lifecycle.stoppedOrClosed()) { throw e; } } }