| 38 | } |
| 39 | |
| 40 | StatelessTaskExecutor::Result StatelessTaskExecutor::startTask(const String & unique_task_id, const DistributedQueryTaskDescription & task_description, const String & unique_temp_file_path) |
| 41 | { |
| 42 | /// `unique_task_id` is unique per task, so a repeated start (e.g. a coordinator retry) is the same |
| 43 | /// task. Running it twice would double-write exchanges and temp files and orphan the original from |
| 44 | /// cancel/forget, so treat a duplicate start as a no-op. |
| 45 | { |
| 46 | std::lock_guard lock(tasks_mutex); |
| 47 | if (tasks.contains(unique_task_id)) |
| 48 | { |
| 49 | LOG_WARNING(getLogger("StatelessTaskExecutor"), "Ignoring duplicate start for already running task {}", unique_task_id); |
| 50 | return Result::Ok; |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | ContextPtr global_context = Context::getGlobalContextInstance(); |
| 55 | ContextMutablePtr query_context = Context::createCopy(global_context); |
| 56 | query_context->makeQueryContext(); |
| 57 | { |
| 58 | ClientInfo client_info; |
| 59 | client_info.current_query_id = unique_task_id; |
| 60 | client_info.query_kind = ClientInfo::QueryKind::SECONDARY_QUERY; |
| 61 | client_info.initial_query_id = task_description.initial_query_id; |
| 62 | query_context->setClientInfo(client_info); |
| 63 | } |
| 64 | |
| 65 | /// Apply the initiator's settings so the worker honors query limits and execution-affecting |
| 66 | /// settings. Limits (e.g. max_rows_to_read, max_rows_in_join) are enforced per task, so each |
| 67 | /// fragment stays under the limit but the whole query may exceed it by up to the bucket count. |
| 68 | /// Force make_distributed_plan off: the worker runs an already-split local fragment. |
| 69 | query_context->applySettingsChanges(task_description.settings_changes); |
| 70 | query_context->setSetting("make_distributed_plan", false); |
| 71 | |
| 72 | auto [object_storage, object_storage_path] = getObjectStorageForTemporaryFiles(unique_temp_file_path, query_context); |
| 73 | |
| 74 | std::shared_ptr<std::promise<String>> task_promise = std::make_shared<std::promise<String>>(); |
| 75 | auto task_state = std::make_shared<TaskState>(); |
| 76 | task_state->completion_future = task_promise->get_future(); |
| 77 | |
| 78 | { |
| 79 | std::lock_guard lock(tasks_mutex); |
| 80 | /// If two starts of the same id race, keep the first; overwriting would orphan the running task. |
| 81 | if (!tasks.try_emplace(unique_task_id, task_state).second) |
| 82 | { |
| 83 | LOG_WARNING(getLogger("StatelessTaskExecutor"), "Ignoring duplicate start for already running task {}", unique_task_id); |
| 84 | return Result::Ok; |
| 85 | } |
| 86 | } |
| 87 | |
| 88 | /// Callback for periodic cancellation check |
| 89 | auto is_task_cancelled = [cancelled = task_state->cancelled]() -> bool |
| 90 | { |
| 91 | return *cancelled; |
| 92 | }; |
| 93 | |
| 94 | auto update_progress = [task_progress = task_state->progress](const Progress & progress) |
| 95 | { |
| 96 | task_progress->incrementPiecewiseAtomically(progress); |
| 97 | }; |
no test coverage detected