/** * diqube: Distributed Query Base. * * Copyright (C) 2015 Bastian Gloeckle * * This file is part of diqube. * * diqube is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.diqube.queries; import java.util.UUID; import java.util.stream.Stream; import org.diqube.util.Pair; /** * Class that can provide the query UUID and execution UUID to any thread while executing a query. * * A query UUID is and ID identifying a global query across the whole diqube cluster - meaning the execution of one diql * string that was sent by the user. A query can have multiple executions, though - namely one for the query master and * then for each query remote another one. * * The query and execution UUID currently being active are managed using {@link ThreadLocal}s and the execution engine * (=all classes that are used during execution) rely on these fields being available. Therefore, when other threads are * used for computing some part of the execution pipelines (e.g. when using parallel {@link Stream}s), you need to make * sure to populate the state correctly to those threads (see {@link #getCurrentThreadState()}, * {@link #setCurrentThreadState(QueryUuidThreadState)} and {@link QueryUuidThreadState}). * * @author Bastian Gloeckle */ public class QueryUuid { private static final ThreadLocal<Pair<UUID, UUID>> queryUuidThreadLocal = new ThreadLocal<>(); /** * @param queryUuid * The query UUID for the current thread. * @param executionUuid * The execution UUID for the current thread. */ public static void setCurrentQueryUuidAndExecutionUuid(UUID queryUuid, UUID executionUuid) { queryUuidThreadLocal.set(new Pair<>(queryUuid, executionUuid)); } /** * Call this method to clear the resources taken up from a call to {@link #setCurrentQueryUuidAndExecutionUuid(UUID)}. */ public static void clearCurrent() { queryUuidThreadLocal.set(null); } /** * @return The query UUID of the current thread if available, else <code>null</code>. The returned UUID is that query * UUID that the current thread is supposed to work for. */ public static UUID getCurrentQueryUuid() { Pair<UUID, UUID> p = queryUuidThreadLocal.get(); if (p == null) return null; return p.getLeft(); } /** * @return The execution UUID of the current thread if available, else <code>null</code>. */ public static UUID getCurrentExecutionUuid() { Pair<UUID, UUID> p = queryUuidThreadLocal.get(); if (p == null) return null; return p.getRight(); } /** * @return The current state of QueryUuid, dumped in a immutable {@link QueryUuidThreadState} object that can be used * for {@link #setCurrentThreadState(QueryUuidThreadState)} (even multiple calls to the latter are possible). * @see class comment on {@link QueryUuid}. * @see QueryUuidThreadState */ public static QueryUuidThreadState getCurrentThreadState() { return new QueryUuidThreadState(getCurrentQueryUuid(), getCurrentExecutionUuid()); } /** * @param state * (Re-)set a specific state in the current thread. Be sure to call {@link #clearCurrent()} after calling * this method on a thread! * @see class comment on {@link QueryUuid}. * @see QueryUuidThreadState */ public static void setCurrentThreadState(QueryUuidThreadState state) { if (state.queryUuid == null && state.executionUuid == null) clearCurrent(); else setCurrentQueryUuidAndExecutionUuid(state.queryUuid, state.executionUuid); } /** * Immutable state that was collected from a Thread using {@link QueryUuid#getCurrentThreadState()}. * * This is useful when using parallel streams for example: We need to maintain the threadlocals used in * {@link QueryUuid} for all threads that are computing some part of an execution pipeline. If parallel {@link Stream} * s are used, these may use different threads than the one currently being executed. Therefore, before starting a new * parallel stream processing pipeline, one should fetch the current UUIDs using * {@link QueryUuid#getCurrentThreadState()}. Then, in each step in the pipeline that calls other classes that might * rely on the UUIDs being set, the {@link QueryUuid#setCurrentThreadState(QueryUuidThreadState)} should be called, to * prepare the ThreadLocals on that thread. After that computation method is completed, the thread state should be * cleared using {@link QueryUuid#clearCurrent()}. After the pipeline has completed, be sure to re-set the thread * state once using {@link QueryUuid#setCurrentThreadState(QueryUuidThreadState)}, as the pipeline might have computed * some parts in the current thread (= it did not only use the threads in a ThreadPool to compute the pipeline, but * the current thread, too) - and then {@link QueryUuid#clearCurrent()} will have been called on that thread. * * Example: * * <pre> * QueryUuidThreadState uuidState = QueryUuid.getCurrentThreadState(); * try { * Stream.parallel().map(a -> { * QueryUuid.setCurrentThreadState(uuidState); * try { * // do your mapping stuff * finally { * QueryUuid.clearCurrent(); * } * };).findAny(); * } finally { * QueryUuid.setCurrentThreadState(uuidState); * } * * </pre> */ public static class QueryUuidThreadState { private UUID queryUuid; private UUID executionUuid; private QueryUuidThreadState(UUID queryUuid, UUID executionUuid) { this.queryUuid = queryUuid; this.executionUuid = executionUuid; } } }