diff --git a/core/trino-main/pom.xml b/core/trino-main/pom.xml index 679423aa85af..efe3e478b219 100644 --- a/core/trino-main/pom.xml +++ b/core/trino-main/pom.xml @@ -191,6 +191,11 @@ trace-token + + io.airlift + tracing + + io.airlift units @@ -291,6 +296,16 @@ jjwt-jackson + + io.opentelemetry + opentelemetry-api + + + + io.opentelemetry + opentelemetry-context + + it.unimi.dsi fastutil @@ -368,6 +383,12 @@ + + com.squareup.okhttp3 + okhttp + runtime + + net.java.dev.jna jna-platform @@ -449,12 +470,6 @@ test - - com.squareup.okhttp3 - okhttp - test - - com.squareup.okhttp3 okhttp-urlconnection diff --git a/core/trino-main/src/main/java/io/trino/Session.java b/core/trino-main/src/main/java/io/trino/Session.java index 0fd1b26ce096..6dc3db14ea38 100644 --- a/core/trino-main/src/main/java/io/trino/Session.java +++ b/core/trino-main/src/main/java/io/trino/Session.java @@ -21,6 +21,7 @@ import io.airlift.slice.Slice; import io.airlift.units.DataSize; import io.airlift.units.Duration; +import io.opentelemetry.api.trace.Span; import io.trino.client.ProtocolHeaders; import io.trino.metadata.SessionPropertyManager; import io.trino.security.AccessControl; @@ -60,6 +61,7 @@ public final class Session { private final QueryId queryId; + private final Span querySpan; private final Optional transactionId; private final boolean clientTransactionSupport; private final Identity identity; @@ -87,6 +89,7 @@ public final class Session public Session( QueryId queryId, + Span querySpan, Optional transactionId, boolean clientTransactionSupport, Identity identity, @@ -112,6 +115,7 @@ public Session( Optional exchangeEncryptionKey) { this.queryId = requireNonNull(queryId, "queryId is null"); + this.querySpan = requireNonNull(querySpan, "querySpan is null"); this.transactionId = requireNonNull(transactionId, "transactionId is null"); this.clientTransactionSupport = clientTransactionSupport; this.identity = requireNonNull(identity, "identity is null"); @@ -150,6 +154,11 @@ public QueryId getQueryId() return queryId; } + public Span getQuerySpan() + { + return querySpan; + } + public String getUser() { return identity.getUser(); @@ -332,6 +341,7 @@ public Session beginTransactionId(TransactionId transactionId, TransactionManage return new Session( queryId, + querySpan, Optional.of(transactionId), clientTransactionSupport, Identity.from(identity) @@ -381,6 +391,7 @@ public Session withDefaultProperties(Map systemPropertyDefaults, return new Session( queryId, + querySpan, transactionId, clientTransactionSupport, identity, @@ -411,6 +422,7 @@ public Session withExchangeEncryption(Slice encryptionKey) checkState(exchangeEncryptionKey.isEmpty(), "exchangeEncryptionKey is already present"); return new Session( queryId, + querySpan, transactionId, clientTransactionSupport, identity, @@ -459,6 +471,7 @@ public SessionRepresentation toSessionRepresentation() { return new SessionRepresentation( queryId.toString(), + querySpan, transactionId, clientTransactionSupport, identity.getUser(), @@ -491,6 +504,7 @@ public String toString() { return toStringHelper(this) .add("queryId", queryId) + .add("querySpan", querySpanString().orElse(null)) .add("transactionId", transactionId) .add("user", getUser()) .add("principal", getIdentity().getPrincipal().orElse(null)) @@ -512,6 +526,16 @@ public String toString() .toString(); } + private Optional querySpanString() + { + return Optional.of(querySpan) + .filter(span -> span.getSpanContext().isValid()) + .map(span -> toStringHelper("Span") + .add("spanId", span.getSpanContext().getSpanId()) + .add("traceId", span.getSpanContext().getTraceId()) + .toString()); + } + private void validateCatalogProperties( Optional transactionId, AccessControl accessControl, @@ -560,6 +584,7 @@ public SecurityContext toSecurityContext() public static class SessionBuilder { private QueryId queryId; + private Span querySpan = Span.getInvalid(); private TransactionId transactionId; private boolean clientTransactionSupport; private Identity identity; @@ -624,6 +649,13 @@ public SessionBuilder setQueryId(QueryId queryId) return this; } + @CanIgnoreReturnValue + public SessionBuilder setQuerySpan(Span querySpan) + { + this.querySpan = requireNonNull(querySpan, "querySpan is null"); + return this; + } + @CanIgnoreReturnValue public SessionBuilder setTransactionId(TransactionId transactionId) { @@ -853,6 +885,7 @@ public Session build() { return new Session( queryId, + querySpan, Optional.ofNullable(transactionId), clientTransactionSupport, identity, diff --git a/core/trino-main/src/main/java/io/trino/SessionRepresentation.java b/core/trino-main/src/main/java/io/trino/SessionRepresentation.java index b0e2dbd93b2e..dd95c26f3e57 100644 --- a/core/trino-main/src/main/java/io/trino/SessionRepresentation.java +++ b/core/trino-main/src/main/java/io/trino/SessionRepresentation.java @@ -18,6 +18,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import io.airlift.slice.Slice; +import io.opentelemetry.api.trace.Span; import io.trino.metadata.SessionPropertyManager; import io.trino.spi.QueryId; import io.trino.spi.security.BasicPrincipal; @@ -42,6 +43,7 @@ public final class SessionRepresentation { private final String queryId; + private final Span querySpan; private final Optional transactionId; private final boolean clientTransactionSupport; private final String user; @@ -71,6 +73,7 @@ public final class SessionRepresentation @JsonCreator public SessionRepresentation( @JsonProperty("queryId") String queryId, + @JsonProperty("querySpan") Span querySpan, @JsonProperty("transactionId") Optional transactionId, @JsonProperty("clientTransactionSupport") boolean clientTransactionSupport, @JsonProperty("user") String user, @@ -98,6 +101,7 @@ public SessionRepresentation( @JsonProperty("protocolName") String protocolName) { this.queryId = requireNonNull(queryId, "queryId is null"); + this.querySpan = requireNonNull(querySpan, "querySpan is null"); this.transactionId = requireNonNull(transactionId, "transactionId is null"); this.clientTransactionSupport = clientTransactionSupport; this.user = requireNonNull(user, "user is null"); @@ -136,6 +140,12 @@ public String getQueryId() return queryId; } + @JsonProperty + public Span getQuerySpan() + { + return querySpan; + } + @JsonProperty public Optional getTransactionId() { @@ -317,6 +327,7 @@ public Session toSession(SessionPropertyManager sessionPropertyManager, Map duplicatePluginClassLoaderFactory) { + this.openTelemetry = requireNonNull(openTelemetry, "openTelemetry is null"); + this.tracer = requireNonNull(tracer, "tracer is null"); this.nodeManager = requireNonNull(nodeManager, "nodeManager is null"); this.versionEmbedder = requireNonNull(versionEmbedder, "versionEmbedder is null"); this.typeManager = requireNonNull(typeManager, "typeManager is null"); @@ -61,6 +69,18 @@ public ConnectorContextInstance( this.catalogHandle = requireNonNull(catalogHandle, "catalogHandle is null"); } + @Override + public OpenTelemetry getOpenTelemetry() + { + return openTelemetry; + } + + @Override + public Tracer getTracer() + { + return tracer; + } + @Override public CatalogHandle getCatalogHandle() { diff --git a/core/trino-main/src/main/java/io/trino/connector/ConnectorServices.java b/core/trino-main/src/main/java/io/trino/connector/ConnectorServices.java index 81d6de16ddae..aa88537c502d 100644 --- a/core/trino-main/src/main/java/io/trino/connector/ConnectorServices.java +++ b/core/trino-main/src/main/java/io/trino/connector/ConnectorServices.java @@ -17,6 +17,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import io.airlift.log.Logger; +import io.opentelemetry.api.trace.Tracer; import io.trino.metadata.CatalogMetadata.SecurityManagement; import io.trino.metadata.CatalogProcedures; import io.trino.metadata.CatalogTableFunctions; @@ -62,6 +63,7 @@ public class ConnectorServices { private static final Logger log = Logger.get(ConnectorServices.class); + private final Tracer tracer; private final CatalogHandle catalogHandle; private final Connector connector; private final Runnable afterShutdown; @@ -87,8 +89,9 @@ public class ConnectorServices private final AtomicBoolean shutdown = new AtomicBoolean(); - public ConnectorServices(CatalogHandle catalogHandle, Connector connector, Runnable afterShutdown) + public ConnectorServices(Tracer tracer, CatalogHandle catalogHandle, Connector connector, Runnable afterShutdown) { + this.tracer = requireNonNull(tracer, "tracer is null"); this.catalogHandle = requireNonNull(catalogHandle, "catalogHandle is null"); this.connector = requireNonNull(connector, "connector is null"); this.afterShutdown = requireNonNull(afterShutdown, "afterShutdown is null"); @@ -207,6 +210,11 @@ public ConnectorServices(CatalogHandle catalogHandle, Connector connector, Runna this.capabilities = capabilities; } + public Tracer getTracer() + { + return tracer; + } + public CatalogHandle getCatalogHandle() { return catalogHandle; diff --git a/core/trino-main/src/main/java/io/trino/connector/DefaultCatalogFactory.java b/core/trino-main/src/main/java/io/trino/connector/DefaultCatalogFactory.java index b127c7efb204..6048b3e1dab6 100644 --- a/core/trino-main/src/main/java/io/trino/connector/DefaultCatalogFactory.java +++ b/core/trino-main/src/main/java/io/trino/connector/DefaultCatalogFactory.java @@ -14,6 +14,8 @@ package io.trino.connector; import io.airlift.node.NodeInfo; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.Tracer; import io.trino.connector.informationschema.InformationSchemaConnector; import io.trino.connector.system.CoordinatorSystemTablesProvider; import io.trino.connector.system.StaticSystemTablesProvider; @@ -66,6 +68,7 @@ public class DefaultCatalogFactory private final PageIndexerFactory pageIndexerFactory; private final NodeInfo nodeInfo; private final VersionEmbedder versionEmbedder; + private final OpenTelemetry openTelemetry; private final TransactionManager transactionManager; private final TypeManager typeManager; @@ -83,6 +86,7 @@ public DefaultCatalogFactory( PageIndexerFactory pageIndexerFactory, NodeInfo nodeInfo, VersionEmbedder versionEmbedder, + OpenTelemetry openTelemetry, TransactionManager transactionManager, TypeManager typeManager, NodeSchedulerConfig nodeSchedulerConfig) @@ -95,6 +99,7 @@ public DefaultCatalogFactory( this.pageIndexerFactory = requireNonNull(pageIndexerFactory, "pageIndexerFactory is null"); this.nodeInfo = requireNonNull(nodeInfo, "nodeInfo is null"); this.versionEmbedder = requireNonNull(versionEmbedder, "versionEmbedder is null"); + this.openTelemetry = requireNonNull(openTelemetry, "openTelemetry is null"); this.transactionManager = requireNonNull(transactionManager, "transactionManager is null"); this.typeManager = requireNonNull(typeManager, "typeManager is null"); this.schedulerIncludeCoordinator = nodeSchedulerConfig.isIncludeCoordinator(); @@ -149,12 +154,16 @@ public CatalogConnector createCatalog(CatalogHandle catalogHandle, ConnectorName private CatalogConnector createCatalog(CatalogHandle catalogHandle, ConnectorName connectorName, Connector connector, Runnable destroy, Optional catalogProperties) { + Tracer tracer = createTracer(catalogHandle); + ConnectorServices catalogConnector = new ConnectorServices( + tracer, catalogHandle, connector, destroy); ConnectorServices informationSchemaConnector = new ConnectorServices( + tracer, createInformationSchemaCatalogHandle(catalogHandle), new InformationSchemaConnector(catalogHandle.getCatalogName(), nodeManager, metadata, accessControl), () -> {}); @@ -172,6 +181,7 @@ private CatalogConnector createCatalog(CatalogHandle catalogHandle, ConnectorNam } ConnectorServices systemConnector = new ConnectorServices( + tracer, createSystemTablesCatalogHandle(catalogHandle), new SystemConnector( nodeManager, @@ -197,6 +207,8 @@ private Connector createConnector( { ConnectorContext context = new ConnectorContextInstance( catalogHandle, + openTelemetry, + createTracer(catalogHandle), new ConnectorAwareNodeManager(nodeManager, nodeInfo.getEnvironment(), catalogHandle, schedulerIncludeCoordinator), versionEmbedder, typeManager, @@ -210,6 +222,11 @@ private Connector createConnector( } } + private Tracer createTracer(CatalogHandle catalogHandle) + { + return openTelemetry.getTracer("trino.catalog." + catalogHandle.getCatalogName()); + } + private static class InternalConnectorFactory { private final ConnectorFactory connectorFactory; diff --git a/core/trino-main/src/main/java/io/trino/dispatcher/DispatchManager.java b/core/trino-main/src/main/java/io/trino/dispatcher/DispatchManager.java index 19148442fbe0..8374b3007bc2 100644 --- a/core/trino-main/src/main/java/io/trino/dispatcher/DispatchManager.java +++ b/core/trino-main/src/main/java/io/trino/dispatcher/DispatchManager.java @@ -16,6 +16,10 @@ import com.google.common.util.concurrent.AbstractFuture; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; import io.trino.Session; import io.trino.execution.QueryIdGenerator; import io.trino.execution.QueryInfo; @@ -52,6 +56,7 @@ import static io.trino.execution.QueryState.QUEUED; import static io.trino.execution.QueryState.RUNNING; import static io.trino.spi.StandardErrorCode.QUERY_TEXT_TOO_LARGE; +import static io.trino.tracing.ScopedSpan.scopedSpan; import static io.trino.util.StatementUtils.getQueryType; import static java.lang.String.format; import static java.util.Objects.requireNonNull; @@ -67,6 +72,7 @@ public class DispatchManager private final SessionSupplier sessionSupplier; private final SessionPropertyDefaults sessionPropertyDefaults; private final SessionPropertyManager sessionPropertyManager; + private final Tracer tracer; private final int maxQueryLength; @@ -87,6 +93,7 @@ public DispatchManager( SessionSupplier sessionSupplier, SessionPropertyDefaults sessionPropertyDefaults, SessionPropertyManager sessionPropertyManager, + Tracer tracer, QueryManagerConfig queryManagerConfig, DispatchExecutor dispatchExecutor) { @@ -99,6 +106,7 @@ public DispatchManager( this.sessionSupplier = requireNonNull(sessionSupplier, "sessionSupplier is null"); this.sessionPropertyDefaults = requireNonNull(sessionPropertyDefaults, "sessionPropertyDefaults is null"); this.sessionPropertyManager = sessionPropertyManager; + this.tracer = requireNonNull(tracer, "tracer is null"); this.maxQueryLength = queryManagerConfig.getMaxQueryLength(); @@ -131,9 +139,10 @@ public QueryId createQueryId() return queryIdGenerator.createNextQueryId(); } - public ListenableFuture createQuery(QueryId queryId, Slug slug, SessionContext sessionContext, String query) + public ListenableFuture createQuery(QueryId queryId, Span querySpan, Slug slug, SessionContext sessionContext, String query) { requireNonNull(queryId, "queryId is null"); + requireNonNull(querySpan, "querySpan is null"); requireNonNull(sessionContext, "sessionContext is null"); requireNonNull(query, "query is null"); checkArgument(!query.isEmpty(), "query must not be empty string"); @@ -143,14 +152,18 @@ public ListenableFuture createQuery(QueryId queryId, Slug slug, SessionCon // Using NonCancellationPropagatingFuture is not enough; it does not propagate cancel to wrapped future // but it would still return true on call to isCancelled() after cancel() is called on it. DispatchQueryCreationFuture queryCreationFuture = new DispatchQueryCreationFuture(); - dispatchExecutor.execute(() -> { - try { - createQueryInternal(queryId, slug, sessionContext, query, resourceGroupManager); + dispatchExecutor.execute(Context.current().wrap(() -> { + Span span = tracer.spanBuilder("dispatch") + .addLink(Span.current().getSpanContext()) + .setParent(Context.current().with(querySpan)) + .startSpan(); + try (var ignored = scopedSpan(span)) { + createQueryInternal(queryId, querySpan, slug, sessionContext, query, resourceGroupManager); } finally { queryCreationFuture.set(null); } - }); + })); return queryCreationFuture; } @@ -158,7 +171,7 @@ public ListenableFuture createQuery(QueryId queryId, Slug slug, SessionCon * Creates and registers a dispatch query with the query tracker. This method will never fail to register a query with the query * tracker. If an error occurs while creating a dispatch query, a failed dispatch will be created and registered. */ - private void createQueryInternal(QueryId queryId, Slug slug, SessionContext sessionContext, String query, ResourceGroupManager resourceGroupManager) + private void createQueryInternal(QueryId queryId, Span querySpan, Slug slug, SessionContext sessionContext, String query, ResourceGroupManager resourceGroupManager) { Session session = null; PreparedQuery preparedQuery = null; @@ -170,7 +183,7 @@ private void createQueryInternal(QueryId queryId, Slug slug, SessionContext } // decode session - session = sessionSupplier.createSession(queryId, sessionContext); + session = sessionSupplier.createSession(queryId, querySpan, sessionContext); // check query execute permissions accessControl.checkCanExecuteQuery(sessionContext.getIdentity()); @@ -223,6 +236,9 @@ private void createQueryInternal(QueryId queryId, Slug slug, SessionContext Optional preparedSql = Optional.ofNullable(preparedQuery).flatMap(PreparedQuery::getPrepareSql); DispatchQuery failedDispatchQuery = failedDispatchQueryFactory.createFailedDispatchQuery(session, query, preparedSql, Optional.empty(), throwable); queryCreated(failedDispatchQuery); + querySpan.setStatus(StatusCode.ERROR, throwable.getMessage()) + .recordException(throwable) + .end(); } } diff --git a/core/trino-main/src/main/java/io/trino/dispatcher/QueuedStatementResource.java b/core/trino-main/src/main/java/io/trino/dispatcher/QueuedStatementResource.java index d413af170472..81d0a25d4893 100644 --- a/core/trino-main/src/main/java/io/trino/dispatcher/QueuedStatementResource.java +++ b/core/trino-main/src/main/java/io/trino/dispatcher/QueuedStatementResource.java @@ -20,6 +20,9 @@ import com.google.common.util.concurrent.SettableFuture; import io.airlift.log.Logger; import io.airlift.units.Duration; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.api.trace.Tracer; import io.trino.client.QueryError; import io.trino.client.QueryResults; import io.trino.client.StatementStats; @@ -37,6 +40,7 @@ import io.trino.spi.ErrorCode; import io.trino.spi.QueryId; import io.trino.spi.security.Identity; +import io.trino.tracing.TrinoAttributes; import javax.annotation.Nullable; import javax.annotation.PostConstruct; @@ -109,6 +113,7 @@ public class QueuedStatementResource private final HttpRequestSessionContextFactory sessionContextFactory; private final DispatchManager dispatchManager; + private final Tracer tracer; private final QueryInfoUrlFactory queryInfoUrlFactory; @@ -123,6 +128,7 @@ public class QueuedStatementResource public QueuedStatementResource( HttpRequestSessionContextFactory sessionContextFactory, DispatchManager dispatchManager, + Tracer tracer, DispatchExecutor executor, QueryInfoUrlFactory queryInfoUrlTemplate, ServerConfig serverConfig, @@ -131,6 +137,7 @@ public QueuedStatementResource( { this.sessionContextFactory = requireNonNull(sessionContextFactory, "sessionContextFactory is null"); this.dispatchManager = requireNonNull(dispatchManager, "dispatchManager is null"); + this.tracer = requireNonNull(tracer, "tracer is null"); this.responseExecutor = executor.getExecutor(); this.timeoutExecutor = executor.getScheduledExecutor(); this.queryInfoUrlFactory = requireNonNull(queryInfoUrlTemplate, "queryInfoUrlTemplate is null"); @@ -180,7 +187,7 @@ private Query registerQuery(String statement, HttpServletRequest servletRequest, MultivaluedMap headers = httpHeaders.getRequestHeaders(); SessionContext sessionContext = sessionContextFactory.createSessionContext(headers, alternateHeaderName, remoteAddress, identity); - Query query = new Query(statement, sessionContext, dispatchManager, queryInfoUrlFactory); + Query query = new Query(statement, sessionContext, dispatchManager, queryInfoUrlFactory, tracer); queryManager.registerQuery(query); // let authentication filter know that identity lifecycle has been handed off @@ -310,6 +317,7 @@ private static final class Query private final DispatchManager dispatchManager; private final QueryId queryId; private final Optional queryInfoUrl; + private final Span querySpan; private final Slug slug = Slug.createNew(); private final AtomicLong lastToken = new AtomicLong(); @@ -317,7 +325,7 @@ private static final class Query private final AtomicReference submissionGate = new AtomicReference<>(); private final SettableFuture creationFuture = SettableFuture.create(); - public Query(String query, SessionContext sessionContext, DispatchManager dispatchManager, QueryInfoUrlFactory queryInfoUrlFactory) + public Query(String query, SessionContext sessionContext, DispatchManager dispatchManager, QueryInfoUrlFactory queryInfoUrlFactory, Tracer tracer) { this.query = requireNonNull(query, "query is null"); this.sessionContext = requireNonNull(sessionContext, "sessionContext is null"); @@ -325,6 +333,12 @@ public Query(String query, SessionContext sessionContext, DispatchManager dispat this.queryId = dispatchManager.createQueryId(); requireNonNull(queryInfoUrlFactory, "queryInfoUrlFactory is null"); this.queryInfoUrl = queryInfoUrlFactory.getQueryInfoUrl(queryId); + requireNonNull(tracer, "tracer is null"); + this.querySpan = tracer.spanBuilder("query") + .addLink(Span.current().getSpanContext()) + .setNoParent() + .setAttribute(TrinoAttributes.QUERY_ID, queryId.toString()) + .startSpan(); } public QueryId getQueryId() @@ -370,7 +384,8 @@ private ListenableFuture waitForDispatched() private void submitIfNeeded() { if (submissionGate.compareAndSet(null, true)) { - creationFuture.setFuture(dispatchManager.createQuery(queryId, slug, sessionContext, query)); + querySpan.addEvent("submit"); + creationFuture.setFuture(dispatchManager.createQuery(queryId, querySpan, slug, sessionContext, query)); } } @@ -408,6 +423,7 @@ public void cancel() public void destroy() { + querySpan.setStatus(StatusCode.ERROR).end(); sessionContext.getIdentity().destroy(); } diff --git a/core/trino-main/src/main/java/io/trino/execution/MemoryTrackingRemoteTaskFactory.java b/core/trino-main/src/main/java/io/trino/execution/MemoryTrackingRemoteTaskFactory.java index 2275cff23e69..3f13f3a2f136 100644 --- a/core/trino-main/src/main/java/io/trino/execution/MemoryTrackingRemoteTaskFactory.java +++ b/core/trino-main/src/main/java/io/trino/execution/MemoryTrackingRemoteTaskFactory.java @@ -15,6 +15,7 @@ import com.google.common.collect.Multimap; import io.airlift.units.DataSize; +import io.opentelemetry.api.trace.Span; import io.trino.Session; import io.trino.execution.NodeTaskMap.PartitionedSplitCountTracker; import io.trino.execution.StateMachine.StateChangeListener; @@ -45,6 +46,7 @@ public MemoryTrackingRemoteTaskFactory(RemoteTaskFactory remoteTaskFactory, Quer @Override public RemoteTask createRemoteTask( Session session, + Span stageSpan, TaskId taskId, InternalNode node, PlanFragment fragment, @@ -55,7 +57,9 @@ public RemoteTask createRemoteTask( Optional estimatedMemory, boolean summarizeTaskInfo) { - RemoteTask task = remoteTaskFactory.createRemoteTask(session, + RemoteTask task = remoteTaskFactory.createRemoteTask( + session, + stageSpan, taskId, node, fragment, diff --git a/core/trino-main/src/main/java/io/trino/execution/QueryStateMachine.java b/core/trino-main/src/main/java/io/trino/execution/QueryStateMachine.java index 7d910375ccdd..5b15d9bee00c 100644 --- a/core/trino-main/src/main/java/io/trino/execution/QueryStateMachine.java +++ b/core/trino-main/src/main/java/io/trino/execution/QueryStateMachine.java @@ -24,6 +24,9 @@ import com.google.common.util.concurrent.ListenableFuture; import io.airlift.log.Logger; import io.airlift.units.Duration; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.StatusCode; import io.trino.Session; import io.trino.client.NodeVersion; import io.trino.exchange.ExchangeInput; @@ -50,6 +53,7 @@ import io.trino.sql.analyzer.Output; import io.trino.sql.planner.PlanFragment; import io.trino.sql.planner.plan.TableScanNode; +import io.trino.tracing.TrinoAttributes; import io.trino.transaction.TransactionId; import io.trino.transaction.TransactionInfo; import io.trino.transaction.TransactionManager; @@ -81,6 +85,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Strings.nullToEmpty; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static io.airlift.units.DataSize.succinctBytes; import static io.trino.SystemSessionProperties.getRetryPolicy; @@ -301,6 +306,10 @@ static QueryStateMachine beginWithTicker( session = session.withExchangeEncryption(serializeAesEncryptionKey(createRandomAesEncryptionKey())); } + Span querySpan = session.getQuerySpan(); + + querySpan.setAttribute(TrinoAttributes.QUERY_TYPE, queryType.map(Enum::name).orElse("UNKNOWN")); + QueryStateMachine queryStateMachine = new QueryStateMachine( query, preparedQuery, @@ -315,6 +324,7 @@ static QueryStateMachine beginWithTicker( queryStatsCollector, queryType, version); + queryStateMachine.addStateChangeListener(newState -> { QUERY_STATE_LOG.debug("Query %s is %s", queryStateMachine.getQueryId(), newState); if (newState.isDone()) { @@ -323,6 +333,24 @@ static QueryStateMachine beginWithTicker( } }); + queryStateMachine.addStateChangeListener(newState -> { + querySpan.addEvent("query_state", Attributes.of( + TrinoAttributes.EVENT_STATE, newState.toString())); + if (newState.isDone()) { + queryStateMachine.getFailureInfo().ifPresentOrElse( + failure -> { + ErrorCode errorCode = requireNonNull(failure.getErrorCode()); + querySpan.setStatus(StatusCode.ERROR, nullToEmpty(failure.getMessage())) + .recordException(failure.toException()) + .setAttribute(TrinoAttributes.ERROR_CODE, errorCode.getCode()) + .setAttribute(TrinoAttributes.ERROR_NAME, errorCode.getName()) + .setAttribute(TrinoAttributes.ERROR_TYPE, errorCode.getType().toString()); + }, + () -> querySpan.setStatus(StatusCode.OK)); + querySpan.end(); + } + }); + return queryStateMachine; } diff --git a/core/trino-main/src/main/java/io/trino/execution/RemoteTaskFactory.java b/core/trino-main/src/main/java/io/trino/execution/RemoteTaskFactory.java index 085107dcb74e..d394f8744378 100644 --- a/core/trino-main/src/main/java/io/trino/execution/RemoteTaskFactory.java +++ b/core/trino-main/src/main/java/io/trino/execution/RemoteTaskFactory.java @@ -15,6 +15,7 @@ import com.google.common.collect.Multimap; import io.airlift.units.DataSize; +import io.opentelemetry.api.trace.Span; import io.trino.Session; import io.trino.execution.NodeTaskMap.PartitionedSplitCountTracker; import io.trino.execution.buffer.OutputBuffers; @@ -31,6 +32,7 @@ public interface RemoteTaskFactory { RemoteTask createRemoteTask( Session session, + Span stageSpan, TaskId taskId, InternalNode node, PlanFragment fragment, diff --git a/core/trino-main/src/main/java/io/trino/execution/SplitRunner.java b/core/trino-main/src/main/java/io/trino/execution/SplitRunner.java index 3bc1cac89f1f..5b12171230ec 100644 --- a/core/trino-main/src/main/java/io/trino/execution/SplitRunner.java +++ b/core/trino-main/src/main/java/io/trino/execution/SplitRunner.java @@ -15,12 +15,17 @@ import com.google.common.util.concurrent.ListenableFuture; import io.airlift.units.Duration; +import io.opentelemetry.api.trace.Span; import java.io.Closeable; public interface SplitRunner extends Closeable { + int getPipelineId(); + + Span getPipelineSpan(); + boolean isFinished(); ListenableFuture processFor(Duration duration); diff --git a/core/trino-main/src/main/java/io/trino/execution/SqlQueryExecution.java b/core/trino-main/src/main/java/io/trino/execution/SqlQueryExecution.java index 3d8ec74314af..a47b73d691a5 100644 --- a/core/trino-main/src/main/java/io/trino/execution/SqlQueryExecution.java +++ b/core/trino-main/src/main/java/io/trino/execution/SqlQueryExecution.java @@ -17,6 +17,9 @@ import io.airlift.concurrent.SetThreadName; import io.airlift.units.DataSize; import io.airlift.units.Duration; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; import io.trino.Session; import io.trino.SystemSessionProperties; import io.trino.cost.CostCalculator; @@ -92,6 +95,7 @@ import static io.trino.execution.QueryState.PLANNING; import static io.trino.server.DynamicFilterService.DynamicFiltersStats; import static io.trino.spi.StandardErrorCode.STACK_OVERFLOW; +import static io.trino.tracing.ScopedSpan.scopedSpan; import static java.lang.Thread.currentThread; import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.SECONDS; @@ -102,6 +106,7 @@ public class SqlQueryExecution { private final QueryStateMachine stateMachine; private final Slug slug; + private final Tracer tracer; private final PlannerContext plannerContext; private final SplitSourceFactory splitSourceFactory; private final NodePartitioningManager nodePartitioningManager; @@ -138,6 +143,7 @@ private SqlQueryExecution( PreparedQuery preparedQuery, QueryStateMachine stateMachine, Slug slug, + Tracer tracer, PlannerContext plannerContext, AnalyzerFactory analyzerFactory, SplitSourceFactory splitSourceFactory, @@ -170,6 +176,7 @@ private SqlQueryExecution( { try (SetThreadName ignored = new SetThreadName("Query-%s", stateMachine.getQueryId())) { this.slug = requireNonNull(slug, "slug is null"); + this.tracer = requireNonNull(tracer, "tracer is null"); this.plannerContext = requireNonNull(plannerContext, "plannerContext is null"); this.splitSourceFactory = requireNonNull(splitSourceFactory, "splitSourceFactory is null"); this.nodePartitioningManager = requireNonNull(nodePartitioningManager, "nodePartitioningManager is null"); @@ -449,7 +456,10 @@ public void addFinalQueryInfoListener(StateChangeListener stateChange private PlanRoot planQuery() { - try { + Span span = tracer.spanBuilder("planner") + .setParent(Context.current().with(getSession().getQuerySpan())) + .startSpan(); + try (var ignored = scopedSpan(span)) { return doPlanQuery(); } catch (StackOverflowError e) { @@ -474,11 +484,15 @@ private PlanRoot doPlanQuery() queryPlan.set(plan); // fragment the plan - SubPlan fragmentedPlan = planFragmenter.createSubPlans(stateMachine.getSession(), plan, false, stateMachine.getWarningCollector()); + SubPlan fragmentedPlan; + try (var ignored = scopedSpan(tracer, "fragment-plan")) { + fragmentedPlan = planFragmenter.createSubPlans(stateMachine.getSession(), plan, false, stateMachine.getWarningCollector()); + } // extract inputs - List inputs = new InputExtractor(plannerContext.getMetadata(), stateMachine.getSession()).extractInputs(fragmentedPlan); - stateMachine.setInputs(inputs); + try (var ignored = scopedSpan(tracer, "extract-inputs")) { + stateMachine.setInputs(new InputExtractor(plannerContext.getMetadata(), stateMachine.getSession()).extractInputs(fragmentedPlan)); + } stateMachine.setOutput(analysis.getTarget()); @@ -517,6 +531,7 @@ private void planDistribution(PlanRoot plan) failureDetector, nodeTaskMap, executionPolicy, + tracer, schedulerStats, dynamicFilterService, tableExecuteContextManager, @@ -535,6 +550,7 @@ private void planDistribution(PlanRoot plan) nodeTaskMap, queryExecutor, schedulerExecutor, + tracer, schedulerStats, partitionMemoryEstimatorFactory, nodePartitioningManager, @@ -724,6 +740,7 @@ public boolean isSummarizeTaskInfos() public static class SqlQueryExecutionFactory implements QueryExecutionFactory { + private final Tracer tracer; private final SplitSchedulerStats schedulerStats; private final int scheduleSplitBatchSize; private final PlannerContext plannerContext; @@ -754,6 +771,7 @@ public static class SqlQueryExecutionFactory @Inject SqlQueryExecutionFactory( + Tracer tracer, QueryManagerConfig config, PlannerContext plannerContext, AnalyzerFactory analyzerFactory, @@ -782,6 +800,7 @@ public static class SqlQueryExecutionFactory EventDrivenTaskSourceFactory eventDrivenTaskSourceFactory, TaskDescriptorStorage taskDescriptorStorage) { + this.tracer = requireNonNull(tracer, "tracer is null"); this.schedulerStats = requireNonNull(schedulerStats, "schedulerStats is null"); this.scheduleSplitBatchSize = config.getScheduleSplitBatchSize(); this.plannerContext = requireNonNull(plannerContext, "plannerContext is null"); @@ -827,6 +846,7 @@ public QueryExecution createQueryExecution( preparedQuery, stateMachine, slug, + tracer, plannerContext, analyzerFactory, splitSourceFactory, diff --git a/core/trino-main/src/main/java/io/trino/execution/SqlStage.java b/core/trino-main/src/main/java/io/trino/execution/SqlStage.java index 3eb447a3488f..4b40091e3be4 100644 --- a/core/trino-main/src/main/java/io/trino/execution/SqlStage.java +++ b/core/trino-main/src/main/java/io/trino/execution/SqlStage.java @@ -17,6 +17,8 @@ import com.google.common.collect.Multimap; import io.airlift.units.DataSize; import io.airlift.units.Duration; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; import io.trino.Session; import io.trino.execution.StateMachine.StateChangeListener; import io.trino.execution.buffer.OutputBuffers; @@ -82,6 +84,7 @@ public static SqlStage createSqlStage( boolean summarizeTaskInfo, NodeTaskMap nodeTaskMap, Executor stateMachineExecutor, + Tracer tracer, SplitSchedulerStats schedulerStats) { requireNonNull(stageId, "stageId is null"); @@ -92,11 +95,21 @@ public static SqlStage createSqlStage( requireNonNull(session, "session is null"); requireNonNull(nodeTaskMap, "nodeTaskMap is null"); requireNonNull(stateMachineExecutor, "stateMachineExecutor is null"); + requireNonNull(tracer, "tracer is null"); requireNonNull(schedulerStats, "schedulerStats is null"); + StageStateMachine stateMachine = new StageStateMachine( + stageId, + fragment, + tables, + stateMachineExecutor, + tracer, + session.getQuerySpan(), + schedulerStats); + SqlStage sqlStage = new SqlStage( session, - new StageStateMachine(stageId, fragment, tables, stateMachineExecutor, schedulerStats), + stateMachine, remoteTaskFactory, nodeTaskMap, summarizeTaskInfo); @@ -136,6 +149,11 @@ public StageId getStageId() return stateMachine.getStageId(); } + public Span getStageSpan() + { + return stateMachine.getStageSpan(); + } + public StageState getState() { return stateMachine.getState(); @@ -240,6 +258,7 @@ public synchronized Optional createTask( RemoteTask task = remoteTaskFactory.createRemoteTask( session, + stateMachine.getStageSpan(), taskId, node, stateMachine.getFragment().withBucketToPartition(bucketToPartition), diff --git a/core/trino-main/src/main/java/io/trino/execution/SqlTask.java b/core/trino-main/src/main/java/io/trino/execution/SqlTask.java index 9e646931bb34..08a79683ce7f 100644 --- a/core/trino-main/src/main/java/io/trino/execution/SqlTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/SqlTask.java @@ -22,6 +22,10 @@ import io.airlift.stats.CounterStat; import io.airlift.units.DataSize; import io.airlift.units.Duration; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; import io.trino.Session; import io.trino.exchange.ExchangeManagerRegistry; import io.trino.execution.DynamicFiltersCollector.VersionedDynamicFilterDomains; @@ -41,6 +45,7 @@ import io.trino.sql.planner.PlanFragment; import io.trino.sql.planner.plan.DynamicFilterId; import io.trino.sql.planner.plan.PlanNodeId; +import io.trino.tracing.TrinoAttributes; import org.joda.time.DateTime; import javax.annotation.Nullable; @@ -67,13 +72,8 @@ import static io.airlift.units.DataSize.succinctBytes; import static io.trino.execution.DynamicFiltersCollector.INITIAL_DYNAMIC_FILTERS_VERSION; import static io.trino.execution.DynamicFiltersCollector.INITIAL_DYNAMIC_FILTER_DOMAINS; -import static io.trino.execution.TaskState.ABORTED; -import static io.trino.execution.TaskState.ABORTING; -import static io.trino.execution.TaskState.CANCELED; -import static io.trino.execution.TaskState.CANCELING; import static io.trino.execution.TaskState.FAILED; import static io.trino.execution.TaskState.FAILING; -import static io.trino.execution.TaskState.FINISHED; import static io.trino.execution.TaskState.RUNNING; import static io.trino.util.Failures.toFailures; import static java.lang.String.format; @@ -91,6 +91,7 @@ public class SqlTask private final TaskStateMachine taskStateMachine; private final OutputBuffer outputBuffer; private final QueryContext queryContext; + private final Tracer tracer; private final SqlTaskExecutionFactory sqlTaskExecutionFactory; private final Executor taskNotificationExecutor; @@ -103,6 +104,7 @@ public class SqlTask @GuardedBy("taskHolderLock") private final AtomicReference taskHolderReference = new AtomicReference<>(new TaskHolder()); private final AtomicBoolean needsPlan = new AtomicBoolean(true); + private final AtomicReference taskSpan = new AtomicReference<>(Span.getInvalid()); private final AtomicReference traceToken = new AtomicReference<>(); private final AtomicReference> catalogs = new AtomicReference<>(); @@ -111,6 +113,7 @@ public static SqlTask createSqlTask( URI location, String nodeId, QueryContext queryContext, + Tracer tracer, SqlTaskExecutionFactory sqlTaskExecutionFactory, ExecutorService taskNotificationExecutor, Consumer onDone, @@ -119,7 +122,7 @@ public static SqlTask createSqlTask( ExchangeManagerRegistry exchangeManagerRegistry, CounterStat failedTasks) { - SqlTask sqlTask = new SqlTask(taskId, location, nodeId, queryContext, sqlTaskExecutionFactory, taskNotificationExecutor, maxBufferSize, maxBroadcastBufferSize, exchangeManagerRegistry); + SqlTask sqlTask = new SqlTask(taskId, location, nodeId, queryContext, tracer, sqlTaskExecutionFactory, taskNotificationExecutor, maxBufferSize, maxBroadcastBufferSize, exchangeManagerRegistry); sqlTask.initialize(onDone, failedTasks); return sqlTask; } @@ -129,6 +132,7 @@ private SqlTask( URI location, String nodeId, QueryContext queryContext, + Tracer tracer, SqlTaskExecutionFactory sqlTaskExecutionFactory, ExecutorService taskNotificationExecutor, DataSize maxBufferSize, @@ -140,6 +144,7 @@ private SqlTask( this.location = requireNonNull(location, "location is null"); this.nodeId = requireNonNull(nodeId, "nodeId is null"); this.queryContext = requireNonNull(queryContext, "queryContext is null"); + this.tracer = requireNonNull(tracer, "tracer is null"); this.sqlTaskExecutionFactory = requireNonNull(sqlTaskExecutionFactory, "sqlTaskExecutionFactory is null"); this.taskNotificationExecutor = requireNonNull(taskNotificationExecutor, "taskNotificationExecutor is null"); requireNonNull(maxBufferSize, "maxBufferSize is null"); @@ -166,6 +171,9 @@ private void initialize(Consumer onDone, CounterStat failedTasks) AtomicBoolean outputBufferCleanedUp = new AtomicBoolean(); taskStateMachine.addStateChangeListener(newState -> { + taskSpan.get().addEvent("task_state", Attributes.of( + TrinoAttributes.EVENT_STATE, newState.toString())); + if (newState.isTerminatingOrDone()) { if (newState.isTerminating()) { // This section must be synchronized to lock out any threads that might be attempting to create a SqlTaskExecution @@ -223,6 +231,10 @@ else if (newState.isDone()) { if (newState != RUNNING) { notifyStatusChanged(); } + + if (newState.isDone()) { + taskSpan.get().end(); + } }); } @@ -452,6 +464,7 @@ public synchronized ListenableFuture getTaskInfo(long callersCurrentVe public TaskInfo updateTask( Session session, + Span stageSpan, Optional fragment, List splitAssignments, OutputBuffers outputBuffers, @@ -475,7 +488,7 @@ public TaskInfo updateTask( SqlTaskExecution taskExecution = taskHolder.getTaskExecution(); if (taskExecution == null) { checkState(fragment.isPresent(), "fragment must be present"); - taskExecution = tryCreateSqlTaskExecution(session, fragment.get()); + taskExecution = tryCreateSqlTaskExecution(session, stageSpan, fragment.get()); } // taskExecution can still be null if the creation was skipped if (taskExecution != null) { @@ -495,7 +508,7 @@ public TaskInfo updateTask( } @Nullable - private SqlTaskExecution tryCreateSqlTaskExecution(Session session, PlanFragment fragment) + private SqlTaskExecution tryCreateSqlTaskExecution(Session session, Span stageSpan, PlanFragment fragment) { synchronized (taskHolderLock) { // Recheck holder for task execution after acquiring the lock @@ -513,8 +526,16 @@ private SqlTaskExecution tryCreateSqlTaskExecution(Session session, PlanFragment return null; } + taskSpan.set(tracer.spanBuilder("task") + .setParent(Context.current().with(stageSpan)) + .setAttribute(TrinoAttributes.QUERY_ID, taskId.getQueryId().toString()) + .setAttribute(TrinoAttributes.STAGE_ID, taskId.getStageId().toString()) + .setAttribute(TrinoAttributes.TASK_ID, taskId.toString()) + .startSpan()); + execution = sqlTaskExecutionFactory.create( session, + taskSpan.get(), queryContext, taskStateMachine, outputBuffer, diff --git a/core/trino-main/src/main/java/io/trino/execution/SqlTaskExecution.java b/core/trino-main/src/main/java/io/trino/execution/SqlTaskExecution.java index eb97f0101b17..88e59d86e4cc 100644 --- a/core/trino-main/src/main/java/io/trino/execution/SqlTaskExecution.java +++ b/core/trino-main/src/main/java/io/trino/execution/SqlTaskExecution.java @@ -21,6 +21,9 @@ import com.google.common.util.concurrent.ListenableFuture; import io.airlift.concurrent.SetThreadName; import io.airlift.units.Duration; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; import io.trino.event.SplitMonitor; import io.trino.execution.StateMachine.StateChangeListener; import io.trino.execution.buffer.BufferState; @@ -37,6 +40,7 @@ import io.trino.spi.TrinoException; import io.trino.sql.planner.LocalExecutionPlanner.LocalExecutionPlan; import io.trino.sql.planner.plan.PlanNodeId; +import io.trino.tracing.TrinoAttributes; import javax.annotation.Nullable; import javax.annotation.concurrent.GuardedBy; @@ -81,6 +85,7 @@ public class SqlTaskExecution { private final TaskId taskId; private final TaskStateMachine taskStateMachine; + private final Span taskSpan; private final TaskContext taskContext; private final OutputBuffer outputBuffer; @@ -114,14 +119,17 @@ public class SqlTaskExecution public SqlTaskExecution( TaskStateMachine taskStateMachine, TaskContext taskContext, + Span taskSpan, OutputBuffer outputBuffer, LocalExecutionPlan localExecutionPlan, TaskExecutor taskExecutor, SplitMonitor splitMonitor, + Tracer tracer, Executor notificationExecutor) { this.taskStateMachine = requireNonNull(taskStateMachine, "taskStateMachine is null"); this.taskId = taskStateMachine.getTaskId(); + this.taskSpan = requireNonNull(taskSpan, "taskSpan is null"); this.taskContext = requireNonNull(taskContext, "taskContext is null"); this.outputBuffer = requireNonNull(outputBuffer, "outputBuffer is null"); @@ -141,10 +149,10 @@ public SqlTaskExecution( for (DriverFactory driverFactory : driverFactories) { Optional sourceId = driverFactory.getSourceId(); if (sourceId.isPresent() && partitionedSources.contains(sourceId.get())) { - driverRunnerFactoriesWithSplitLifeCycle.put(sourceId.get(), new DriverSplitRunnerFactory(driverFactory, true)); + driverRunnerFactoriesWithSplitLifeCycle.put(sourceId.get(), new DriverSplitRunnerFactory(driverFactory, tracer, true)); } else { - DriverSplitRunnerFactory runnerFactory = new DriverSplitRunnerFactory(driverFactory, false); + DriverSplitRunnerFactory runnerFactory = new DriverSplitRunnerFactory(driverFactory, tracer, false); sourceId.ifPresent(planNodeId -> driverRunnerFactoriesWithRemoteSource.put(planNodeId, runnerFactory)); driverRunnerFactoriesWithTaskLifeCycle.add(runnerFactory); } @@ -172,6 +180,14 @@ public SqlTaskExecution( else { taskHandle = createTaskHandle(taskStateMachine, taskContext, outputBuffer, driverFactories, taskExecutor, driverAndTaskTerminationTracker); } + + taskStateMachine.addStateChangeListener(state -> { + if (state.isDone()) { + for (DriverSplitRunnerFactory factory : allDriverRunnerFactories) { + factory.getPipelineSpan().end(); + } + } + }); } } @@ -590,6 +606,7 @@ private class DriverSplitRunnerFactory { private final DriverFactory driverFactory; private final PipelineContext pipelineContext; + private final Span pipelineSpan; // number of created DriverSplitRunners that haven't created underlying Driver private final AtomicInteger pendingCreations = new AtomicInteger(); @@ -601,10 +618,17 @@ private class DriverSplitRunnerFactory private final AtomicLong inFlightSplits = new AtomicLong(); private final AtomicBoolean noMoreSplits = new AtomicBoolean(); - private DriverSplitRunnerFactory(DriverFactory driverFactory, boolean partitioned) + private DriverSplitRunnerFactory(DriverFactory driverFactory, Tracer tracer, boolean partitioned) { this.driverFactory = driverFactory; this.pipelineContext = taskContext.addPipelineContext(driverFactory.getPipelineId(), driverFactory.isInputDriver(), driverFactory.isOutputDriver(), partitioned); + this.pipelineSpan = tracer.spanBuilder("pipeline") + .setParent(Context.current().with(taskSpan)) + .setAttribute(TrinoAttributes.QUERY_ID, taskId.getQueryId().toString()) + .setAttribute(TrinoAttributes.STAGE_ID, taskId.getStageId().toString()) + .setAttribute(TrinoAttributes.TASK_ID, taskId.toString()) + .setAttribute(TrinoAttributes.PIPELINE_ID, taskId.getStageId() + "-" + pipelineContext.getPipelineId()) + .startSpan(); } public DriverSplitRunner createPartitionedDriverRunner(ScheduledSplit partitionedSplit) @@ -640,6 +664,7 @@ public Driver createDriver(DriverContext driverContext, @Nullable ScheduledSplit Driver driver; try { driver = driverFactory.createDriver(driverContext); + Span.fromContext(Context.current()).addEvent("driver-created"); } catch (Throwable t) { try { @@ -761,6 +786,7 @@ public void closeDriverFactoryIfFullyCreated() } if (isNoMoreDriverRunner() && pendingCreations.get() == 0) { driverFactory.noMoreDrivers(); + pipelineSpan.addEvent("driver-factory-closed"); } } @@ -778,6 +804,11 @@ public void splitsAdded(int count, long weightSum) { pipelineContext.splitsAdded(count, weightSum); } + + public Span getPipelineSpan() + { + return pipelineSpan; + } } private static class DriverSplitRunner @@ -810,6 +841,18 @@ public synchronized DriverContext getDriverContext() return driver.getDriverContext(); } + @Override + public int getPipelineId() + { + return driverContext.getPipelineContext().getPipelineId(); + } + + @Override + public Span getPipelineSpan() + { + return driverSplitRunnerFactory.getPipelineSpan(); + } + @Override public synchronized boolean isFinished() { diff --git a/core/trino-main/src/main/java/io/trino/execution/SqlTaskExecutionFactory.java b/core/trino-main/src/main/java/io/trino/execution/SqlTaskExecutionFactory.java index dd89d6e1cf78..6778cf252fb2 100644 --- a/core/trino-main/src/main/java/io/trino/execution/SqlTaskExecutionFactory.java +++ b/core/trino-main/src/main/java/io/trino/execution/SqlTaskExecutionFactory.java @@ -14,6 +14,8 @@ package io.trino.execution; import io.airlift.concurrent.SetThreadName; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; import io.trino.Session; import io.trino.event.SplitMonitor; import io.trino.execution.buffer.OutputBuffer; @@ -28,6 +30,7 @@ import java.util.concurrent.Executor; import static com.google.common.base.Throwables.throwIfUnchecked; +import static io.trino.tracing.ScopedSpan.scopedSpan; import static java.util.Objects.requireNonNull; public class SqlTaskExecutionFactory @@ -38,6 +41,7 @@ public class SqlTaskExecutionFactory private final LocalExecutionPlanner planner; private final SplitMonitor splitMonitor; + private final Tracer tracer; private final boolean perOperatorCpuTimerEnabled; private final boolean cpuTimerEnabled; @@ -46,18 +50,21 @@ public SqlTaskExecutionFactory( TaskExecutor taskExecutor, LocalExecutionPlanner planner, SplitMonitor splitMonitor, + Tracer tracer, TaskManagerConfig config) { this.taskNotificationExecutor = requireNonNull(taskNotificationExecutor, "taskNotificationExecutor is null"); this.taskExecutor = requireNonNull(taskExecutor, "taskExecutor is null"); this.planner = requireNonNull(planner, "planner is null"); this.splitMonitor = requireNonNull(splitMonitor, "splitMonitor is null"); + this.tracer = requireNonNull(tracer, "tracer is null"); this.perOperatorCpuTimerEnabled = config.isPerOperatorCpuTimerEnabled(); this.cpuTimerEnabled = config.isTaskCpuTimerEnabled(); } public SqlTaskExecution create( Session session, + Span taskSpan, QueryContext queryContext, TaskStateMachine taskStateMachine, OutputBuffer outputBuffer, @@ -73,7 +80,7 @@ public SqlTaskExecution create( LocalExecutionPlan localExecutionPlan; try (SetThreadName ignored = new SetThreadName("Task-%s", taskStateMachine.getTaskId())) { - try { + try (var ignoredSpan = scopedSpan(tracer, "local-planner")) { localExecutionPlan = planner.plan( taskContext, fragment.getRoot(), @@ -92,10 +99,12 @@ public SqlTaskExecution create( return new SqlTaskExecution( taskStateMachine, taskContext, + taskSpan, outputBuffer, localExecutionPlan, taskExecutor, splitMonitor, + tracer, taskNotificationExecutor); } } diff --git a/core/trino-main/src/main/java/io/trino/execution/SqlTaskManager.java b/core/trino-main/src/main/java/io/trino/execution/SqlTaskManager.java index d0bb6beb8e5d..a2c8b2c94ff9 100644 --- a/core/trino-main/src/main/java/io/trino/execution/SqlTaskManager.java +++ b/core/trino-main/src/main/java/io/trino/execution/SqlTaskManager.java @@ -26,6 +26,8 @@ import io.airlift.stats.GcMonitor; import io.airlift.units.DataSize; import io.airlift.units.Duration; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; import io.trino.Session; import io.trino.collect.cache.NonEvictableLoadingCache; import io.trino.connector.CatalogProperties; @@ -152,6 +154,7 @@ public SqlTaskManager( LocalSpillManager localSpillManager, NodeSpillConfig nodeSpillConfig, GcMonitor gcMonitor, + Tracer tracer, ExchangeManagerRegistry exchangeManagerRegistry) { this(versionEmbedder, @@ -168,6 +171,7 @@ public SqlTaskManager( localSpillManager, nodeSpillConfig, gcMonitor, + tracer, exchangeManagerRegistry, STUCK_SPLIT_STACK_TRACE_PREDICATE); } @@ -188,6 +192,7 @@ public SqlTaskManager( LocalSpillManager localSpillManager, NodeSpillConfig nodeSpillConfig, GcMonitor gcMonitor, + Tracer tracer, ExchangeManagerRegistry exchangeManagerRegistry, Predicate> stuckSplitStackTracePredicate) { @@ -207,7 +212,7 @@ public SqlTaskManager( this.taskManagementExecutor = taskManagementExecutor.getExecutor(); this.driverYieldExecutor = newScheduledThreadPool(config.getTaskYieldThreads(), threadsNamed("task-yield-%s")); - SqlTaskExecutionFactory sqlTaskExecutionFactory = new SqlTaskExecutionFactory(taskNotificationExecutor, taskExecutor, planner, splitMonitor, config); + SqlTaskExecutionFactory sqlTaskExecutionFactory = new SqlTaskExecutionFactory(taskNotificationExecutor, taskExecutor, planner, splitMonitor, tracer, config); DataSize maxQueryMemoryPerNode = nodeMemoryConfig.getMaxQueryMemoryPerNode(); DataSize maxQuerySpillPerNode = nodeSpillConfig.getQueryMaxSpillPerNode(); @@ -223,6 +228,7 @@ public SqlTaskManager( locationFactory.createLocalTaskLocation(taskId), nodeInfo.getNodeId(), queryContexts.getUnchecked(taskId.getQueryId()), + tracer, sqlTaskExecutionFactory, taskNotificationExecutor, sqlTask -> finishedTaskStats.merge(sqlTask.getIoStats()), @@ -460,13 +466,14 @@ public void pruneCatalogs(Set activeCatalogs) public TaskInfo updateTask( Session session, TaskId taskId, + Span stageSpan, Optional fragment, List splitAssignments, OutputBuffers outputBuffers, Map dynamicFilterDomains) { try { - return versionEmbedder.embedVersion(() -> doUpdateTask(session, taskId, fragment, splitAssignments, outputBuffers, dynamicFilterDomains)).call(); + return versionEmbedder.embedVersion(() -> doUpdateTask(session, taskId, stageSpan, fragment, splitAssignments, outputBuffers, dynamicFilterDomains)).call(); } catch (Exception e) { throwIfUnchecked(e); @@ -478,6 +485,7 @@ public TaskInfo updateTask( private TaskInfo doUpdateTask( Session session, TaskId taskId, + Span stageSpan, Optional fragment, List splitAssignments, OutputBuffers outputBuffers, @@ -485,6 +493,7 @@ private TaskInfo doUpdateTask( { requireNonNull(session, "session is null"); requireNonNull(taskId, "taskId is null"); + requireNonNull(stageSpan, "stageSpan is null"); requireNonNull(fragment, "fragment is null"); requireNonNull(splitAssignments, "splitAssignments is null"); requireNonNull(outputBuffers, "outputBuffers is null"); @@ -519,7 +528,7 @@ private TaskInfo doUpdateTask( }); sqlTask.recordHeartbeat(); - return sqlTask.updateTask(session, fragment, splitAssignments, outputBuffers, dynamicFilterDomains); + return sqlTask.updateTask(session, stageSpan, fragment, splitAssignments, outputBuffers, dynamicFilterDomains); } /** @@ -736,7 +745,7 @@ private Optional createStuckSplitTasksInterrupter( * The detection is invoked periodically with the frequency of {@link StuckSplitTasksInterrupter#stuckSplitsDetectionInterval}. * A thread gets interrupted once the split processing continues beyond {@link StuckSplitTasksInterrupter#interruptStuckSplitTasksTimeout} and * the split threaddump matches with {@link StuckSplitTasksInterrupter#stuckSplitStackTracePredicate}.

- * + *

* There is a potential race condition for this {@link StuckSplitTasksInterrupter} class. The problematic flow is that we may * kill a task that is long-running, but not really stuck on the code that matches {@link StuckSplitTasksInterrupter#stuckSplitStackTracePredicate} (e.g. JONI code). * Consider the following example: diff --git a/core/trino-main/src/main/java/io/trino/execution/StageStateMachine.java b/core/trino-main/src/main/java/io/trino/execution/StageStateMachine.java index 9cd84291110b..d4d479d092ce 100644 --- a/core/trino-main/src/main/java/io/trino/execution/StageStateMachine.java +++ b/core/trino-main/src/main/java/io/trino/execution/StageStateMachine.java @@ -18,6 +18,10 @@ import io.airlift.log.Logger; import io.airlift.stats.Distribution; import io.airlift.units.Duration; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; import io.trino.execution.StateMachine.StateChangeListener; import io.trino.execution.scheduler.SplitSchedulerStats; import io.trino.operator.BlockedReason; @@ -29,6 +33,7 @@ import io.trino.sql.planner.PlanFragment; import io.trino.sql.planner.plan.PlanNodeId; import io.trino.sql.planner.plan.TableScanNode; +import io.trino.tracing.TrinoAttributes; import io.trino.util.Failures; import io.trino.util.Optionals; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; @@ -82,6 +87,7 @@ public class StageStateMachine private final StateMachine stageState; private final StateMachine> finalStageInfo; + private final Span stageSpan; private final AtomicReference failureCause = new AtomicReference<>(); private final AtomicReference schedulingComplete = new AtomicReference<>(); @@ -98,6 +104,8 @@ public StageStateMachine( PlanFragment fragment, Map tables, Executor executor, + Tracer tracer, + Span querySpan, SplitSchedulerStats schedulerStats) { this.stageId = requireNonNull(stageId, "stageId is null"); @@ -109,6 +117,20 @@ public StageStateMachine( stageState.addStateChangeListener(state -> log.debug("Stage %s is %s", stageId, state)); finalStageInfo = new StateMachine<>("final stage " + stageId, executor, Optional.empty()); + + stageSpan = tracer.spanBuilder("stage") + .setParent(Context.current().with(querySpan)) + .setAttribute(TrinoAttributes.QUERY_ID, stageId.getQueryId().toString()) + .setAttribute(TrinoAttributes.STAGE_ID, stageId.toString()) + .startSpan(); + + stageState.addStateChangeListener(state -> { + stageSpan.addEvent("stage_state", Attributes.of( + TrinoAttributes.EVENT_STATE, state.toString())); + if (state.isDone()) { + stageSpan.end(); + } + }); } public StageId getStageId() @@ -126,6 +148,11 @@ public PlanFragment getFragment() return fragment; } + public Span getStageSpan() + { + return stageSpan; + } + /** * Listener is always notified asynchronously using a dedicated notification thread pool so, care should * be taken to avoid leaking {@code this} when adding a listener in a constructor. Additionally, it is diff --git a/core/trino-main/src/main/java/io/trino/execution/executor/PrioritizedSplitRunner.java b/core/trino-main/src/main/java/io/trino/execution/executor/PrioritizedSplitRunner.java index 5b98af62e0a3..0d4f3a68f169 100644 --- a/core/trino-main/src/main/java/io/trino/execution/executor/PrioritizedSplitRunner.java +++ b/core/trino-main/src/main/java/io/trino/execution/executor/PrioritizedSplitRunner.java @@ -21,7 +21,11 @@ import io.airlift.stats.CpuTimer; import io.airlift.stats.TimeStat; import io.airlift.units.Duration; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; import io.trino.execution.SplitRunner; +import io.trino.tracing.TrinoAttributes; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -29,6 +33,7 @@ import java.util.concurrent.atomic.AtomicReference; import static io.trino.operator.Operator.NOT_BLOCKED; +import static io.trino.tracing.ScopedSpan.scopedSpan; import static java.lang.String.format; import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.NANOSECONDS; @@ -50,6 +55,9 @@ public class PrioritizedSplitRunner private final long workerId; private final SplitRunner split; + private final Span splitSpan; + + private final Tracer tracer; private final Ticker ticker; private final SettableFuture finishedFuture = SettableFuture.create(); @@ -75,7 +83,10 @@ public class PrioritizedSplitRunner PrioritizedSplitRunner( TaskHandle taskHandle, + int splitId, SplitRunner split, + Span splitSpan, + Tracer tracer, Ticker ticker, CounterStat globalCpuTimeMicros, CounterStat globalScheduledTimeMicros, @@ -83,8 +94,10 @@ public class PrioritizedSplitRunner TimeStat unblockedQuantaWallTime) { this.taskHandle = requireNonNull(taskHandle, "taskHandle is null"); - this.splitId = taskHandle.getNextSplitId(); + this.splitId = splitId; this.split = requireNonNull(split, "split is null"); + this.splitSpan = requireNonNull(splitSpan, "splitSpan is null"); + this.tracer = requireNonNull(tracer, "tracer is null"); this.ticker = requireNonNull(ticker, "ticker is null"); this.workerId = NEXT_WORKER_ID.getAndIncrement(); this.globalCpuTimeMicros = requireNonNull(globalCpuTimeMicros, "globalCpuTimeMicros is null"); @@ -119,6 +132,12 @@ public void destroy() catch (RuntimeException e) { log.error(e, "Error closing split for task %s", taskHandle.getTaskId()); } + finally { + splitSpan.setAttribute(TrinoAttributes.SPLIT_SCHEDULED_TIME_NANOS, getScheduledNanos()); + splitSpan.setAttribute(TrinoAttributes.SPLIT_CPU_TIME_NANOS, getCpuTimeNanos()); + splitSpan.setAttribute(TrinoAttributes.SPLIT_WAIT_TIME_NANOS, getWaitNanos()); + splitSpan.end(); + } } public long getCreatedNanos() @@ -152,7 +171,11 @@ public long getWaitNanos() public ListenableFuture process() { - try { + Span span = tracer.spanBuilder("process") + .setParent(Context.current().with(splitSpan)) + .startSpan(); + + try (var ignored = scopedSpan(span)) { long startNanos = ticker.read(); start.compareAndSet(0, startNanos); lastReady.compareAndSet(0, startNanos); @@ -185,6 +208,10 @@ public ListenableFuture process() globalCpuTimeMicros.update(quantaCpuNanos / 1000); globalScheduledTimeMicros.update(quantaScheduledNanos / 1000); + span.setAttribute(TrinoAttributes.SPLIT_CPU_TIME_NANOS, quantaCpuNanos); + span.setAttribute(TrinoAttributes.SPLIT_SCHEDULED_TIME_NANOS, quantaScheduledNanos); + span.setAttribute(TrinoAttributes.SPLIT_BLOCKED, blocked != NOT_BLOCKED); + return blocked; } catch (Throwable e) { diff --git a/core/trino-main/src/main/java/io/trino/execution/executor/TaskExecutor.java b/core/trino-main/src/main/java/io/trino/execution/executor/TaskExecutor.java index c501e223ccfc..da10e1532feb 100644 --- a/core/trino-main/src/main/java/io/trino/execution/executor/TaskExecutor.java +++ b/core/trino-main/src/main/java/io/trino/execution/executor/TaskExecutor.java @@ -25,11 +25,15 @@ import io.airlift.stats.TimeDistribution; import io.airlift.stats.TimeStat; import io.airlift.units.Duration; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; import io.trino.execution.SplitRunner; import io.trino.execution.TaskId; import io.trino.execution.TaskManagerConfig; import io.trino.spi.TrinoException; import io.trino.spi.VersionEmbedder; +import io.trino.tracing.TrinoAttributes; import org.weakref.jmx.Managed; import org.weakref.jmx.Nested; @@ -68,6 +72,7 @@ import static com.google.common.collect.Sets.newConcurrentHashSet; import static io.airlift.concurrent.Threads.daemonThreadsNamed; import static io.airlift.concurrent.Threads.threadsNamed; +import static io.airlift.tracing.Tracing.noopTracer; import static io.trino.execution.executor.MultilevelSplitQueue.computeLevel; import static io.trino.version.EmbedVersion.testingVersionEmbedder; import static java.lang.Math.min; @@ -92,6 +97,7 @@ public class TaskExecutor private final int guaranteedNumberOfDriversPerTask; private final int maximumNumberOfDriversPerTask; private final VersionEmbedder versionEmbedder; + private final Tracer tracer; private final Ticker ticker; @@ -163,7 +169,7 @@ public class TaskExecutor private volatile boolean closed; @Inject - public TaskExecutor(TaskManagerConfig config, VersionEmbedder versionEmbedder, MultilevelSplitQueue splitQueue) + public TaskExecutor(TaskManagerConfig config, VersionEmbedder versionEmbedder, Tracer tracer, MultilevelSplitQueue splitQueue) { this( config.getMaxWorkerThreads(), @@ -172,6 +178,7 @@ public TaskExecutor(TaskManagerConfig config, VersionEmbedder versionEmbedder, M config.getMaxDriversPerTask(), config.getInterruptStuckSplitTasksWarningThreshold(), versionEmbedder, + tracer, splitQueue, Ticker.systemTicker()); } @@ -179,13 +186,13 @@ public TaskExecutor(TaskManagerConfig config, VersionEmbedder versionEmbedder, M @VisibleForTesting public TaskExecutor(int runnerThreads, int minDrivers, int guaranteedNumberOfDriversPerTask, int maximumNumberOfDriversPerTask, Ticker ticker) { - this(runnerThreads, minDrivers, guaranteedNumberOfDriversPerTask, maximumNumberOfDriversPerTask, new Duration(10, TimeUnit.MINUTES), testingVersionEmbedder(), new MultilevelSplitQueue(2), ticker); + this(runnerThreads, minDrivers, guaranteedNumberOfDriversPerTask, maximumNumberOfDriversPerTask, new Duration(10, TimeUnit.MINUTES), testingVersionEmbedder(), noopTracer(), new MultilevelSplitQueue(2), ticker); } @VisibleForTesting public TaskExecutor(int runnerThreads, int minDrivers, int guaranteedNumberOfDriversPerTask, int maximumNumberOfDriversPerTask, MultilevelSplitQueue splitQueue, Ticker ticker) { - this(runnerThreads, minDrivers, guaranteedNumberOfDriversPerTask, maximumNumberOfDriversPerTask, new Duration(10, TimeUnit.MINUTES), testingVersionEmbedder(), splitQueue, ticker); + this(runnerThreads, minDrivers, guaranteedNumberOfDriversPerTask, maximumNumberOfDriversPerTask, new Duration(10, TimeUnit.MINUTES), testingVersionEmbedder(), noopTracer(), splitQueue, ticker); } @VisibleForTesting @@ -196,6 +203,7 @@ public TaskExecutor( int maximumNumberOfDriversPerTask, Duration stuckSplitsWarningThreshold, VersionEmbedder versionEmbedder, + Tracer tracer, MultilevelSplitQueue splitQueue, Ticker ticker) { @@ -209,6 +217,7 @@ public TaskExecutor( this.executorMBean = new ThreadPoolExecutorMBean((ThreadPoolExecutor) executor); this.runnerThreads = runnerThreads; this.versionEmbedder = requireNonNull(versionEmbedder, "versionEmbedder is null"); + this.tracer = requireNonNull(tracer, "tracer is null"); this.ticker = requireNonNull(ticker, "ticker is null"); this.stuckSplitsWarningThreshold = requireNonNull(stuckSplitsWarningThreshold, "stuckSplitsWarningThreshold is null"); @@ -340,9 +349,24 @@ public List> enqueueSplits(TaskHandle taskHandle, boolean List> finishedFutures = new ArrayList<>(taskSplits.size()); synchronized (this) { for (SplitRunner taskSplit : taskSplits) { + TaskId taskId = taskHandle.getTaskId(); + int splitId = taskHandle.getNextSplitId(); + + Span splitSpan = tracer.spanBuilder(intermediate ? "split (intermediate)" : "split (leaf)") + .setParent(Context.current().with(taskSplit.getPipelineSpan())) + .setAttribute(TrinoAttributes.QUERY_ID, taskId.getQueryId().toString()) + .setAttribute(TrinoAttributes.STAGE_ID, taskId.getStageId().toString()) + .setAttribute(TrinoAttributes.TASK_ID, taskId.toString()) + .setAttribute(TrinoAttributes.PIPELINE_ID, taskId.getStageId() + "-" + taskSplit.getPipelineId()) + .setAttribute(TrinoAttributes.SPLIT_ID, taskId + "-" + splitId) + .startSpan(); + PrioritizedSplitRunner prioritizedSplitRunner = new PrioritizedSplitRunner( taskHandle, + splitId, taskSplit, + splitSpan, + tracer, ticker, globalCpuTimeMicros, globalScheduledTimeMicros, diff --git a/core/trino-main/src/main/java/io/trino/execution/scheduler/EventDrivenFaultTolerantQueryScheduler.java b/core/trino-main/src/main/java/io/trino/execution/scheduler/EventDrivenFaultTolerantQueryScheduler.java index 2aa19f767187..8c941109eb33 100644 --- a/core/trino-main/src/main/java/io/trino/execution/scheduler/EventDrivenFaultTolerantQueryScheduler.java +++ b/core/trino-main/src/main/java/io/trino/execution/scheduler/EventDrivenFaultTolerantQueryScheduler.java @@ -33,6 +33,7 @@ import io.airlift.log.Logger; import io.airlift.units.DataSize; import io.airlift.units.Duration; +import io.opentelemetry.api.trace.Tracer; import io.trino.Session; import io.trino.exchange.SpoolingExchangeInput; import io.trino.execution.BasicStageStats; @@ -168,6 +169,7 @@ public class EventDrivenFaultTolerantQueryScheduler private final NodeTaskMap nodeTaskMap; private final ExecutorService queryExecutor; private final ScheduledExecutorService scheduledExecutorService; + private final Tracer tracer; private final SplitSchedulerStats schedulerStats; private final PartitionMemoryEstimatorFactory memoryEstimatorFactory; private final NodePartitioningManager nodePartitioningManager; @@ -195,6 +197,7 @@ public EventDrivenFaultTolerantQueryScheduler( NodeTaskMap nodeTaskMap, ExecutorService queryExecutor, ScheduledExecutorService scheduledExecutorService, + Tracer tracer, SplitSchedulerStats schedulerStats, PartitionMemoryEstimatorFactory memoryEstimatorFactory, NodePartitioningManager nodePartitioningManager, @@ -216,6 +219,7 @@ public EventDrivenFaultTolerantQueryScheduler( this.nodeTaskMap = requireNonNull(nodeTaskMap, "nodeTaskMap is null"); this.queryExecutor = requireNonNull(queryExecutor, "queryExecutor is null"); this.scheduledExecutorService = requireNonNull(scheduledExecutorService, "scheduledExecutorService is null"); + this.tracer = requireNonNull(tracer, "tracer is null"); this.schedulerStats = requireNonNull(schedulerStats, "schedulerStats is null"); this.memoryEstimatorFactory = requireNonNull(memoryEstimatorFactory, "memoryEstimatorFactory is null"); this.nodePartitioningManager = requireNonNull(nodePartitioningManager, "partitioningSchemeFactory is null"); @@ -279,7 +283,9 @@ public synchronized void start() summarizeTaskInfo, nodeTaskMap, queryExecutor, - scheduledExecutorService, schedulerStats, + scheduledExecutorService, + tracer, + schedulerStats, memoryEstimatorFactory, partitioningSchemeFactory, exchangeManager, @@ -458,6 +464,7 @@ private static class Scheduler private final NodeTaskMap nodeTaskMap; private final ExecutorService queryExecutor; private final ScheduledExecutorService scheduledExecutorService; + private final Tracer tracer; private final SplitSchedulerStats schedulerStats; private final PartitionMemoryEstimatorFactory memoryEstimatorFactory; private final FaultTolerantPartitioningSchemeFactory partitioningSchemeFactory; @@ -501,6 +508,7 @@ public Scheduler( NodeTaskMap nodeTaskMap, ExecutorService queryExecutor, ScheduledExecutorService scheduledExecutorService, + Tracer tracer, SplitSchedulerStats schedulerStats, PartitionMemoryEstimatorFactory memoryEstimatorFactory, FaultTolerantPartitioningSchemeFactory partitioningSchemeFactory, @@ -525,6 +533,7 @@ public Scheduler( this.nodeTaskMap = requireNonNull(nodeTaskMap, "nodeTaskMap is null"); this.queryExecutor = requireNonNull(queryExecutor, "queryExecutor is null"); this.scheduledExecutorService = requireNonNull(scheduledExecutorService, "scheduledExecutorService is null"); + this.tracer = requireNonNull(tracer, "tracer is null"); this.schedulerStats = requireNonNull(schedulerStats, "schedulerStats is null"); this.memoryEstimatorFactory = requireNonNull(memoryEstimatorFactory, "memoryEstimatorFactory is null"); this.partitioningSchemeFactory = requireNonNull(partitioningSchemeFactory, "partitioningSchemeFactory is null"); @@ -782,6 +791,7 @@ private void createStageExecution(SubPlan subPlan, boolean rootFragment, int sch summarizeTaskInfo, nodeTaskMap, queryStateMachine.getStateMachineExecutor(), + tracer, schedulerStats); closer.register(stage::abort); stageRegistry.add(stage); @@ -811,6 +821,7 @@ private void createStageExecution(SubPlan subPlan, boolean rootFragment, int sch EventDrivenTaskSource taskSource = closer.register(taskSourceFactory.create( session, + stage.getStageSpan(), fragment, sourceExchanges.buildOrThrow(), partitioningSchemeFactory.get(fragment.getPartitioning()), diff --git a/core/trino-main/src/main/java/io/trino/execution/scheduler/EventDrivenTaskSourceFactory.java b/core/trino-main/src/main/java/io/trino/execution/scheduler/EventDrivenTaskSourceFactory.java index 2ea724d34985..c3b564188e56 100644 --- a/core/trino-main/src/main/java/io/trino/execution/scheduler/EventDrivenTaskSourceFactory.java +++ b/core/trino-main/src/main/java/io/trino/execution/scheduler/EventDrivenTaskSourceFactory.java @@ -15,6 +15,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; +import io.opentelemetry.api.trace.Span; import io.trino.Session; import io.trino.execution.ForQueryExecution; import io.trino.execution.QueryManagerConfig; @@ -106,6 +107,7 @@ public EventDrivenTaskSourceFactory( public EventDrivenTaskSource create( Session session, + Span stageSpan, PlanFragment fragment, Map sourceExchanges, FaultTolerantPartitioningScheme sourcePartitioningScheme, @@ -125,7 +127,7 @@ public EventDrivenTaskSource create( tableExecuteContextManager, sourceExchanges, remoteSources.build(), - () -> splitSourceFactory.createSplitSources(session, fragment), + () -> splitSourceFactory.createSplitSources(session, stageSpan, fragment), createSplitAssigner( session, fragment, diff --git a/core/trino-main/src/main/java/io/trino/execution/scheduler/PipelinedQueryScheduler.java b/core/trino-main/src/main/java/io/trino/execution/scheduler/PipelinedQueryScheduler.java index 855185fc742c..0c7a2154061b 100644 --- a/core/trino-main/src/main/java/io/trino/execution/scheduler/PipelinedQueryScheduler.java +++ b/core/trino-main/src/main/java/io/trino/execution/scheduler/PipelinedQueryScheduler.java @@ -25,6 +25,8 @@ import io.airlift.log.Logger; import io.airlift.stats.TimeStat; import io.airlift.units.Duration; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; import io.trino.Session; import io.trino.exchange.DirectExchangeInput; import io.trino.execution.BasicStageStats; @@ -199,6 +201,7 @@ public PipelinedQueryScheduler( FailureDetector failureDetector, NodeTaskMap nodeTaskMap, ExecutionPolicy executionPolicy, + Tracer tracer, SplitSchedulerStats schedulerStats, DynamicFilterService dynamicFilterService, TableExecuteContextManager tableExecuteContextManager, @@ -224,6 +227,7 @@ public PipelinedQueryScheduler( metadata, remoteTaskFactory, nodeTaskMap, + tracer, schedulerStats, plan, summarizeTaskInfo); @@ -1025,10 +1029,11 @@ private static StageScheduler createStageScheduler( TableExecuteContextManager tableExecuteContextManager) { Session session = queryStateMachine.getSession(); + Span stageSpan = stageExecution.getStageSpan(); PlanFragment fragment = stageExecution.getFragment(); PartitioningHandle partitioningHandle = fragment.getPartitioning(); Optional partitionCount = fragment.getPartitionCount(); - Map splitSources = splitSourceFactory.createSplitSources(session, fragment); + Map splitSources = splitSourceFactory.createSplitSources(session, stageSpan, fragment); if (!splitSources.isEmpty()) { queryStateMachine.addStateChangeListener(new StateChangeListener<>() { diff --git a/core/trino-main/src/main/java/io/trino/execution/scheduler/PipelinedStageExecution.java b/core/trino-main/src/main/java/io/trino/execution/scheduler/PipelinedStageExecution.java index 3e01c9e3966a..b46f13322a80 100644 --- a/core/trino-main/src/main/java/io/trino/execution/scheduler/PipelinedStageExecution.java +++ b/core/trino-main/src/main/java/io/trino/execution/scheduler/PipelinedStageExecution.java @@ -20,6 +20,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; import io.airlift.log.Logger; +import io.opentelemetry.api.trace.Span; import io.trino.exchange.DirectExchangeInput; import io.trino.execution.ExecutionFailureInfo; import io.trino.execution.RemoteTask; @@ -552,6 +553,12 @@ public int getAttemptId() return attempt; } + @Override + public Span getStageSpan() + { + return stage.getStageSpan(); + } + @Override public PlanFragment getFragment() { diff --git a/core/trino-main/src/main/java/io/trino/execution/scheduler/StageExecution.java b/core/trino-main/src/main/java/io/trino/execution/scheduler/StageExecution.java index 89fc0bc73e2e..d283010b7f65 100644 --- a/core/trino-main/src/main/java/io/trino/execution/scheduler/StageExecution.java +++ b/core/trino-main/src/main/java/io/trino/execution/scheduler/StageExecution.java @@ -14,6 +14,7 @@ package io.trino.execution.scheduler; import com.google.common.collect.Multimap; +import io.opentelemetry.api.trace.Span; import io.trino.execution.ExecutionFailureInfo; import io.trino.execution.RemoteTask; import io.trino.execution.StageId; @@ -36,6 +37,8 @@ public interface StageExecution int getAttemptId(); + Span getStageSpan(); + PlanFragment getFragment(); boolean isAnyTaskBlocked(); diff --git a/core/trino-main/src/main/java/io/trino/execution/scheduler/StageManager.java b/core/trino-main/src/main/java/io/trino/execution/scheduler/StageManager.java index 524424ca968d..2bc6391858b2 100644 --- a/core/trino-main/src/main/java/io/trino/execution/scheduler/StageManager.java +++ b/core/trino-main/src/main/java/io/trino/execution/scheduler/StageManager.java @@ -17,6 +17,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.graph.Traverser; import io.airlift.units.Duration; +import io.opentelemetry.api.trace.Tracer; import io.trino.Session; import io.trino.execution.BasicStageStats; import io.trino.execution.NodeTaskMap; @@ -65,6 +66,7 @@ static StageManager create( Metadata metadata, RemoteTaskFactory taskFactory, NodeTaskMap nodeTaskMap, + Tracer tracer, SplitSchedulerStats schedulerStats, SubPlan planTree, boolean summarizeTaskInfo) @@ -88,6 +90,7 @@ static StageManager create( summarizeTaskInfo, nodeTaskMap, queryStateMachine.getStateMachineExecutor(), + tracer, schedulerStats); StageId stageId = stage.getStageId(); stages.put(stageId, stage); diff --git a/core/trino-main/src/main/java/io/trino/metadata/Catalog.java b/core/trino-main/src/main/java/io/trino/metadata/Catalog.java index cbbab652291a..f0cf057f1c78 100644 --- a/core/trino-main/src/main/java/io/trino/metadata/Catalog.java +++ b/core/trino-main/src/main/java/io/trino/metadata/Catalog.java @@ -134,7 +134,11 @@ private static CatalogTransaction beginTransaction( transactionHandle = connector.beginTransaction(isolationLevel, readOnly, autoCommitContext); } - return new CatalogTransaction(connectorServices.getCatalogHandle(), connector, transactionHandle); + return new CatalogTransaction( + connectorServices.getTracer(), + connectorServices.getCatalogHandle(), + connector, + transactionHandle); } @Override diff --git a/core/trino-main/src/main/java/io/trino/metadata/CatalogTransaction.java b/core/trino-main/src/main/java/io/trino/metadata/CatalogTransaction.java index d4e7ac751d0d..13cb22b3aa60 100644 --- a/core/trino-main/src/main/java/io/trino/metadata/CatalogTransaction.java +++ b/core/trino-main/src/main/java/io/trino/metadata/CatalogTransaction.java @@ -13,12 +13,16 @@ */ package io.trino.metadata; +import io.opentelemetry.api.trace.Tracer; import io.trino.Session; +import io.trino.connector.informationschema.InformationSchemaMetadata; +import io.trino.connector.system.SystemTablesMetadata; import io.trino.spi.connector.CatalogHandle; import io.trino.spi.connector.Connector; import io.trino.spi.connector.ConnectorMetadata; import io.trino.spi.connector.ConnectorSession; import io.trino.spi.connector.ConnectorTransactionHandle; +import io.trino.tracing.TracingConnectorMetadata; import javax.annotation.concurrent.GuardedBy; @@ -29,6 +33,7 @@ public class CatalogTransaction { + private final Tracer tracer; private final CatalogHandle catalogHandle; private final Connector connector; private final ConnectorTransactionHandle transactionHandle; @@ -37,10 +42,12 @@ public class CatalogTransaction private final AtomicBoolean finished = new AtomicBoolean(); public CatalogTransaction( + Tracer tracer, CatalogHandle catalogHandle, Connector connector, ConnectorTransactionHandle transactionHandle) { + this.tracer = requireNonNull(tracer, "tracer is null"); this.catalogHandle = requireNonNull(catalogHandle, "catalogHandle is null"); this.connector = requireNonNull(connector, "connector is null"); this.transactionHandle = requireNonNull(transactionHandle, "transactionHandle is null"); @@ -62,6 +69,7 @@ public synchronized ConnectorMetadata getConnectorMetadata(Session session) if (connectorMetadata == null) { ConnectorSession connectorSession = session.toConnectorSession(catalogHandle); connectorMetadata = connector.getMetadata(connectorSession, transactionHandle); + connectorMetadata = tracingConnectorMetadata(catalogHandle.getCatalogName(), connectorMetadata); } return connectorMetadata; } @@ -85,4 +93,12 @@ public void abort() connector.rollback(transactionHandle); } } + + private ConnectorMetadata tracingConnectorMetadata(String catalogName, ConnectorMetadata delegate) + { + if ((delegate instanceof SystemTablesMetadata) || (delegate instanceof InformationSchemaMetadata)) { + return delegate; + } + return new TracingConnectorMetadata(tracer, catalogName, delegate); + } } diff --git a/core/trino-main/src/main/java/io/trino/server/HttpRemoteTaskFactory.java b/core/trino-main/src/main/java/io/trino/server/HttpRemoteTaskFactory.java index 974084c7aaab..e9a38930e1c9 100644 --- a/core/trino-main/src/main/java/io/trino/server/HttpRemoteTaskFactory.java +++ b/core/trino-main/src/main/java/io/trino/server/HttpRemoteTaskFactory.java @@ -20,6 +20,8 @@ import io.airlift.json.JsonCodec; import io.airlift.units.DataSize; import io.airlift.units.Duration; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; import io.trino.Session; import io.trino.execution.DynamicFiltersCollector.VersionedDynamicFilterDomains; import io.trino.execution.LocationFactory; @@ -77,6 +79,7 @@ public class HttpRemoteTaskFactory private final ThreadPoolExecutorMBean executorMBean; private final ScheduledExecutorService updateScheduledExecutor; private final ScheduledExecutorService errorScheduledExecutor; + private final Tracer tracer; private final RemoteTaskStats stats; private final DynamicFilterService dynamicFilterService; @@ -91,6 +94,7 @@ public HttpRemoteTaskFactory( JsonCodec taskInfoCodec, JsonCodec taskUpdateRequestCodec, JsonCodec failTaskRequestCoded, + Tracer tracer, RemoteTaskStats stats, DynamicFilterService dynamicFilterService) { @@ -108,6 +112,7 @@ public HttpRemoteTaskFactory( this.coreExecutor = newCachedThreadPool(daemonThreadsNamed("remote-task-callback-%s")); this.executor = new BoundedExecutor(coreExecutor, config.getRemoteTaskMaxCallbackThreads()); this.executorMBean = new ThreadPoolExecutorMBean((ThreadPoolExecutor) coreExecutor); + this.tracer = requireNonNull(tracer, "tracer is null"); this.stats = requireNonNull(stats, "stats is null"); this.dynamicFilterService = requireNonNull(dynamicFilterService, "dynamicFilterService is null"); @@ -133,6 +138,7 @@ public void stop() @Override public RemoteTask createRemoteTask( Session session, + Span stageSpan, TaskId taskId, InternalNode node, PlanFragment fragment, @@ -143,7 +149,9 @@ public RemoteTask createRemoteTask( Optional estimatedMemory, boolean summarizeTaskInfo) { - return new HttpRemoteTask(session, + return new HttpRemoteTask( + session, + stageSpan, taskId, node.getNodeIdentifier(), locationFactory.createTaskLocation(node, taskId), @@ -165,6 +173,7 @@ public RemoteTask createRemoteTask( taskUpdateRequestCodec, failTaskRequestCoded, partitionedSplitCountTracker, + tracer, stats, dynamicFilterService, outboundDynamicFilterIds, diff --git a/core/trino-main/src/main/java/io/trino/server/NoOpSessionSupplier.java b/core/trino-main/src/main/java/io/trino/server/NoOpSessionSupplier.java index 291c6e0ba3f7..723ccf4c4ea7 100644 --- a/core/trino-main/src/main/java/io/trino/server/NoOpSessionSupplier.java +++ b/core/trino-main/src/main/java/io/trino/server/NoOpSessionSupplier.java @@ -13,6 +13,7 @@ */ package io.trino.server; +import io.opentelemetry.api.trace.Span; import io.trino.Session; import io.trino.spi.QueryId; @@ -23,7 +24,7 @@ public class NoOpSessionSupplier implements SessionSupplier { @Override - public Session createSession(QueryId queryId, SessionContext context) + public Session createSession(QueryId queryId, Span querySpan, SessionContext context) { throw new UnsupportedOperationException(); } diff --git a/core/trino-main/src/main/java/io/trino/server/PluginManager.java b/core/trino-main/src/main/java/io/trino/server/PluginManager.java index 92052fcd1d6b..87fa1131488a 100644 --- a/core/trino-main/src/main/java/io/trino/server/PluginManager.java +++ b/core/trino-main/src/main/java/io/trino/server/PluginManager.java @@ -72,6 +72,8 @@ public class PluginManager .add("com.fasterxml.jackson.annotation.") .add("io.airlift.slice.") .add("org.openjdk.jol.") + .add("io.opentelemetry.api.") + .add("io.opentelemetry.context.") .build(); private static final Logger log = Logger.get(PluginManager.class); diff --git a/core/trino-main/src/main/java/io/trino/server/QuerySessionSupplier.java b/core/trino-main/src/main/java/io/trino/server/QuerySessionSupplier.java index 58fe9b468a19..9bae204717e9 100644 --- a/core/trino-main/src/main/java/io/trino/server/QuerySessionSupplier.java +++ b/core/trino-main/src/main/java/io/trino/server/QuerySessionSupplier.java @@ -13,6 +13,7 @@ */ package io.trino.server; +import io.opentelemetry.api.trace.Span; import io.trino.Session; import io.trino.metadata.Metadata; import io.trino.metadata.SessionPropertyManager; @@ -69,7 +70,7 @@ public QuerySessionSupplier( } @Override - public Session createSession(QueryId queryId, SessionContext context) + public Session createSession(QueryId queryId, Span querySpan, SessionContext context) { Identity identity = context.getIdentity(); accessControl.checkCanSetUser(identity.getPrincipal(), identity.getUser()); @@ -90,6 +91,7 @@ public Session createSession(QueryId queryId, SessionContext context) SessionBuilder sessionBuilder = Session.builder(sessionPropertyManager) .setQueryId(queryId) + .setQuerySpan(querySpan) .setIdentity(identity) .setPath(context.getPath().or(() -> defaultPath).map(SqlPath::new)) .setSource(context.getSource()) diff --git a/core/trino-main/src/main/java/io/trino/server/Server.java b/core/trino-main/src/main/java/io/trino/server/Server.java index 8318dc1f26e2..c90c1221af69 100644 --- a/core/trino-main/src/main/java/io/trino/server/Server.java +++ b/core/trino-main/src/main/java/io/trino/server/Server.java @@ -38,6 +38,7 @@ import io.airlift.node.NodeModule; import io.airlift.openmetrics.JmxOpenMetricsModule; import io.airlift.tracetoken.TraceTokenModule; +import io.airlift.tracing.TracingModule; import io.trino.client.NodeVersion; import io.trino.connector.CatalogManagerConfig; import io.trino.connector.CatalogManagerConfig.CatalogMangerKind; @@ -112,6 +113,7 @@ private void doStart(String trinoVersion) new JmxOpenMetricsModule(), new LogJmxModule(), new TraceTokenModule(), + new TracingModule("trino", trinoVersion), new EventModule(), new JsonEventModule(), new ServerSecurityModule(), diff --git a/core/trino-main/src/main/java/io/trino/server/ServerMainModule.java b/core/trino-main/src/main/java/io/trino/server/ServerMainModule.java index 5e768f344bd8..e9a7b81a0136 100644 --- a/core/trino-main/src/main/java/io/trino/server/ServerMainModule.java +++ b/core/trino-main/src/main/java/io/trino/server/ServerMainModule.java @@ -141,6 +141,8 @@ import io.trino.sql.planner.RuleStatsRecorder; import io.trino.sql.planner.TypeAnalyzer; import io.trino.sql.tree.Expression; +import io.trino.tracing.ForTracing; +import io.trino.tracing.TracingMetadata; import io.trino.type.BlockTypeOperators; import io.trino.type.InternalTypeManager; import io.trino.type.JsonPath2016Type; @@ -368,7 +370,8 @@ protected void setup(Binder binder) // metadata binder.bind(MetadataManager.class).in(Scopes.SINGLETON); - binder.bind(Metadata.class).to(MetadataManager.class).in(Scopes.SINGLETON); + binder.bind(Metadata.class).annotatedWith(ForTracing.class).to(MetadataManager.class).in(Scopes.SINGLETON); + binder.bind(Metadata.class).to(TracingMetadata.class).in(Scopes.SINGLETON); newOptionalBinder(binder, SystemSecurityMetadata.class) .setDefault() .to(DisabledSystemSecurityMetadata.class) diff --git a/core/trino-main/src/main/java/io/trino/server/SessionSupplier.java b/core/trino-main/src/main/java/io/trino/server/SessionSupplier.java index 9837b8a9c24d..2ff7e3a4672e 100644 --- a/core/trino-main/src/main/java/io/trino/server/SessionSupplier.java +++ b/core/trino-main/src/main/java/io/trino/server/SessionSupplier.java @@ -13,10 +13,11 @@ */ package io.trino.server; +import io.opentelemetry.api.trace.Span; import io.trino.Session; import io.trino.spi.QueryId; public interface SessionSupplier { - Session createSession(QueryId queryId, SessionContext context); + Session createSession(QueryId queryId, Span querySpan, SessionContext context); } diff --git a/core/trino-main/src/main/java/io/trino/server/TaskResource.java b/core/trino-main/src/main/java/io/trino/server/TaskResource.java index 007f7a4909f8..e143a835aa76 100644 --- a/core/trino-main/src/main/java/io/trino/server/TaskResource.java +++ b/core/trino-main/src/main/java/io/trino/server/TaskResource.java @@ -151,8 +151,10 @@ public void createOrUpdateTask( return; } - TaskInfo taskInfo = taskManager.updateTask(session, + TaskInfo taskInfo = taskManager.updateTask( + session, taskId, + taskUpdateRequest.getStageSpan(), taskUpdateRequest.getFragment(), taskUpdateRequest.getSplitAssignments(), taskUpdateRequest.getOutputIds(), diff --git a/core/trino-main/src/main/java/io/trino/server/TaskUpdateRequest.java b/core/trino-main/src/main/java/io/trino/server/TaskUpdateRequest.java index 08a3b8807a40..b30175502ec1 100644 --- a/core/trino-main/src/main/java/io/trino/server/TaskUpdateRequest.java +++ b/core/trino-main/src/main/java/io/trino/server/TaskUpdateRequest.java @@ -17,6 +17,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableList; import io.airlift.slice.Slice; +import io.opentelemetry.api.trace.Span; import io.trino.SessionRepresentation; import io.trino.execution.SplitAssignment; import io.trino.execution.buffer.OutputBuffers; @@ -36,6 +37,7 @@ public class TaskUpdateRequest private final SessionRepresentation session; // extraCredentials is stored separately from SessionRepresentation to avoid being leaked private final Map extraCredentials; + private final Span stageSpan; private final Optional fragment; private final List splitAssignments; private final OutputBuffers outputIds; @@ -46,6 +48,7 @@ public class TaskUpdateRequest public TaskUpdateRequest( @JsonProperty("session") SessionRepresentation session, @JsonProperty("extraCredentials") Map extraCredentials, + @JsonProperty("stageSpan") Span stageSpan, @JsonProperty("fragment") Optional fragment, @JsonProperty("splitAssignments") List splitAssignments, @JsonProperty("outputIds") OutputBuffers outputIds, @@ -54,6 +57,7 @@ public TaskUpdateRequest( { requireNonNull(session, "session is null"); requireNonNull(extraCredentials, "extraCredentials is null"); + requireNonNull(stageSpan, "stageSpan is null"); requireNonNull(fragment, "fragment is null"); requireNonNull(splitAssignments, "splitAssignments is null"); requireNonNull(outputIds, "outputIds is null"); @@ -62,6 +66,7 @@ public TaskUpdateRequest( this.session = session; this.extraCredentials = extraCredentials; + this.stageSpan = stageSpan; this.fragment = fragment; this.splitAssignments = ImmutableList.copyOf(splitAssignments); this.outputIds = outputIds; @@ -81,6 +86,12 @@ public Map getExtraCredentials() return extraCredentials; } + @JsonProperty + public Span getStageSpan() + { + return stageSpan; + } + @JsonProperty public Optional getFragment() { diff --git a/core/trino-main/src/main/java/io/trino/server/remotetask/ContinuousTaskStatusFetcher.java b/core/trino-main/src/main/java/io/trino/server/remotetask/ContinuousTaskStatusFetcher.java index 22994843ab40..6fbdee7ab376 100644 --- a/core/trino-main/src/main/java/io/trino/server/remotetask/ContinuousTaskStatusFetcher.java +++ b/core/trino-main/src/main/java/io/trino/server/remotetask/ContinuousTaskStatusFetcher.java @@ -22,6 +22,7 @@ import io.airlift.json.JsonCodec; import io.airlift.log.Logger; import io.airlift.units.Duration; +import io.opentelemetry.api.trace.SpanBuilder; import io.trino.execution.StateMachine; import io.trino.execution.TaskId; import io.trino.execution.TaskStatus; @@ -34,6 +35,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; +import java.util.function.Supplier; import static com.google.common.base.Strings.isNullOrEmpty; import static com.google.common.net.HttpHeaders.CONTENT_TYPE; @@ -62,6 +64,7 @@ class ContinuousTaskStatusFetcher private final Duration refreshMaxWait; private final Executor executor; private final HttpClient httpClient; + private final Supplier spanBuilderFactory; private final RequestErrorTracker errorTracker; private final RemoteTaskStats stats; @@ -79,6 +82,7 @@ public ContinuousTaskStatusFetcher( DynamicFiltersFetcher dynamicFiltersFetcher, Executor executor, HttpClient httpClient, + Supplier spanBuilderFactory, Duration maxErrorDuration, ScheduledExecutorService errorScheduledExecutor, RemoteTaskStats stats) @@ -95,6 +99,7 @@ public ContinuousTaskStatusFetcher( this.executor = requireNonNull(executor, "executor is null"); this.httpClient = requireNonNull(httpClient, "httpClient is null"); + this.spanBuilderFactory = requireNonNull(spanBuilderFactory, "spanBuilderFactory is null"); this.errorTracker = new RequestErrorTracker(taskId, initialTaskStatus.getSelf(), maxErrorDuration, errorScheduledExecutor, "getting task status"); this.stats = requireNonNull(stats, "stats is null"); @@ -146,6 +151,7 @@ private synchronized void scheduleNextRequest() .setHeader(CONTENT_TYPE, JSON_UTF_8.toString()) .setHeader(TRINO_CURRENT_VERSION, Long.toString(taskStatus.getVersion())) .setHeader(TRINO_MAX_WAIT, refreshMaxWait.toString()) + .setSpanBuilder(spanBuilderFactory.get()) .build(); errorTracker.startRequest(); diff --git a/core/trino-main/src/main/java/io/trino/server/remotetask/DynamicFiltersFetcher.java b/core/trino-main/src/main/java/io/trino/server/remotetask/DynamicFiltersFetcher.java index cdb88244b289..61712bf56359 100644 --- a/core/trino-main/src/main/java/io/trino/server/remotetask/DynamicFiltersFetcher.java +++ b/core/trino-main/src/main/java/io/trino/server/remotetask/DynamicFiltersFetcher.java @@ -20,6 +20,7 @@ import io.airlift.http.client.Request; import io.airlift.json.JsonCodec; import io.airlift.units.Duration; +import io.opentelemetry.api.trace.SpanBuilder; import io.trino.execution.DynamicFiltersCollector.VersionedDynamicFilterDomains; import io.trino.execution.TaskId; import io.trino.server.DynamicFilterService; @@ -30,6 +31,7 @@ import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; import java.util.function.Consumer; +import java.util.function.Supplier; import static com.google.common.net.HttpHeaders.CONTENT_TYPE; import static com.google.common.net.MediaType.JSON_UTF_8; @@ -52,6 +54,7 @@ class DynamicFiltersFetcher private final Duration refreshMaxWait; private final Executor executor; private final HttpClient httpClient; + private final Supplier spanBuilderFactory; private final RequestErrorTracker errorTracker; private final RemoteTaskStats stats; private final DynamicFilterService dynamicFilterService; @@ -73,6 +76,7 @@ public DynamicFiltersFetcher( JsonCodec dynamicFilterDomainsCodec, Executor executor, HttpClient httpClient, + Supplier spanBuilderFactory, Duration maxErrorDuration, ScheduledExecutorService errorScheduledExecutor, RemoteTaskStats stats, @@ -87,6 +91,7 @@ public DynamicFiltersFetcher( this.executor = requireNonNull(executor, "executor is null"); this.httpClient = requireNonNull(httpClient, "httpClient is null"); + this.spanBuilderFactory = requireNonNull(spanBuilderFactory, "spanBuilderFactory is null"); this.errorTracker = new RequestErrorTracker(taskId, taskUri, maxErrorDuration, errorScheduledExecutor, "getting dynamic filter domains"); this.stats = requireNonNull(stats, "stats is null"); @@ -142,6 +147,7 @@ private synchronized void fetchDynamicFiltersIfNecessary() .setHeader(CONTENT_TYPE, JSON_UTF_8.toString()) .setHeader(TRINO_CURRENT_VERSION, Long.toString(localDynamicFiltersVersion)) .setHeader(TRINO_MAX_WAIT, refreshMaxWait.toString()) + .setSpanBuilder(spanBuilderFactory.get()) .build(); errorTracker.startRequest(); diff --git a/core/trino-main/src/main/java/io/trino/server/remotetask/HttpRemoteTask.java b/core/trino-main/src/main/java/io/trino/server/remotetask/HttpRemoteTask.java index 9e59b6bec98d..805475eb5717 100644 --- a/core/trino-main/src/main/java/io/trino/server/remotetask/HttpRemoteTask.java +++ b/core/trino-main/src/main/java/io/trino/server/remotetask/HttpRemoteTask.java @@ -33,6 +33,10 @@ import io.airlift.log.Logger; import io.airlift.units.DataSize; import io.airlift.units.Duration; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanBuilder; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; import io.trino.Session; import io.trino.execution.DynamicFiltersCollector; import io.trino.execution.DynamicFiltersCollector.VersionedDynamicFilterDomains; @@ -64,6 +68,7 @@ import io.trino.sql.planner.plan.DynamicFilterId; import io.trino.sql.planner.plan.PlanNode; import io.trino.sql.planner.plan.PlanNodeId; +import io.trino.tracing.TrinoAttributes; import org.joda.time.DateTime; import javax.annotation.concurrent.GuardedBy; @@ -128,12 +133,15 @@ public final class HttpRemoteTask private final TaskId taskId; private final Session session; + private final Span stageSpan; private final String nodeId; private final PlanFragment planFragment; private final AtomicLong nextSplitId = new AtomicLong(); private final RemoteTaskStats stats; + private final Tracer tracer; + private final Span span; private final TaskInfoFetcher taskInfoFetcher; private final ContinuousTaskStatusFetcher taskStatusFetcher; private final DynamicFiltersFetcher dynamicFiltersFetcher; @@ -196,6 +204,7 @@ public final class HttpRemoteTask public HttpRemoteTask( Session session, + Span stageSpan, TaskId taskId, String nodeId, URI location, @@ -217,12 +226,14 @@ public HttpRemoteTask( JsonCodec taskUpdateRequestCodec, JsonCodec failTaskRequestCodec, PartitionedSplitCountTracker partitionedSplitCountTracker, + Tracer tracer, RemoteTaskStats stats, DynamicFilterService dynamicFilterService, Set outboundDynamicFilterIds, Optional estimatedMemory) { requireNonNull(session, "session is null"); + requireNonNull(stageSpan, "stageSpan is null"); requireNonNull(taskId, "taskId is null"); requireNonNull(nodeId, "nodeId is null"); requireNonNull(location, "location is null"); @@ -241,6 +252,7 @@ public HttpRemoteTask( try (SetThreadName ignored = new SetThreadName("HttpRemoteTask-%s", taskId)) { this.taskId = taskId; this.session = session; + this.stageSpan = stageSpan; this.nodeId = nodeId; this.planFragment = planFragment; this.outputBuffers.set(outputBuffers); @@ -255,6 +267,8 @@ public HttpRemoteTask( this.failTaskRequestCodec = failTaskRequestCodec; this.updateErrorTracker = new RequestErrorTracker(taskId, location, maxErrorDuration, errorScheduledExecutor, "updating task"); this.partitionedSplitCountTracker = requireNonNull(partitionedSplitCountTracker, "partitionedSplitCountTracker is null"); + this.tracer = requireNonNull(tracer, "tracer is null"); + this.span = createSpanBuilder("remote-task", stageSpan).startSpan(); this.stats = stats; for (Entry entry : initialSplits.entries()) { @@ -307,6 +321,7 @@ public HttpRemoteTask( dynamicFilterDomainsCodec, executor, httpClient, + () -> createSpanBuilder("task-dynamic-filters", span), maxErrorDuration, errorScheduledExecutor, stats, @@ -320,6 +335,7 @@ public HttpRemoteTask( dynamicFiltersFetcher, executor, httpClient, + () -> createSpanBuilder("task-status", span), maxErrorDuration, errorScheduledExecutor, stats); @@ -329,6 +345,7 @@ public HttpRemoteTask( taskStatusFetcher, initialTask, httpClient, + () -> createSpanBuilder("task-info", span), taskInfoUpdateInterval, taskInfoCodec, maxErrorDuration, @@ -349,6 +366,9 @@ public HttpRemoteTask( partitionedSplitCountTracker.setPartitionedSplits(getPartitionedSplitsInfo()); updateSplitQueueSpace(); } + if (state.isDone()) { + span.end(); + } }); this.outboundDynamicFiltersCollector = new DynamicFiltersCollector(this::triggerUpdate); @@ -709,6 +729,7 @@ private void sendUpdate() TaskUpdateRequest updateRequest = new TaskUpdateRequest( session.toSessionRepresentation(), session.getIdentity().getExtraCredentials(), + stageSpan, fragment, splitAssignments, outputBuffers.get(), @@ -734,6 +755,7 @@ private void sendUpdate() .setUri(uriBuilder.build()) .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.JSON_UTF_8.toString()) .setBodyGenerator(createStaticBodyGenerator(taskUpdateRequestJson)) + .setSpanBuilder(createSpanBuilder("task-update", span)) .build(); updateErrorTracker.startRequest(); @@ -895,6 +917,7 @@ private Request buildDeleteTaskRequest(boolean abort) HttpUriBuilder uriBuilder = getHttpUriBuilder(getTaskStatus()).addParameter("abort", "" + abort); return prepareDelete() .setUri(uriBuilder.build()) + .setSpanBuilder(createSpanBuilder("task-delete", span)) .build(); } @@ -906,6 +929,7 @@ private Request buildFailTaskRequest(FailTaskRequest failTaskRequest) .setUri(uriBuilder.build()) .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.JSON_UTF_8.toString()) .setBodyGenerator(createStaticBodyGenerator(failTaskRequestCodec.toJsonBytes(failTaskRequest))) + .setSpanBuilder(createSpanBuilder("task-fail", span)) .build(); } @@ -1052,6 +1076,15 @@ private HttpUriBuilder getHttpUriBuilder(TaskStatus taskStatus) return uriBuilder; } + private SpanBuilder createSpanBuilder(String name, Span parent) + { + return tracer.spanBuilder(name) + .setParent(Context.current().with(parent)) + .setAttribute(TrinoAttributes.QUERY_ID, taskId.getQueryId().toString()) + .setAttribute(TrinoAttributes.STAGE_ID, taskId.getStageId().toString()) + .setAttribute(TrinoAttributes.TASK_ID, taskId.toString()); + } + @Override public String toString() { diff --git a/core/trino-main/src/main/java/io/trino/server/remotetask/TaskInfoFetcher.java b/core/trino-main/src/main/java/io/trino/server/remotetask/TaskInfoFetcher.java index 2b446fd0b991..c141dfe85394 100644 --- a/core/trino-main/src/main/java/io/trino/server/remotetask/TaskInfoFetcher.java +++ b/core/trino-main/src/main/java/io/trino/server/remotetask/TaskInfoFetcher.java @@ -23,6 +23,7 @@ import io.airlift.json.JsonCodec; import io.airlift.units.DataSize; import io.airlift.units.Duration; +import io.opentelemetry.api.trace.SpanBuilder; import io.trino.execution.StateMachine; import io.trino.execution.StateMachine.StateChangeListener; import io.trino.execution.TaskId; @@ -42,6 +43,7 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; +import java.util.function.Supplier; import static com.google.common.base.Preconditions.checkState; import static com.google.common.net.HttpHeaders.CONTENT_TYPE; @@ -68,6 +70,7 @@ public class TaskInfoFetcher private final Executor executor; private final HttpClient httpClient; + private final Supplier spanBuilderFactory; private final RequestErrorTracker errorTracker; private final boolean summarizeTaskInfo; @@ -90,6 +93,7 @@ public TaskInfoFetcher( ContinuousTaskStatusFetcher taskStatusFetcher, TaskInfo initialTask, HttpClient httpClient, + Supplier spanBuilderFactory, Duration updateInterval, JsonCodec taskInfoCodec, Duration maxErrorDuration, @@ -118,6 +122,7 @@ public TaskInfoFetcher( this.executor = requireNonNull(executor, "executor is null"); this.httpClient = requireNonNull(httpClient, "httpClient is null"); + this.spanBuilderFactory = requireNonNull(spanBuilderFactory, "spanBuilderFactory is null"); this.stats = requireNonNull(stats, "stats is null"); this.estimatedMemory = requireNonNull(estimatedMemory, "estimatedMemory is null"); } @@ -224,6 +229,7 @@ private synchronized void sendNextRequest() Request request = prepareGet() .setUri(uri) .setHeader(CONTENT_TYPE, JSON_UTF_8.toString()) + .setSpanBuilder(spanBuilderFactory.get()) .build(); errorTracker.startRequest(); diff --git a/core/trino-main/src/main/java/io/trino/server/testing/TestingTrinoServer.java b/core/trino-main/src/main/java/io/trino/server/testing/TestingTrinoServer.java index 34582cd46921..b32792eabfe0 100644 --- a/core/trino-main/src/main/java/io/trino/server/testing/TestingTrinoServer.java +++ b/core/trino-main/src/main/java/io/trino/server/testing/TestingTrinoServer.java @@ -36,6 +36,7 @@ import io.airlift.node.testing.TestingNodeModule; import io.airlift.openmetrics.JmxOpenMetricsModule; import io.airlift.tracetoken.TraceTokenModule; +import io.airlift.tracing.TracingModule; import io.trino.connector.CatalogManagerModule; import io.trino.connector.ConnectorName; import io.trino.connector.ConnectorServicesProvider; @@ -131,6 +132,8 @@ public class TestingTrinoServer implements Closeable { + private static final String VERSION = "testversion"; + public static TestingTrinoServer create() { return builder().build(); @@ -262,10 +265,11 @@ private TestingTrinoServer( .add(new JmxOpenMetricsModule()) .add(new EventModule()) .add(new TraceTokenModule()) + .add(new TracingModule("trino", VERSION)) .add(new ServerSecurityModule()) .add(new CatalogManagerModule()) .add(new TransactionManagerModule()) - .add(new ServerMainModule("testversion")) + .add(new ServerMainModule(VERSION)) .add(new TestingWarningCollectorModule()) .add(binder -> { binder.bind(EventListenerConfig.class).in(Scopes.SINGLETON); diff --git a/core/trino-main/src/main/java/io/trino/split/BufferingSplitSource.java b/core/trino-main/src/main/java/io/trino/split/BufferingSplitSource.java index 41248cf928e6..084d6722fa99 100644 --- a/core/trino-main/src/main/java/io/trino/split/BufferingSplitSource.java +++ b/core/trino-main/src/main/java/io/trino/split/BufferingSplitSource.java @@ -15,6 +15,7 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import io.opentelemetry.context.Context; import io.trino.metadata.Split; import io.trino.spi.connector.CatalogHandle; @@ -72,6 +73,7 @@ public Optional> getTableExecuteSplitsInfo() private static class GetNextBatch { + private final Context context = Context.current(); private final SplitSource splitSource; private final int min; private final int max; @@ -102,7 +104,10 @@ private ListenableFuture fetchSplits() if (splits.size() >= min) { return immediateVoidFuture(); } - ListenableFuture future = splitSource.getNextBatch(max - splits.size()); + ListenableFuture future; + try (var ignored = context.makeCurrent()) { + future = splitSource.getNextBatch(max - splits.size()); + } return Futures.transformAsync(future, splitBatch -> { splits.addAll(splitBatch.getSplits()); if (splitBatch.isLastBatch()) { diff --git a/core/trino-main/src/main/java/io/trino/split/SplitManager.java b/core/trino-main/src/main/java/io/trino/split/SplitManager.java index b5ed757ec6f6..dbb11680b375 100644 --- a/core/trino-main/src/main/java/io/trino/split/SplitManager.java +++ b/core/trino-main/src/main/java/io/trino/split/SplitManager.java @@ -13,6 +13,9 @@ */ package io.trino.split; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; import io.trino.Session; import io.trino.connector.CatalogServiceProvider; import io.trino.execution.QueryManagerConfig; @@ -24,26 +27,32 @@ import io.trino.spi.connector.ConnectorSplitSource; import io.trino.spi.connector.Constraint; import io.trino.spi.connector.DynamicFilter; +import io.trino.tracing.TrinoAttributes; import javax.inject.Inject; +import java.util.Optional; + import static io.trino.SystemSessionProperties.isAllowPushdownIntoConnectors; import static java.util.Objects.requireNonNull; public class SplitManager { private final CatalogServiceProvider splitManagerProvider; + private final Tracer tracer; private final int minScheduleSplitBatchSize; @Inject - public SplitManager(CatalogServiceProvider splitManagerProvider, QueryManagerConfig config) + public SplitManager(CatalogServiceProvider splitManagerProvider, Tracer tracer, QueryManagerConfig config) { this.splitManagerProvider = requireNonNull(splitManagerProvider, "splitManagerProvider is null"); + this.tracer = requireNonNull(tracer, "tracer is null"); this.minScheduleSplitBatchSize = config.getMinScheduleSplitBatchSize(); } public SplitSource getSplits( Session session, + Span parentSpan, TableHandle table, DynamicFilter dynamicFilter, Constraint constraint) @@ -64,13 +73,22 @@ public SplitSource getSplits( constraint); SplitSource splitSource = new ConnectorAwareSplitSource(catalogHandle, source); + + Span span = splitSourceSpan(parentSpan, catalogHandle); + if (minScheduleSplitBatchSize > 1) { + splitSource = new TracingSplitSource(splitSource, tracer, Optional.empty(), "split-batch"); splitSource = new BufferingSplitSource(splitSource, minScheduleSplitBatchSize); + splitSource = new TracingSplitSource(splitSource, tracer, Optional.of(span), "split-buffer"); } + else { + splitSource = new TracingSplitSource(splitSource, tracer, Optional.of(span), "split-batch"); + } + return splitSource; } - public SplitSource getSplits(Session session, TableFunctionHandle function) + public SplitSource getSplits(Session session, Span parentSpan, TableFunctionHandle function) { CatalogHandle catalogHandle = function.getCatalogHandle(); ConnectorSplitManager splitManager = splitManagerProvider.getService(catalogHandle); @@ -81,6 +99,17 @@ public SplitSource getSplits(Session session, TableFunctionHandle function) function.getSchemaFunctionName(), function.getFunctionHandle()); - return new ConnectorAwareSplitSource(catalogHandle, source); + SplitSource splitSource = new ConnectorAwareSplitSource(catalogHandle, source); + + Span span = splitSourceSpan(parentSpan, catalogHandle); + return new TracingSplitSource(splitSource, tracer, Optional.of(span), "split-buffer"); + } + + private Span splitSourceSpan(Span querySpan, CatalogHandle catalogHandle) + { + return tracer.spanBuilder("split-source") + .setParent(Context.current().with(querySpan)) + .setAttribute(TrinoAttributes.CATALOG, catalogHandle.getCatalogName()) + .startSpan(); } } diff --git a/core/trino-main/src/main/java/io/trino/split/TracingSplitSource.java b/core/trino-main/src/main/java/io/trino/split/TracingSplitSource.java new file mode 100644 index 000000000000..8df2cc09b5b1 --- /dev/null +++ b/core/trino-main/src/main/java/io/trino/split/TracingSplitSource.java @@ -0,0 +1,108 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.split; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; +import io.trino.spi.connector.CatalogHandle; +import io.trino.tracing.TrinoAttributes; + +import java.util.List; +import java.util.Optional; + +import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static java.util.Objects.requireNonNull; + +public class TracingSplitSource + implements SplitSource +{ + private final SplitSource source; + private final Tracer tracer; + private final Optional parentSpan; + private final String spanName; + + public TracingSplitSource(SplitSource source, Tracer tracer, Optional parentSpan, String spanName) + { + this.source = requireNonNull(source, "source is null"); + this.tracer = requireNonNull(tracer, "tracer is null"); + this.parentSpan = requireNonNull(parentSpan, "parentSpan is null"); + this.spanName = requireNonNull(spanName, "spanName is null"); + } + + @Override + public CatalogHandle getCatalogHandle() + { + return source.getCatalogHandle(); + } + + @Override + public ListenableFuture getNextBatch(int maxSize) + { + Span span = tracer.spanBuilder(spanName) + .setParent(parentSpan.map(Context.current()::with).orElse(Context.current())) + .setAttribute(TrinoAttributes.SPLIT_BATCH_MAX_SIZE, (long) maxSize) + .startSpan(); + + ListenableFuture future; + try (var ignored = span.makeCurrent()) { + future = source.getNextBatch(maxSize); + } + catch (Throwable t) { + span.end(); + throw t; + } + + Futures.addCallback(future, new FutureCallback<>() + { + @Override + public void onSuccess(SplitBatch batch) + { + span.setAttribute(TrinoAttributes.SPLIT_BATCH_RESULT_SIZE, batch.getSplits().size()); + span.end(); + } + + @Override + public void onFailure(Throwable t) + { + span.end(); + } + }, directExecutor()); + + return future; + } + + @Override + public void close() + { + try (source) { + parentSpan.ifPresent(Span::end); + } + } + + @Override + public boolean isFinished() + { + return source.isFinished(); + } + + @Override + public Optional> getTableExecuteSplitsInfo() + { + return source.getTableExecuteSplitsInfo(); + } +} diff --git a/core/trino-main/src/main/java/io/trino/sql/PlannerContext.java b/core/trino-main/src/main/java/io/trino/sql/PlannerContext.java index 5bf90f91e5fb..a73d8c93e0ae 100644 --- a/core/trino-main/src/main/java/io/trino/sql/PlannerContext.java +++ b/core/trino-main/src/main/java/io/trino/sql/PlannerContext.java @@ -13,6 +13,7 @@ */ package io.trino.sql; +import io.opentelemetry.api.trace.Tracer; import io.trino.metadata.FunctionManager; import io.trino.metadata.Metadata; import io.trino.spi.block.BlockEncodingSerde; @@ -39,19 +40,22 @@ public class PlannerContext private final BlockEncodingSerde blockEncodingSerde; private final TypeManager typeManager; private final FunctionManager functionManager; + private final Tracer tracer; @Inject public PlannerContext(Metadata metadata, TypeOperators typeOperators, BlockEncodingSerde blockEncodingSerde, TypeManager typeManager, - FunctionManager functionManager) + FunctionManager functionManager, + Tracer tracer) { this.metadata = requireNonNull(metadata, "metadata is null"); this.typeOperators = requireNonNull(typeOperators, "typeOperators is null"); this.blockEncodingSerde = requireNonNull(blockEncodingSerde, "blockEncodingSerde is null"); this.typeManager = requireNonNull(typeManager, "typeManager is null"); this.functionManager = requireNonNull(functionManager, "functionManager is null"); + this.tracer = requireNonNull(tracer, "tracer is null"); } public Metadata getMetadata() @@ -78,4 +82,9 @@ public FunctionManager getFunctionManager() { return functionManager; } + + public Tracer getTracer() + { + return tracer; + } } diff --git a/core/trino-main/src/main/java/io/trino/sql/analyzer/Analyzer.java b/core/trino-main/src/main/java/io/trino/sql/analyzer/Analyzer.java index c04f8e20bc8b..f08161ff853a 100644 --- a/core/trino-main/src/main/java/io/trino/sql/analyzer/Analyzer.java +++ b/core/trino-main/src/main/java/io/trino/sql/analyzer/Analyzer.java @@ -15,6 +15,9 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; import io.trino.Session; import io.trino.execution.querystats.PlanOptimizersStatsCollector; import io.trino.execution.warnings.WarningCollector; @@ -37,6 +40,7 @@ import static io.trino.sql.analyzer.ExpressionTreeUtils.extractWindowExpressions; import static io.trino.sql.analyzer.QueryType.OTHERS; import static io.trino.sql.analyzer.SemanticExceptions.semanticException; +import static io.trino.tracing.ScopedSpan.scopedSpan; import static java.util.Objects.requireNonNull; public class Analyzer @@ -47,7 +51,8 @@ public class Analyzer private final List parameters; private final Map, Expression> parameterLookup; private final WarningCollector warningCollector; - private PlanOptimizersStatsCollector planOptimizersStatsCollector; + private final PlanOptimizersStatsCollector planOptimizersStatsCollector; + private final Tracer tracer; private final StatementRewrite statementRewrite; Analyzer( @@ -58,6 +63,7 @@ public class Analyzer Map, Expression> parameterLookup, WarningCollector warningCollector, PlanOptimizersStatsCollector planOptimizersStatsCollector, + Tracer tracer, StatementRewrite statementRewrite) { this.session = requireNonNull(session, "session is null"); @@ -67,12 +73,18 @@ public class Analyzer this.parameterLookup = parameterLookup; this.warningCollector = requireNonNull(warningCollector, "warningCollector is null"); this.planOptimizersStatsCollector = requireNonNull(planOptimizersStatsCollector, "planOptimizersStatsCollector is null"); + this.tracer = requireNonNull(tracer, "tracer is null"); this.statementRewrite = requireNonNull(statementRewrite, "statementRewrite is null"); } public Analysis analyze(Statement statement) { - return analyze(statement, OTHERS); + Span span = tracer.spanBuilder("analyzer") + .setParent(Context.current().with(session.getQuerySpan())) + .startSpan(); + try (var ignored = scopedSpan(span)) { + return analyze(statement, OTHERS); + } } public Analysis analyze(Statement statement, QueryType queryType) @@ -80,15 +92,21 @@ public Analysis analyze(Statement statement, QueryType queryType) Statement rewrittenStatement = statementRewrite.rewrite(analyzerFactory, session, statement, parameters, parameterLookup, warningCollector, planOptimizersStatsCollector); Analysis analysis = new Analysis(rewrittenStatement, parameterLookup, queryType); StatementAnalyzer analyzer = statementAnalyzerFactory.createStatementAnalyzer(analysis, session, warningCollector, CorrelationSupport.ALLOWED); - analyzer.analyze(rewrittenStatement, Optional.empty()); - // check column access permissions for each table - analysis.getTableColumnReferences().forEach((accessControlInfo, tableColumnReferences) -> - tableColumnReferences.forEach((tableName, columns) -> - accessControlInfo.getAccessControl().checkCanSelectFromColumns( - accessControlInfo.getSecurityContext(session.getRequiredTransactionId(), session.getQueryId()), - tableName, - columns))); + try (var ignored = scopedSpan(tracer, "analyze")) { + analyzer.analyze(rewrittenStatement, Optional.empty()); + } + + try (var ignored = scopedSpan(tracer, "access-control")) { + // check column access permissions for each table + analysis.getTableColumnReferences().forEach((accessControlInfo, tableColumnReferences) -> + tableColumnReferences.forEach((tableName, columns) -> + accessControlInfo.getAccessControl().checkCanSelectFromColumns( + accessControlInfo.getSecurityContext(session.getRequiredTransactionId(), session.getQueryId()), + tableName, + columns))); + } + return analysis; } diff --git a/core/trino-main/src/main/java/io/trino/sql/analyzer/AnalyzerFactory.java b/core/trino-main/src/main/java/io/trino/sql/analyzer/AnalyzerFactory.java index 354605386e8c..b5b8c49c8d19 100644 --- a/core/trino-main/src/main/java/io/trino/sql/analyzer/AnalyzerFactory.java +++ b/core/trino-main/src/main/java/io/trino/sql/analyzer/AnalyzerFactory.java @@ -13,6 +13,7 @@ */ package io.trino.sql.analyzer; +import io.opentelemetry.api.trace.Tracer; import io.trino.Session; import io.trino.execution.querystats.PlanOptimizersStatsCollector; import io.trino.execution.warnings.WarningCollector; @@ -32,12 +33,14 @@ public class AnalyzerFactory { private final StatementAnalyzerFactory statementAnalyzerFactory; private final StatementRewrite statementRewrite; + private final Tracer tracer; @Inject - public AnalyzerFactory(StatementAnalyzerFactory statementAnalyzerFactory, StatementRewrite statementRewrite) + public AnalyzerFactory(StatementAnalyzerFactory statementAnalyzerFactory, StatementRewrite statementRewrite, Tracer tracer) { this.statementAnalyzerFactory = requireNonNull(statementAnalyzerFactory, "statementAnalyzerFactory is null"); this.statementRewrite = requireNonNull(statementRewrite, "statementRewrite is null"); + this.tracer = requireNonNull(tracer, "tracer is null"); } public Analyzer createAnalyzer( @@ -55,6 +58,7 @@ public Analyzer createAnalyzer( parameterLookup, warningCollector, planOptimizersStatsCollector, + tracer, statementRewrite); } } diff --git a/core/trino-main/src/main/java/io/trino/sql/planner/LocalExecutionPlanner.java b/core/trino-main/src/main/java/io/trino/sql/planner/LocalExecutionPlanner.java index 1b4a60db51b5..92be1ac482ef 100644 --- a/core/trino-main/src/main/java/io/trino/sql/planner/LocalExecutionPlanner.java +++ b/core/trino-main/src/main/java/io/trino/sql/planner/LocalExecutionPlanner.java @@ -689,7 +689,7 @@ public void addDriverFactory(boolean inputDriver, boolean outputDriver, Physical else { operatorFactories = toOperatorFactories(operatorFactoriesWithTypes); } - driverFactories.add(new DriverFactory(getNextPipelineId(), inputDriver, outputDriver, operatorFactories, driverInstances)); + addDriverFactory(inputDriver, outputDriver, operatorFactories, driverInstances); } private List handleLateMaterialization(List operatorFactories) diff --git a/core/trino-main/src/main/java/io/trino/sql/planner/LogicalPlanner.java b/core/trino-main/src/main/java/io/trino/sql/planner/LogicalPlanner.java index 6b11d3e1c224..f78ce7aa1bcd 100644 --- a/core/trino-main/src/main/java/io/trino/sql/planner/LogicalPlanner.java +++ b/core/trino-main/src/main/java/io/trino/sql/planner/LogicalPlanner.java @@ -15,7 +15,11 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.errorprone.annotations.MustBeClosed; import io.airlift.log.Logger; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanBuilder; +import io.opentelemetry.context.Context; import io.trino.Session; import io.trino.cost.CachingCostProvider; import io.trino.cost.CachingStatsProvider; @@ -55,6 +59,7 @@ import io.trino.sql.analyzer.RelationType; import io.trino.sql.analyzer.Scope; import io.trino.sql.planner.StatisticsAggregationPlanner.TableStatisticAggregation; +import io.trino.sql.planner.iterative.IterativeOptimizer; import io.trino.sql.planner.optimizations.PlanOptimizer; import io.trino.sql.planner.plan.Assignments; import io.trino.sql.planner.plan.ExplainAnalyzeNode; @@ -99,9 +104,13 @@ import io.trino.sql.tree.Table; import io.trino.sql.tree.TableExecute; import io.trino.sql.tree.Update; +import io.trino.tracing.ScopedSpan; +import io.trino.tracing.TrinoAttributes; import io.trino.type.TypeCoercion; import io.trino.type.UnknownType; +import javax.annotation.Nonnull; + import java.util.AbstractMap.SimpleImmutableEntry; import java.util.ArrayList; import java.util.HashMap; @@ -149,6 +158,7 @@ import static io.trino.sql.planner.sanity.PlanSanityChecker.DISTRIBUTED_PLAN_SANITY_CHECKER; import static io.trino.sql.tree.BooleanLiteral.TRUE_LITERAL; import static io.trino.sql.tree.ComparisonExpression.Operator.GREATER_THAN_OR_EQUAL; +import static io.trino.tracing.ScopedSpan.scopedSpan; import static java.lang.String.format; import static java.util.Objects.requireNonNull; @@ -230,7 +240,10 @@ public Plan plan(Analysis analysis, Stage stage) public Plan plan(Analysis analysis, Stage stage, boolean collectPlanStatistics) { - PlanNode root = planStatement(analysis, analysis.getStatement()); + PlanNode root; + try (var ignored = scopedSpan(plannerContext.getTracer(), "plan")) { + root = planStatement(analysis, analysis.getStatement()); + } if (LOG.isDebugEnabled()) { LOG.debug("Initial plan:\n%s", PlanPrinter.textLogicalPlan( @@ -244,34 +257,25 @@ public Plan plan(Analysis analysis, Stage stage, boolean collectPlanStatistics) false)); } - planSanityChecker.validateIntermediatePlan(root, session, plannerContext, typeAnalyzer, symbolAllocator.getTypes(), warningCollector); + try (var ignored = scopedSpan(plannerContext.getTracer(), "validate-intermediate")) { + planSanityChecker.validateIntermediatePlan(root, session, plannerContext, typeAnalyzer, symbolAllocator.getTypes(), warningCollector); + } TableStatsProvider tableStatsProvider = new CachingTableStatsProvider(metadata, session); if (stage.ordinal() >= OPTIMIZED.ordinal()) { - for (PlanOptimizer optimizer : planOptimizers) { - root = optimizer.optimize(root, session, symbolAllocator.getTypes(), symbolAllocator, idAllocator, warningCollector, planOptimizersStatsCollector, tableStatsProvider); - if (root == null) { - throw new NullPointerException(optimizer.getClass().getName() + " returned a null plan"); - } - - if (LOG.isDebugEnabled()) { - LOG.debug("%s:\n%s", optimizer.getClass().getName(), PlanPrinter.textLogicalPlan( - root, - symbolAllocator.getTypes(), - metadata, - plannerContext.getFunctionManager(), - StatsAndCosts.empty(), - session, - 0, - false)); + try (var ignored = scopedSpan(plannerContext.getTracer(), "optimizer")) { + for (PlanOptimizer optimizer : planOptimizers) { + root = runOptimizer(root, tableStatsProvider, optimizer); } } } if (stage.ordinal() >= OPTIMIZED_AND_VALIDATED.ordinal()) { // make sure we produce a valid plan after optimizations run. This is mainly to catch programming errors - planSanityChecker.validateFinalPlan(root, session, plannerContext, typeAnalyzer, symbolAllocator.getTypes(), warningCollector); + try (var ignored = scopedSpan(plannerContext.getTracer(), "validate-final")) { + planSanityChecker.validateFinalPlan(root, session, plannerContext, typeAnalyzer, symbolAllocator.getTypes(), warningCollector); + } } TypeProvider types = symbolAllocator.getTypes(); @@ -280,11 +284,55 @@ public Plan plan(Analysis analysis, Stage stage, boolean collectPlanStatistics) if (collectPlanStatistics) { StatsProvider statsProvider = new CachingStatsProvider(statsCalculator, session, types, tableStatsProvider); CostProvider costProvider = new CachingCostProvider(costCalculator, statsProvider, Optional.empty(), session, types); - statsAndCosts = StatsAndCosts.create(root, statsProvider, costProvider); + try (var ignored = scopedSpan(plannerContext.getTracer(), "plan-stats")) { + statsAndCosts = StatsAndCosts.create(root, statsProvider, costProvider); + } } return new Plan(root, types, statsAndCosts); } + @Nonnull + private PlanNode runOptimizer(PlanNode root, TableStatsProvider tableStatsProvider, PlanOptimizer optimizer) + { + PlanNode result; + try (var ignored = optimizerSpan(optimizer)) { + result = optimizer.optimize(root, session, symbolAllocator.getTypes(), symbolAllocator, idAllocator, warningCollector, planOptimizersStatsCollector, tableStatsProvider); + } + if (result == null) { + throw new NullPointerException(optimizer.getClass().getName() + " returned a null plan"); + } + + if (LOG.isDebugEnabled()) { + LOG.debug("%s:\n%s", optimizer.getClass().getName(), PlanPrinter.textLogicalPlan( + result, + symbolAllocator.getTypes(), + metadata, + plannerContext.getFunctionManager(), + StatsAndCosts.empty(), + session, + 0, + false)); + } + + return result; + } + + @MustBeClosed + private ScopedSpan optimizerSpan(PlanOptimizer optimizer) + { + if (!Span.fromContext(Context.current()).isRecording()) { + return null; + } + SpanBuilder builder = plannerContext.getTracer().spanBuilder("optimize") + .setAttribute(TrinoAttributes.OPTIMIZER_NAME, optimizer.getClass().getSimpleName()); + if (optimizer instanceof IterativeOptimizer iterative) { + builder.setAttribute(TrinoAttributes.OPTIMIZER_RULES, iterative.getRules().stream() + .map(x -> x.getClass().getSimpleName()) + .toList()); + } + return scopedSpan(builder.startSpan()); + } + public PlanNode planStatement(Analysis analysis, Statement statement) { if ((statement instanceof CreateTableAsSelect && analysis.getCreate().orElseThrow().isCreateTableAsSelectNoOp()) || diff --git a/core/trino-main/src/main/java/io/trino/sql/planner/SplitSourceFactory.java b/core/trino-main/src/main/java/io/trino/sql/planner/SplitSourceFactory.java index e47e2eae0eab..7b76175a4a5c 100644 --- a/core/trino-main/src/main/java/io/trino/sql/planner/SplitSourceFactory.java +++ b/core/trino-main/src/main/java/io/trino/sql/planner/SplitSourceFactory.java @@ -16,6 +16,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import io.airlift.log.Logger; +import io.opentelemetry.api.trace.Span; import io.trino.Session; import io.trino.server.DynamicFilterService; import io.trino.spi.connector.Constraint; @@ -100,13 +101,13 @@ public SplitSourceFactory(SplitManager splitManager, PlannerContext plannerConte this.typeAnalyzer = requireNonNull(typeAnalyzer, "typeAnalyzer is null"); } - public Map createSplitSources(Session session, PlanFragment fragment) + public Map createSplitSources(Session session, Span stageSpan, PlanFragment fragment) { ImmutableList.Builder allSplitSources = ImmutableList.builder(); try { // get splits for this fragment, this is lazy so split assignments aren't actually calculated here return fragment.getRoot().accept( - new Visitor(session, TypeProvider.copyOf(fragment.getSymbols()), allSplitSources), + new Visitor(session, stageSpan, TypeProvider.copyOf(fragment.getSymbols()), allSplitSources), null); } catch (Throwable t) { @@ -129,15 +130,18 @@ private final class Visitor extends PlanVisitor, Void> { private final Session session; + private final Span stageSpan; private final TypeProvider typeProvider; private final ImmutableList.Builder splitSources; private Visitor( Session session, + Span stageSpan, TypeProvider typeProvider, ImmutableList.Builder allSplitSources) { this.session = session; + this.stageSpan = stageSpan; this.typeProvider = typeProvider; this.splitSources = allSplitSources; } @@ -179,6 +183,7 @@ private Map visitScanAndFilter(TableScanNode node, Opti // get dataSource for table SplitSource splitSource = splitManager.getSplits( session, + stageSpan, node.getTable(), dynamicFilter, constraint); @@ -311,7 +316,7 @@ public Map visitTableFunctionProcessor(TableFunctionPro { if (node.getSource().isEmpty()) { // this is a source node, so produce splits - SplitSource splitSource = splitManager.getSplits(session, node.getHandle()); + SplitSource splitSource = splitManager.getSplits(session, stageSpan, node.getHandle()); splitSources.add(splitSource); return ImmutableMap.of(node.getId(), splitSource); diff --git a/core/trino-main/src/main/java/io/trino/sql/planner/iterative/rule/ExtractSpatialJoins.java b/core/trino-main/src/main/java/io/trino/sql/planner/iterative/rule/ExtractSpatialJoins.java index a8c44e663fb0..5e50315660dd 100644 --- a/core/trino-main/src/main/java/io/trino/sql/planner/iterative/rule/ExtractSpatialJoins.java +++ b/core/trino-main/src/main/java/io/trino/sql/planner/iterative/rule/ExtractSpatialJoins.java @@ -81,7 +81,6 @@ import static io.trino.matching.Capture.newCapture; import static io.trino.spi.StandardErrorCode.INVALID_SPATIAL_PARTITIONING; import static io.trino.spi.connector.Constraint.alwaysTrue; -import static io.trino.spi.connector.DynamicFilter.EMPTY; import static io.trino.spi.type.DoubleType.DOUBLE; import static io.trino.spi.type.IntegerType.INTEGER; import static io.trino.spi.type.VarcharType.VARCHAR; @@ -467,7 +466,7 @@ private static KdbTree loadKdbTree(String tableName, Session session, Metadata m ColumnHandle kdbTreeColumn = Iterables.getOnlyElement(visibleColumnHandles); Optional kdbTree = Optional.empty(); - try (SplitSource splitSource = splitManager.getSplits(session, tableHandle, EMPTY, alwaysTrue())) { + try (SplitSource splitSource = splitManager.getSplits(session, session.getQuerySpan(), tableHandle, DynamicFilter.EMPTY, alwaysTrue())) { while (!Thread.currentThread().isInterrupted()) { SplitBatch splitBatch = getFutureValue(splitSource.getNextBatch(1000)); List splits = splitBatch.getSplits(); diff --git a/core/trino-main/src/main/java/io/trino/testing/LocalQueryRunner.java b/core/trino-main/src/main/java/io/trino/testing/LocalQueryRunner.java index 4866141e1226..2ed2ebaf2f99 100644 --- a/core/trino-main/src/main/java/io/trino/testing/LocalQueryRunner.java +++ b/core/trino-main/src/main/java/io/trino/testing/LocalQueryRunner.java @@ -20,6 +20,9 @@ import io.airlift.node.NodeInfo; import io.airlift.units.DataSize; import io.airlift.units.Duration; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; import io.trino.FeaturesConfig; import io.trino.Session; import io.trino.SystemSessionProperties; @@ -228,6 +231,7 @@ import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static io.airlift.concurrent.MoreFutures.getFutureValue; import static io.airlift.concurrent.Threads.daemonThreadsNamed; +import static io.airlift.tracing.Tracing.noopTracer; import static io.trino.connector.CatalogServiceProviderModule.createAccessControlProvider; import static io.trino.connector.CatalogServiceProviderModule.createAnalyzePropertyManager; import static io.trino.connector.CatalogServiceProviderModule.createColumnPropertyManager; @@ -345,6 +349,7 @@ private LocalQueryRunner( requireNonNull(defaultSessionProperties, "defaultSessionProperties is null"); checkArgument(defaultSession.getTransactionId().isEmpty() || !withInitialTransaction, "Already in transaction"); + Tracer tracer = noopTracer(); this.taskManagerConfig = new TaskManagerConfig().setTaskConcurrency(4); requireNonNull(nodeSpillConfig, "nodeSpillConfig is null"); this.maxSpillPerNode = nodeSpillConfig.getMaxSpillPerNode(); @@ -405,10 +410,11 @@ private LocalQueryRunner( pageIndexerFactory, nodeInfo, testingVersionEmbedder(), + OpenTelemetry.noop(), transactionManager, typeManager, nodeSchedulerConfig)); - this.splitManager = new SplitManager(createSplitManagerProvider(catalogManager), new QueryManagerConfig()); + this.splitManager = new SplitManager(createSplitManagerProvider(catalogManager), tracer, new QueryManagerConfig()); this.pageSourceManager = new PageSourceManager(createPageSourceProvider(catalogManager)); this.pageSinkManager = new PageSinkManager(createPageSinkProvider(catalogManager)); this.indexManager = new IndexManager(createIndexProvider(catalogManager)); @@ -432,7 +438,7 @@ private LocalQueryRunner( new JsonValueFunction(functionManager, metadata, typeManager), new JsonQueryFunction(functionManager, metadata, typeManager))); - this.plannerContext = new PlannerContext(metadata, typeOperators, blockEncodingSerde, typeManager, functionManager); + this.plannerContext = new PlannerContext(metadata, typeOperators, blockEncodingSerde, typeManager, functionManager, tracer); this.pageFunctionCompiler = new PageFunctionCompiler(functionManager, 0); this.expressionCompiler = new ExpressionCompiler(functionManager, pageFunctionCompiler); this.joinFilterFunctionCompiler = new JoinFilterFunctionCompiler(functionManager); @@ -497,6 +503,7 @@ private LocalQueryRunner( Optional transactionId = withInitialTransaction ? Optional.of(transactionManager.beginTransaction(true)) : defaultSession.getTransactionId(); this.defaultSession = new Session( defaultSession.getQueryId(), + Span.getInvalid(), transactionId, defaultSession.isClientTransactionSupport(), defaultSession.getIdentity(), @@ -1018,6 +1025,7 @@ private List createDrivers(Session session, Plan plan, OutputFactory out SplitSource splitSource = splitManager.getSplits( session, + Span.getInvalid(), table, EMPTY, alwaysTrue()); @@ -1171,7 +1179,8 @@ private AnalyzerFactory createAnalyzerFactory(QueryExplainerFactory queryExplain tablePropertyManager, materializedViewPropertyManager), new ShowStatsRewrite(plannerContext.getMetadata(), queryExplainerFactory, statsCalculator), - new ExplainRewrite(queryExplainerFactory, new QueryPreparer(sqlParser))))); + new ExplainRewrite(queryExplainerFactory, new QueryPreparer(sqlParser)))), + plannerContext.getTracer()); } private static List getNextBatch(SplitSource splitSource) diff --git a/core/trino-main/src/main/java/io/trino/testing/TestingConnectorContext.java b/core/trino-main/src/main/java/io/trino/testing/TestingConnectorContext.java index 5d6c093c2c85..f70ce499db14 100644 --- a/core/trino-main/src/main/java/io/trino/testing/TestingConnectorContext.java +++ b/core/trino-main/src/main/java/io/trino/testing/TestingConnectorContext.java @@ -13,6 +13,9 @@ */ package io.trino.testing; +import io.airlift.tracing.Tracing; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.Tracer; import io.trino.connector.ConnectorAwareNodeManager; import io.trino.metadata.InMemoryNodeManager; import io.trino.operator.GroupByHashPageIndexerFactory; @@ -49,6 +52,18 @@ public TestingConnectorContext() nodeManager = new ConnectorAwareNodeManager(new InMemoryNodeManager(), "testenv", TEST_CATALOG_HANDLE, true); } + @Override + public OpenTelemetry getOpenTelemetry() + { + return OpenTelemetry.noop(); + } + + @Override + public Tracer getTracer() + { + return Tracing.noopTracer(); + } + @Override public NodeManager getNodeManager() { diff --git a/core/trino-main/src/main/java/io/trino/tracing/ForTracing.java b/core/trino-main/src/main/java/io/trino/tracing/ForTracing.java new file mode 100644 index 000000000000..569ee086e787 --- /dev/null +++ b/core/trino-main/src/main/java/io/trino/tracing/ForTracing.java @@ -0,0 +1,29 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.tracing; + +import javax.inject.Qualifier; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Retention(RUNTIME) +@Target({FIELD, PARAMETER, METHOD}) +@Qualifier +public @interface ForTracing {} diff --git a/core/trino-main/src/main/java/io/trino/tracing/ScopedSpan.java b/core/trino-main/src/main/java/io/trino/tracing/ScopedSpan.java new file mode 100644 index 000000000000..1b544b2eb99d --- /dev/null +++ b/core/trino-main/src/main/java/io/trino/tracing/ScopedSpan.java @@ -0,0 +1,58 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.tracing; + +import com.google.errorprone.annotations.MustBeClosed; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Scope; + +import static java.util.Objects.requireNonNull; + +public final class ScopedSpan + implements AutoCloseable +{ + private final Span span; + private final Scope scope; + + @SuppressWarnings("MustBeClosedChecker") + private ScopedSpan(Span span) + { + this.span = requireNonNull(span, "span is null"); + this.scope = span.makeCurrent(); + } + + @Override + public void close() + { + try { + scope.close(); + } + finally { + span.end(); + } + } + + @MustBeClosed + public static ScopedSpan scopedSpan(Tracer tracer, String name) + { + return scopedSpan(tracer.spanBuilder(name).startSpan()); + } + + @MustBeClosed + public static ScopedSpan scopedSpan(Span span) + { + return new ScopedSpan(span); + } +} diff --git a/core/trino-main/src/main/java/io/trino/tracing/TracingConnectorMetadata.java b/core/trino-main/src/main/java/io/trino/tracing/TracingConnectorMetadata.java new file mode 100644 index 000000000000..600c8a014909 --- /dev/null +++ b/core/trino-main/src/main/java/io/trino/tracing/TracingConnectorMetadata.java @@ -0,0 +1,1280 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.tracing; + +import io.airlift.slice.Slice; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.trino.spi.connector.AggregateFunction; +import io.trino.spi.connector.AggregationApplicationResult; +import io.trino.spi.connector.BeginTableExecuteResult; +import io.trino.spi.connector.CatalogSchemaName; +import io.trino.spi.connector.CatalogSchemaTableName; +import io.trino.spi.connector.ColumnHandle; +import io.trino.spi.connector.ColumnMetadata; +import io.trino.spi.connector.ConnectorAnalyzeMetadata; +import io.trino.spi.connector.ConnectorInsertTableHandle; +import io.trino.spi.connector.ConnectorMaterializedViewDefinition; +import io.trino.spi.connector.ConnectorMergeTableHandle; +import io.trino.spi.connector.ConnectorMetadata; +import io.trino.spi.connector.ConnectorOutputMetadata; +import io.trino.spi.connector.ConnectorOutputTableHandle; +import io.trino.spi.connector.ConnectorPartitioningHandle; +import io.trino.spi.connector.ConnectorResolvedIndex; +import io.trino.spi.connector.ConnectorSession; +import io.trino.spi.connector.ConnectorTableExecuteHandle; +import io.trino.spi.connector.ConnectorTableHandle; +import io.trino.spi.connector.ConnectorTableLayout; +import io.trino.spi.connector.ConnectorTableMetadata; +import io.trino.spi.connector.ConnectorTableProperties; +import io.trino.spi.connector.ConnectorTableSchema; +import io.trino.spi.connector.ConnectorTableVersion; +import io.trino.spi.connector.ConnectorViewDefinition; +import io.trino.spi.connector.Constraint; +import io.trino.spi.connector.ConstraintApplicationResult; +import io.trino.spi.connector.JoinApplicationResult; +import io.trino.spi.connector.JoinCondition; +import io.trino.spi.connector.JoinStatistics; +import io.trino.spi.connector.JoinType; +import io.trino.spi.connector.LimitApplicationResult; +import io.trino.spi.connector.MaterializedViewFreshness; +import io.trino.spi.connector.ProjectionApplicationResult; +import io.trino.spi.connector.RetryMode; +import io.trino.spi.connector.RowChangeParadigm; +import io.trino.spi.connector.SampleApplicationResult; +import io.trino.spi.connector.SampleType; +import io.trino.spi.connector.SchemaTableName; +import io.trino.spi.connector.SchemaTablePrefix; +import io.trino.spi.connector.SortItem; +import io.trino.spi.connector.SystemTable; +import io.trino.spi.connector.TableColumnsMetadata; +import io.trino.spi.connector.TableFunctionApplicationResult; +import io.trino.spi.connector.TableScanRedirectApplicationResult; +import io.trino.spi.connector.TopNApplicationResult; +import io.trino.spi.expression.ConnectorExpression; +import io.trino.spi.function.AggregationFunctionMetadata; +import io.trino.spi.function.BoundSignature; +import io.trino.spi.function.FunctionDependencyDeclaration; +import io.trino.spi.function.FunctionId; +import io.trino.spi.function.FunctionMetadata; +import io.trino.spi.function.SchemaFunctionName; +import io.trino.spi.predicate.TupleDomain; +import io.trino.spi.ptf.ConnectorTableFunctionHandle; +import io.trino.spi.security.GrantInfo; +import io.trino.spi.security.Privilege; +import io.trino.spi.security.RoleGrant; +import io.trino.spi.security.TrinoPrincipal; +import io.trino.spi.statistics.ComputedStatistics; +import io.trino.spi.statistics.TableStatistics; +import io.trino.spi.statistics.TableStatisticsMetadata; +import io.trino.spi.type.Type; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.OptionalInt; +import java.util.OptionalLong; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +import static io.airlift.tracing.Tracing.attribute; +import static io.trino.tracing.ScopedSpan.scopedSpan; +import static java.util.Objects.requireNonNull; + +public class TracingConnectorMetadata + implements ConnectorMetadata +{ + private final Tracer tracer; + private final String catalogName; + private final ConnectorMetadata delegate; + + public TracingConnectorMetadata(Tracer tracer, String catalogName, ConnectorMetadata delegate) + { + this.tracer = requireNonNull(tracer, "tracer is null"); + this.catalogName = requireNonNull(catalogName, "catalogName is null"); + this.delegate = requireNonNull(delegate, "delegate is null"); + } + + @Override + public boolean schemaExists(ConnectorSession session, String schemaName) + { + Span span = startSpan("schemaExists", schemaName); + try (var ignored = scopedSpan(span)) { + return delegate.schemaExists(session, schemaName); + } + } + + @Override + public List listSchemaNames(ConnectorSession session) + { + Span span = startSpan("listSchemaNames"); + try (var ignored = scopedSpan(span)) { + return delegate.listSchemaNames(session); + } + } + + @Override + public ConnectorTableHandle getTableHandle(ConnectorSession session, SchemaTableName tableName) + { + Span span = startSpan("getTableHandle", tableName); + try (var ignored = scopedSpan(span)) { + return delegate.getTableHandle(session, tableName); + } + } + + @Override + public ConnectorTableHandle getTableHandle(ConnectorSession session, SchemaTableName tableName, Optional startVersion, Optional endVersion) + { + Span span = startSpan("getTableHandle", tableName); + try (var ignored = scopedSpan(span)) { + return delegate.getTableHandle(session, tableName, startVersion, endVersion); + } + } + + @Override + public Optional getTableHandleForExecute(ConnectorSession session, ConnectorTableHandle tableHandle, String procedureName, Map executeProperties, RetryMode retryMode) + { + Span span = startSpan("getTableHandleForExecute", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.getTableHandleForExecute(session, tableHandle, procedureName, executeProperties, retryMode); + } + } + + @Override + public Optional getLayoutForTableExecute(ConnectorSession session, ConnectorTableExecuteHandle tableExecuteHandle) + { + Span span = startSpan("getLayoutForTableExecute", tableExecuteHandle); + try (var ignored = scopedSpan(span)) { + return delegate.getLayoutForTableExecute(session, tableExecuteHandle); + } + } + + @Override + public BeginTableExecuteResult beginTableExecute(ConnectorSession session, ConnectorTableExecuteHandle tableExecuteHandle, ConnectorTableHandle updatedSourceTableHandle) + { + Span span = startSpan("beginTableExecute", tableExecuteHandle); + try (var ignored = scopedSpan(span)) { + return delegate.beginTableExecute(session, tableExecuteHandle, updatedSourceTableHandle); + } + } + + @Override + public void finishTableExecute(ConnectorSession session, ConnectorTableExecuteHandle tableExecuteHandle, Collection fragments, List tableExecuteState) + { + Span span = startSpan("finishTableExecute", tableExecuteHandle); + try (var ignored = scopedSpan(span)) { + delegate.finishTableExecute(session, tableExecuteHandle, fragments, tableExecuteState); + } + } + + @Override + public void executeTableExecute(ConnectorSession session, ConnectorTableExecuteHandle tableExecuteHandle) + { + Span span = startSpan("executeTableExecute", tableExecuteHandle); + try (var ignored = scopedSpan(span)) { + delegate.executeTableExecute(session, tableExecuteHandle); + } + } + + @Override + public Optional getSystemTable(ConnectorSession session, SchemaTableName tableName) + { + Span span = startSpan("getSystemTable", tableName); + try (var ignored = scopedSpan(span)) { + return delegate.getSystemTable(session, tableName); + } + } + + @Override + public ConnectorTableHandle makeCompatiblePartitioning(ConnectorSession session, ConnectorTableHandle tableHandle, ConnectorPartitioningHandle partitioningHandle) + { + Span span = startSpan("makeCompatiblePartitioning", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.makeCompatiblePartitioning(session, tableHandle, partitioningHandle); + } + } + + @Override + public Optional getCommonPartitioningHandle(ConnectorSession session, ConnectorPartitioningHandle left, ConnectorPartitioningHandle right) + { + Span span = startSpan("getCommonPartitioning"); + try (var ignored = scopedSpan(span)) { + return delegate.getCommonPartitioningHandle(session, left, right); + } + } + + @SuppressWarnings("deprecation") + @Override + public SchemaTableName getSchemaTableName(ConnectorSession session, ConnectorTableHandle table) + { + Span span = startSpan("getSchemaTableName", table); + try (var ignored = scopedSpan(span)) { + return delegate.getSchemaTableName(session, table); + } + } + + @Override + public ConnectorTableSchema getTableSchema(ConnectorSession session, ConnectorTableHandle table) + { + Span span = startSpan("getTableSchema", table); + try (var ignored = scopedSpan(span)) { + return delegate.getTableSchema(session, table); + } + } + + @Override + public ConnectorTableMetadata getTableMetadata(ConnectorSession session, ConnectorTableHandle table) + { + Span span = startSpan("getTableMetadata", table); + try (var ignored = scopedSpan(span)) { + return delegate.getTableMetadata(session, table); + } + } + + @Override + public Optional getInfo(ConnectorTableHandle table) + { + Span span = startSpan("getInfo", table); + try (var ignored = scopedSpan(span)) { + return delegate.getInfo(table); + } + } + + @Override + public List listTables(ConnectorSession session, Optional schemaName) + { + Span span = startSpan("listTables"); + try (var ignored = scopedSpan(span)) { + return delegate.listTables(session, schemaName); + } + } + + @Override + public Map getColumnHandles(ConnectorSession session, ConnectorTableHandle tableHandle) + { + Span span = startSpan("getColumnHandles", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.getColumnHandles(session, tableHandle); + } + } + + @Override + public ColumnMetadata getColumnMetadata(ConnectorSession session, ConnectorTableHandle tableHandle, ColumnHandle columnHandle) + { + Span span = startSpan("getColumnMetadata", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.getColumnMetadata(session, tableHandle, columnHandle); + } + } + + @SuppressWarnings("deprecation") + @Override + public Map> listTableColumns(ConnectorSession session, SchemaTablePrefix prefix) + { + Span span = startSpan("listTableColumns", prefix); + try (var ignored = scopedSpan(span)) { + return delegate.listTableColumns(session, prefix); + } + } + + @Override + public Iterator streamTableColumns(ConnectorSession session, SchemaTablePrefix prefix) + { + Span span = startSpan("streamTableColumns", prefix); + try (var ignored = scopedSpan(span)) { + return delegate.streamTableColumns(session, prefix); + } + } + + @Override + public TableStatistics getTableStatistics(ConnectorSession session, ConnectorTableHandle tableHandle) + { + Span span = startSpan("getTableStatistics", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.getTableStatistics(session, tableHandle); + } + } + + @Override + public void createSchema(ConnectorSession session, String schemaName, Map properties, TrinoPrincipal owner) + { + Span span = startSpan("createSchema", schemaName); + try (var ignored = scopedSpan(span)) { + delegate.createSchema(session, schemaName, properties, owner); + } + } + + @Override + public void dropSchema(ConnectorSession session, String schemaName) + { + Span span = startSpan("dropSchema", schemaName); + try (var ignored = scopedSpan(span)) { + delegate.dropSchema(session, schemaName); + } + } + + @Override + public void renameSchema(ConnectorSession session, String source, String target) + { + Span span = startSpan("renameSchema", source); + try (var ignored = scopedSpan(span)) { + delegate.renameSchema(session, source, target); + } + } + + @Override + public void setSchemaAuthorization(ConnectorSession session, String schemaName, TrinoPrincipal principal) + { + Span span = startSpan("setSchemaAuthorization", schemaName); + try (var ignored = scopedSpan(span)) { + delegate.setSchemaAuthorization(session, schemaName, principal); + } + } + + @Override + public void createTable(ConnectorSession session, ConnectorTableMetadata tableMetadata, boolean ignoreExisting) + { + Span span = startSpan("createTable", tableMetadata.getTable()); + try (var ignored = scopedSpan(span)) { + delegate.createTable(session, tableMetadata, ignoreExisting); + } + } + + @Override + public void dropTable(ConnectorSession session, ConnectorTableHandle tableHandle) + { + Span span = startSpan("dropTable", tableHandle); + try (var ignored = scopedSpan(span)) { + delegate.dropTable(session, tableHandle); + } + } + + @Override + public void truncateTable(ConnectorSession session, ConnectorTableHandle tableHandle) + { + Span span = startSpan("truncateTable", tableHandle); + try (var ignored = scopedSpan(span)) { + delegate.truncateTable(session, tableHandle); + } + } + + @Override + public void renameTable(ConnectorSession session, ConnectorTableHandle tableHandle, SchemaTableName newTableName) + { + Span span = startSpan("renameTable", tableHandle); + try (var ignored = scopedSpan(span)) { + delegate.renameTable(session, tableHandle, newTableName); + } + } + + @Override + public void setTableProperties(ConnectorSession session, ConnectorTableHandle tableHandle, Map> properties) + { + Span span = startSpan("setTableProperties", tableHandle); + try (var ignored = scopedSpan(span)) { + delegate.setTableProperties(session, tableHandle, properties); + } + } + + @Override + public void setTableComment(ConnectorSession session, ConnectorTableHandle tableHandle, Optional comment) + { + Span span = startSpan("setTableComment", tableHandle); + try (var ignored = scopedSpan(span)) { + delegate.setTableComment(session, tableHandle, comment); + } + } + + @Override + public void setViewComment(ConnectorSession session, SchemaTableName viewName, Optional comment) + { + Span span = startSpan("setViewComment", viewName); + try (var ignored = scopedSpan(span)) { + delegate.setViewComment(session, viewName, comment); + } + } + + @Override + public void setViewColumnComment(ConnectorSession session, SchemaTableName viewName, String columnName, Optional comment) + { + Span span = startSpan("setViewColumnComment", viewName); + try (var ignored = scopedSpan(span)) { + delegate.setViewColumnComment(session, viewName, columnName, comment); + } + } + + @Override + public void setColumnComment(ConnectorSession session, ConnectorTableHandle tableHandle, ColumnHandle column, Optional comment) + { + Span span = startSpan("setColumnComment", tableHandle); + try (var ignored = scopedSpan(span)) { + delegate.setColumnComment(session, tableHandle, column, comment); + } + } + + @Override + public void addColumn(ConnectorSession session, ConnectorTableHandle tableHandle, ColumnMetadata column) + { + Span span = startSpan("addColumn", tableHandle); + try (var ignored = scopedSpan(span)) { + delegate.addColumn(session, tableHandle, column); + } + } + + @Override + public void setColumnType(ConnectorSession session, ConnectorTableHandle tableHandle, ColumnHandle column, Type type) + { + Span span = startSpan("setColumnType", tableHandle); + try (var ignored = scopedSpan(span)) { + delegate.setColumnType(session, tableHandle, column, type); + } + } + + @Override + public void setTableAuthorization(ConnectorSession session, SchemaTableName tableName, TrinoPrincipal principal) + { + Span span = startSpan("setTableAuthorization", tableName); + try (var ignored = scopedSpan(span)) { + delegate.setTableAuthorization(session, tableName, principal); + } + } + + @Override + public void renameColumn(ConnectorSession session, ConnectorTableHandle tableHandle, ColumnHandle source, String target) + { + Span span = startSpan("renameColumn", tableHandle); + try (var ignored = scopedSpan(span)) { + delegate.renameColumn(session, tableHandle, source, target); + } + } + + @Override + public void dropColumn(ConnectorSession session, ConnectorTableHandle tableHandle, ColumnHandle column) + { + Span span = startSpan("dropColumn", tableHandle); + try (var ignored = scopedSpan(span)) { + delegate.dropColumn(session, tableHandle, column); + } + } + + @Override + public void dropField(ConnectorSession session, ConnectorTableHandle tableHandle, ColumnHandle column, List fieldPath) + { + Span span = startSpan("dropField", tableHandle); + try (var ignored = scopedSpan(span)) { + delegate.dropField(session, tableHandle, column, fieldPath); + } + } + + @Override + public Optional getNewTableLayout(ConnectorSession session, ConnectorTableMetadata tableMetadata) + { + Span span = startSpan("getNewTableLayout", tableMetadata.getTable()); + try (var ignored = scopedSpan(span)) { + return delegate.getNewTableLayout(session, tableMetadata); + } + } + + @Override + public Optional getInsertLayout(ConnectorSession session, ConnectorTableHandle tableHandle) + { + Span span = startSpan("getInsertLayout", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.getInsertLayout(session, tableHandle); + } + } + + @Override + public TableStatisticsMetadata getStatisticsCollectionMetadataForWrite(ConnectorSession session, ConnectorTableMetadata tableMetadata) + { + Span span = startSpan("getStatisticsCollectionMetadataForWrite", tableMetadata.getTable()); + try (var ignored = scopedSpan(span)) { + return delegate.getStatisticsCollectionMetadataForWrite(session, tableMetadata); + } + } + + @Override + public ConnectorAnalyzeMetadata getStatisticsCollectionMetadata(ConnectorSession session, ConnectorTableHandle tableHandle, Map analyzeProperties) + { + Span span = startSpan("getStatisticsCollectionMetadata", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.getStatisticsCollectionMetadata(session, tableHandle, analyzeProperties); + } + } + + @Override + public ConnectorTableHandle beginStatisticsCollection(ConnectorSession session, ConnectorTableHandle tableHandle) + { + Span span = startSpan("beginStatisticsCollection", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.beginStatisticsCollection(session, tableHandle); + } + } + + @Override + public void finishStatisticsCollection(ConnectorSession session, ConnectorTableHandle tableHandle, Collection computedStatistics) + { + Span span = startSpan("finishStatisticsCollection", tableHandle); + try (var ignored = scopedSpan(span)) { + delegate.finishStatisticsCollection(session, tableHandle, computedStatistics); + } + } + + @Override + public ConnectorOutputTableHandle beginCreateTable(ConnectorSession session, ConnectorTableMetadata tableMetadata, Optional layout, RetryMode retryMode) + { + Span span = startSpan("beginCreateTable", tableMetadata.getTable()); + try (var ignored = scopedSpan(span)) { + return delegate.beginCreateTable(session, tableMetadata, layout, retryMode); + } + } + + @Override + public Optional finishCreateTable(ConnectorSession session, ConnectorOutputTableHandle tableHandle, Collection fragments, Collection computedStatistics) + { + Span span = startSpan("finishCreateTable"); + if (span.isRecording()) { + span.setAttribute(TrinoAttributes.HANDLE, tableHandle.toString()); + } + try (var ignored = scopedSpan(span)) { + return delegate.finishCreateTable(session, tableHandle, fragments, computedStatistics); + } + } + + @Override + public void beginQuery(ConnectorSession session) + { + Span span = startSpan("beginQuery"); + try (var ignored = scopedSpan(span)) { + delegate.beginQuery(session); + } + } + + @Override + public void cleanupQuery(ConnectorSession session) + { + Span span = startSpan("cleanupQuery"); + try (var ignored = scopedSpan(span)) { + delegate.cleanupQuery(session); + } + } + + @Override + public ConnectorInsertTableHandle beginInsert(ConnectorSession session, ConnectorTableHandle tableHandle, List columns, RetryMode retryMode) + { + Span span = startSpan("beginInsert", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.beginInsert(session, tableHandle, columns, retryMode); + } + } + + @Override + public boolean supportsMissingColumnsOnInsert() + { + Span span = startSpan("supportsMissingColumnsOnInsert"); + try (var ignored = scopedSpan(span)) { + return delegate.supportsMissingColumnsOnInsert(); + } + } + + @Override + public Optional finishInsert(ConnectorSession session, ConnectorInsertTableHandle insertHandle, Collection fragments, Collection computedStatistics) + { + Span span = startSpan("finishInsert"); + if (span.isRecording()) { + span.setAttribute(TrinoAttributes.HANDLE, insertHandle.toString()); + } + try (var ignored = scopedSpan(span)) { + return delegate.finishInsert(session, insertHandle, fragments, computedStatistics); + } + } + + @Override + public boolean delegateMaterializedViewRefreshToConnector(ConnectorSession session, SchemaTableName viewName) + { + Span span = startSpan("delegateMaterializedViewRefreshToConnector", viewName); + try (var ignored = scopedSpan(span)) { + return delegate.delegateMaterializedViewRefreshToConnector(session, viewName); + } + } + + @Override + public CompletableFuture refreshMaterializedView(ConnectorSession session, SchemaTableName viewName) + { + Span span = startSpan("refreshMaterializedView", viewName); + try (var ignored = scopedSpan(span)) { + return delegate.refreshMaterializedView(session, viewName); + } + } + + @Override + public ConnectorInsertTableHandle beginRefreshMaterializedView(ConnectorSession session, ConnectorTableHandle tableHandle, List sourceTableHandles, RetryMode retryMode) + { + Span span = startSpan("beginRefreshMaterializedView", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.beginRefreshMaterializedView(session, tableHandle, sourceTableHandles, retryMode); + } + } + + @Override + public Optional finishRefreshMaterializedView(ConnectorSession session, ConnectorTableHandle tableHandle, ConnectorInsertTableHandle insertHandle, Collection fragments, Collection computedStatistics, List sourceTableHandles) + { + Span span = startSpan("finishRefreshMaterializedView", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.finishRefreshMaterializedView(session, tableHandle, insertHandle, fragments, computedStatistics, sourceTableHandles); + } + } + + @Override + public RowChangeParadigm getRowChangeParadigm(ConnectorSession session, ConnectorTableHandle tableHandle) + { + Span span = startSpan("getRowChangeParadigm", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.getRowChangeParadigm(session, tableHandle); + } + } + + @Override + public ColumnHandle getMergeRowIdColumnHandle(ConnectorSession session, ConnectorTableHandle tableHandle) + { + Span span = startSpan("getMergeRowIdColumnHandle", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.getMergeRowIdColumnHandle(session, tableHandle); + } + } + + @Override + public Optional getUpdateLayout(ConnectorSession session, ConnectorTableHandle tableHandle) + { + Span span = startSpan("getUpdateLayout", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.getUpdateLayout(session, tableHandle); + } + } + + @Override + public ConnectorMergeTableHandle beginMerge(ConnectorSession session, ConnectorTableHandle tableHandle, RetryMode retryMode) + { + Span span = startSpan("beginMerge", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.beginMerge(session, tableHandle, retryMode); + } + } + + @Override + public void finishMerge(ConnectorSession session, ConnectorMergeTableHandle tableHandle, Collection fragments, Collection computedStatistics) + { + Span span = startSpan("finishMerge", tableHandle.getTableHandle()); + try (var ignored = scopedSpan(span)) { + delegate.finishMerge(session, tableHandle, fragments, computedStatistics); + } + } + + @Override + public void createView(ConnectorSession session, SchemaTableName viewName, ConnectorViewDefinition definition, boolean replace) + { + Span span = startSpan("createView", viewName); + try (var ignored = scopedSpan(span)) { + delegate.createView(session, viewName, definition, replace); + } + } + + @Override + public void renameView(ConnectorSession session, SchemaTableName source, SchemaTableName target) + { + Span span = startSpan("renameView", source); + try (var ignored = scopedSpan(span)) { + delegate.renameView(session, source, target); + } + } + + @Override + public void setViewAuthorization(ConnectorSession session, SchemaTableName viewName, TrinoPrincipal principal) + { + Span span = startSpan("setViewAuthorization", viewName); + try (var ignored = scopedSpan(span)) { + delegate.setViewAuthorization(session, viewName, principal); + } + } + + @Override + public void dropView(ConnectorSession session, SchemaTableName viewName) + { + Span span = startSpan("dropView", viewName); + try (var ignored = scopedSpan(span)) { + delegate.dropView(session, viewName); + } + } + + @Override + public List listViews(ConnectorSession session, Optional schemaName) + { + Span span = startSpan("listViews", schemaName); + try (var ignored = scopedSpan(span)) { + return delegate.listViews(session, schemaName); + } + } + + @Override + public Map getViews(ConnectorSession session, Optional schemaName) + { + Span span = startSpan("getViews", schemaName); + try (var ignored = scopedSpan(span)) { + return delegate.getViews(session, schemaName); + } + } + + @Override + public Optional getView(ConnectorSession session, SchemaTableName viewName) + { + Span span = startSpan("getView", viewName); + try (var ignored = scopedSpan(span)) { + return delegate.getView(session, viewName); + } + } + + @SuppressWarnings("removal") + @Override + public Map getSchemaProperties(ConnectorSession session, CatalogSchemaName schemaName) + { + Span span = startSpan("getSchemaProperties", schemaName.getSchemaName()); + try (var ignored = scopedSpan(span)) { + return delegate.getSchemaProperties(session, schemaName); + } + } + + @Override + public Map getSchemaProperties(ConnectorSession session, String schemaName) + { + Span span = startSpan("getSchemaProperties", schemaName); + try (var ignored = scopedSpan(span)) { + return delegate.getSchemaProperties(session, schemaName); + } + } + + @SuppressWarnings("removal") + @Override + public Optional getSchemaOwner(ConnectorSession session, CatalogSchemaName schemaName) + { + Span span = startSpan("getSchemaOwner", schemaName.getSchemaName()); + try (var ignored = scopedSpan(span)) { + return delegate.getSchemaOwner(session, schemaName); + } + } + + @Override + public Optional getSchemaOwner(ConnectorSession session, String schemaName) + { + Span span = startSpan("getSchemaOwner", schemaName); + try (var ignored = scopedSpan(span)) { + return delegate.getSchemaOwner(session, schemaName); + } + } + + @Override + public Optional applyDelete(ConnectorSession session, ConnectorTableHandle handle) + { + Span span = startSpan("applyDelete", handle); + try (var ignored = scopedSpan(span)) { + return delegate.applyDelete(session, handle); + } + } + + @Override + public OptionalLong executeDelete(ConnectorSession session, ConnectorTableHandle handle) + { + Span span = startSpan("executeDelete", handle); + try (var ignored = scopedSpan(span)) { + return delegate.executeDelete(session, handle); + } + } + + @Override + public Optional resolveIndex(ConnectorSession session, ConnectorTableHandle tableHandle, Set indexableColumns, Set outputColumns, TupleDomain tupleDomain) + { + Span span = startSpan("resolveIndex", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.resolveIndex(session, tableHandle, indexableColumns, outputColumns, tupleDomain); + } + } + + @Override + public Collection listFunctions(ConnectorSession session, String schemaName) + { + Span span = startSpan("listFunctions", schemaName); + try (var ignored = scopedSpan(span)) { + return delegate.listFunctions(session, schemaName); + } + } + + @Override + public Collection getFunctions(ConnectorSession session, SchemaFunctionName name) + { + Span span = startSpan("getFunctions", name.getSchemaName()) + .setAttribute(TrinoAttributes.FUNCTION, name.getFunctionName()); + try (var ignored = scopedSpan(span)) { + return delegate.getFunctions(session, name); + } + } + + @Override + public FunctionMetadata getFunctionMetadata(ConnectorSession session, FunctionId functionId) + { + Span span = startSpan("getFunctionMetadata", functionId); + try (var ignored = scopedSpan(span)) { + return delegate.getFunctionMetadata(session, functionId); + } + } + + @Override + public AggregationFunctionMetadata getAggregationFunctionMetadata(ConnectorSession session, FunctionId functionId) + { + Span span = startSpan("getAggregationFunctionMetadata", functionId); + try (var ignored = scopedSpan(span)) { + return delegate.getAggregationFunctionMetadata(session, functionId); + } + } + + @Override + public FunctionDependencyDeclaration getFunctionDependencies(ConnectorSession session, FunctionId functionId, BoundSignature boundSignature) + { + Span span = startSpan("getFunctionDependencies", functionId); + try (var ignored = scopedSpan(span)) { + return delegate.getFunctionDependencies(session, functionId, boundSignature); + } + } + + @Override + public boolean roleExists(ConnectorSession session, String role) + { + Span span = startSpan("roleExists"); + try (var ignored = scopedSpan(span)) { + return delegate.roleExists(session, role); + } + } + + @Override + public void createRole(ConnectorSession session, String role, Optional grantor) + { + Span span = startSpan("createRole"); + try (var ignored = scopedSpan(span)) { + delegate.createRole(session, role, grantor); + } + } + + @Override + public void dropRole(ConnectorSession session, String role) + { + Span span = startSpan("dropRole"); + try (var ignored = scopedSpan(span)) { + delegate.dropRole(session, role); + } + } + + @Override + public Set listRoles(ConnectorSession session) + { + Span span = startSpan("listRoles"); + try (var ignored = scopedSpan(span)) { + return delegate.listRoles(session); + } + } + + @Override + public Set listRoleGrants(ConnectorSession session, TrinoPrincipal principal) + { + Span span = startSpan("listRoleGrants"); + try (var ignored = scopedSpan(span)) { + return delegate.listRoleGrants(session, principal); + } + } + + @Override + public void grantRoles(ConnectorSession connectorSession, Set roles, Set grantees, boolean adminOption, Optional grantor) + { + Span span = startSpan("grantRoles"); + try (var ignored = scopedSpan(span)) { + delegate.grantRoles(connectorSession, roles, grantees, adminOption, grantor); + } + } + + @Override + public void revokeRoles(ConnectorSession connectorSession, Set roles, Set grantees, boolean adminOption, Optional grantor) + { + Span span = startSpan("revokeRoles"); + try (var ignored = scopedSpan(span)) { + delegate.revokeRoles(connectorSession, roles, grantees, adminOption, grantor); + } + } + + @Override + public Set listApplicableRoles(ConnectorSession session, TrinoPrincipal principal) + { + Span span = startSpan("listApplicableRoles"); + try (var ignored = scopedSpan(span)) { + return delegate.listApplicableRoles(session, principal); + } + } + + @Override + public Set listEnabledRoles(ConnectorSession session) + { + Span span = startSpan("listEnabledRoles"); + try (var ignored = scopedSpan(span)) { + return delegate.listEnabledRoles(session); + } + } + + @Override + public void grantSchemaPrivileges(ConnectorSession session, String schemaName, Set privileges, TrinoPrincipal grantee, boolean grantOption) + { + Span span = startSpan("grantSchemaPrivileges", schemaName); + try (var ignored = scopedSpan(span)) { + delegate.grantSchemaPrivileges(session, schemaName, privileges, grantee, grantOption); + } + } + + @Override + public void denySchemaPrivileges(ConnectorSession session, String schemaName, Set privileges, TrinoPrincipal grantee) + { + Span span = startSpan("denySchemaPrivileges", schemaName); + try (var ignored = scopedSpan(span)) { + delegate.denySchemaPrivileges(session, schemaName, privileges, grantee); + } + } + + @Override + public void revokeSchemaPrivileges(ConnectorSession session, String schemaName, Set privileges, TrinoPrincipal grantee, boolean grantOption) + { + Span span = startSpan("revokeSchemaPrivileges", schemaName); + try (var ignored = scopedSpan(span)) { + delegate.revokeSchemaPrivileges(session, schemaName, privileges, grantee, grantOption); + } + } + + @Override + public void grantTablePrivileges(ConnectorSession session, SchemaTableName tableName, Set privileges, TrinoPrincipal grantee, boolean grantOption) + { + Span span = startSpan("grantTablePrivileges", tableName); + try (var ignored = scopedSpan(span)) { + delegate.grantTablePrivileges(session, tableName, privileges, grantee, grantOption); + } + } + + @Override + public void denyTablePrivileges(ConnectorSession session, SchemaTableName tableName, Set privileges, TrinoPrincipal grantee) + { + Span span = startSpan("denyTablePrivileges", tableName); + try (var ignored = scopedSpan(span)) { + delegate.denyTablePrivileges(session, tableName, privileges, grantee); + } + } + + @Override + public void revokeTablePrivileges(ConnectorSession session, SchemaTableName tableName, Set privileges, TrinoPrincipal grantee, boolean grantOption) + { + Span span = startSpan("revokeTablePrivileges", tableName); + try (var ignored = scopedSpan(span)) { + delegate.revokeTablePrivileges(session, tableName, privileges, grantee, grantOption); + } + } + + @Override + public List listTablePrivileges(ConnectorSession session, SchemaTablePrefix prefix) + { + Span span = startSpan("listTablePrivileges", prefix); + try (var ignored = scopedSpan(span)) { + return delegate.listTablePrivileges(session, prefix); + } + } + + @Override + public ConnectorTableProperties getTableProperties(ConnectorSession session, ConnectorTableHandle table) + { + Span span = startSpan("getTableProperties", table); + try (var ignored = scopedSpan(span)) { + return delegate.getTableProperties(session, table); + } + } + + @Override + public Optional> applyLimit(ConnectorSession session, ConnectorTableHandle handle, long limit) + { + Span span = startSpan("applyLimit", handle); + try (var ignored = scopedSpan(span)) { + return delegate.applyLimit(session, handle, limit); + } + } + + @Override + public Optional> applyFilter(ConnectorSession session, ConnectorTableHandle handle, Constraint constraint) + { + Span span = startSpan("applyFilter", handle); + try (var ignored = scopedSpan(span)) { + return delegate.applyFilter(session, handle, constraint); + } + } + + @Override + public Optional> applyProjection(ConnectorSession session, ConnectorTableHandle handle, List projections, Map assignments) + { + Span span = startSpan("applyProjection", handle); + try (var ignored = scopedSpan(span)) { + return delegate.applyProjection(session, handle, projections, assignments); + } + } + + @Override + public Optional> applySample(ConnectorSession session, ConnectorTableHandle handle, SampleType sampleType, double sampleRatio) + { + Span span = startSpan("applySample", handle); + try (var ignored = scopedSpan(span)) { + return delegate.applySample(session, handle, sampleType, sampleRatio); + } + } + + @Override + public Optional> applyAggregation(ConnectorSession session, ConnectorTableHandle handle, List aggregates, Map assignments, List> groupingSets) + { + Span span = startSpan("applyAggregation", handle); + try (var ignored = scopedSpan(span)) { + return delegate.applyAggregation(session, handle, aggregates, assignments, groupingSets); + } + } + + @Override + public Optional> applyJoin(ConnectorSession session, JoinType joinType, ConnectorTableHandle left, ConnectorTableHandle right, ConnectorExpression joinCondition, Map leftAssignments, Map rightAssignments, JoinStatistics statistics) + { + Span span = startSpan("applyJoin"); + try (var ignored = scopedSpan(span)) { + return delegate.applyJoin(session, joinType, left, right, joinCondition, leftAssignments, rightAssignments, statistics); + } + } + + @SuppressWarnings("deprecation") + @Override + public Optional> applyJoin(ConnectorSession session, JoinType joinType, ConnectorTableHandle left, ConnectorTableHandle right, List joinConditions, Map leftAssignments, Map rightAssignments, JoinStatistics statistics) + { + Span span = startSpan("applyJoin"); + try (var ignored = scopedSpan(span)) { + return delegate.applyJoin(session, joinType, left, right, joinConditions, leftAssignments, rightAssignments, statistics); + } + } + + @Override + public Optional> applyTopN(ConnectorSession session, ConnectorTableHandle handle, long topNCount, List sortItems, Map assignments) + { + Span span = startSpan("applyTopN", handle); + try (var ignored = scopedSpan(span)) { + return delegate.applyTopN(session, handle, topNCount, sortItems, assignments); + } + } + + @Override + public Optional> applyTableFunction(ConnectorSession session, ConnectorTableFunctionHandle handle) + { + Span span = startSpan("applyTableFunction"); + try (var ignored = scopedSpan(span)) { + return delegate.applyTableFunction(session, handle); + } + } + + @Override + public void validateScan(ConnectorSession session, ConnectorTableHandle handle) + { + Span span = startSpan("validateScan", handle); + try (var ignored = scopedSpan(span)) { + delegate.validateScan(session, handle); + } + } + + @Override + public void createMaterializedView(ConnectorSession session, SchemaTableName viewName, ConnectorMaterializedViewDefinition definition, boolean replace, boolean ignoreExisting) + { + Span span = startSpan("createMaterializedView", viewName); + try (var ignored = scopedSpan(span)) { + delegate.createMaterializedView(session, viewName, definition, replace, ignoreExisting); + } + } + + @Override + public void dropMaterializedView(ConnectorSession session, SchemaTableName viewName) + { + Span span = startSpan("dropMaterializedView", viewName); + try (var ignored = scopedSpan(span)) { + delegate.dropMaterializedView(session, viewName); + } + } + + @Override + public List listMaterializedViews(ConnectorSession session, Optional schemaName) + { + Span span = startSpan("listMaterializedViews", schemaName); + try (var ignored = scopedSpan(span)) { + return delegate.listMaterializedViews(session, schemaName); + } + } + + @Override + public Map getMaterializedViews(ConnectorSession session, Optional schemaName) + { + Span span = startSpan("getMaterializedViews", schemaName); + try (var ignored = scopedSpan(span)) { + return delegate.getMaterializedViews(session, schemaName); + } + } + + @Override + public Optional getMaterializedView(ConnectorSession session, SchemaTableName viewName) + { + Span span = startSpan("getMaterializedView", viewName); + try (var ignored = scopedSpan(span)) { + return delegate.getMaterializedView(session, viewName); + } + } + + @Override + public MaterializedViewFreshness getMaterializedViewFreshness(ConnectorSession session, SchemaTableName name) + { + Span span = startSpan("getMaterializedViewFreshness", name); + try (var ignored = scopedSpan(span)) { + return delegate.getMaterializedViewFreshness(session, name); + } + } + + @Override + public void renameMaterializedView(ConnectorSession session, SchemaTableName source, SchemaTableName target) + { + Span span = startSpan("renameMaterializedView", source); + try (var ignored = scopedSpan(span)) { + delegate.renameMaterializedView(session, source, target); + } + } + + @Override + public void setMaterializedViewProperties(ConnectorSession session, SchemaTableName viewName, Map> properties) + { + Span span = startSpan("setMaterializedViewProperties", viewName); + try (var ignored = scopedSpan(span)) { + delegate.setMaterializedViewProperties(session, viewName, properties); + } + } + + @Override + public Optional applyTableScanRedirect(ConnectorSession session, ConnectorTableHandle tableHandle) + { + Span span = startSpan("applyTableScanRedirect", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.applyTableScanRedirect(session, tableHandle); + } + } + + @Override + public Optional redirectTable(ConnectorSession session, SchemaTableName tableName) + { + Span span = startSpan("redirectTable", tableName); + try (var ignored = scopedSpan(span)) { + return delegate.redirectTable(session, tableName); + } + } + + @Override + public boolean supportsReportingWrittenBytes(ConnectorSession session, SchemaTableName schemaTableName, Map tableProperties) + { + Span span = startSpan("supportsReportingWrittenBytes", schemaTableName); + try (var ignored = scopedSpan(span)) { + return delegate.supportsReportingWrittenBytes(session, schemaTableName, tableProperties); + } + } + + @Override + public boolean supportsReportingWrittenBytes(ConnectorSession session, ConnectorTableHandle connectorTableHandle) + { + Span span = startSpan("supportsReportingWrittenBytes", connectorTableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.supportsReportingWrittenBytes(session, connectorTableHandle); + } + } + + @Override + public OptionalInt getMaxWriterTasks(ConnectorSession session) + { + Span span = startSpan("getMaxWriterTasks"); + try (var ignored = scopedSpan(span)) { + return delegate.getMaxWriterTasks(session); + } + } + + private Span startSpan(String methodName) + { + return tracer.spanBuilder("ConnectorMetadata." + methodName) + .setAttribute(TrinoAttributes.CATALOG, catalogName) + .startSpan(); + } + + private Span startSpan(String methodName, String schemaName) + { + return startSpan(methodName) + .setAttribute(TrinoAttributes.SCHEMA, schemaName); + } + + private Span startSpan(String methodName, Optional schemaName) + { + return startSpan(methodName) + .setAllAttributes(attribute(TrinoAttributes.SCHEMA, schemaName)); + } + + private Span startSpan(String methodName, SchemaTableName table) + { + return startSpan(methodName) + .setAttribute(TrinoAttributes.SCHEMA, table.getSchemaName()) + .setAttribute(TrinoAttributes.TABLE, table.getTableName()); + } + + private Span startSpan(String methodName, SchemaTablePrefix prefix) + { + return startSpan(methodName) + .setAllAttributes(attribute(TrinoAttributes.SCHEMA, prefix.getSchema())) + .setAllAttributes(attribute(TrinoAttributes.TABLE, prefix.getTable())); + } + + private Span startSpan(String methodName, ConnectorTableHandle handle) + { + Span span = startSpan(methodName); + if (span.isRecording()) { + span.setAttribute(TrinoAttributes.HANDLE, handle.toString()); + } + return span; + } + + private Span startSpan(String methodName, ConnectorTableExecuteHandle handle) + { + Span span = startSpan(methodName); + if (span.isRecording()) { + span.setAttribute(TrinoAttributes.HANDLE, handle.toString()); + } + return span; + } + + private Span startSpan(String methodName, FunctionId functionId) + { + Span span = startSpan(methodName); + if (span.isRecording()) { + span.setAttribute(TrinoAttributes.FUNCTION, functionId.toString()); + } + return span; + } +} diff --git a/core/trino-main/src/main/java/io/trino/tracing/TracingMetadata.java b/core/trino-main/src/main/java/io/trino/tracing/TracingMetadata.java new file mode 100644 index 000000000000..f8e09503bace --- /dev/null +++ b/core/trino-main/src/main/java/io/trino/tracing/TracingMetadata.java @@ -0,0 +1,1400 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.tracing; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.util.concurrent.ListenableFuture; +import io.airlift.slice.Slice; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.trino.Session; +import io.trino.metadata.AnalyzeMetadata; +import io.trino.metadata.AnalyzeTableHandle; +import io.trino.metadata.CatalogInfo; +import io.trino.metadata.InsertTableHandle; +import io.trino.metadata.MaterializedViewDefinition; +import io.trino.metadata.MergeHandle; +import io.trino.metadata.Metadata; +import io.trino.metadata.OperatorNotFoundException; +import io.trino.metadata.OutputTableHandle; +import io.trino.metadata.QualifiedObjectName; +import io.trino.metadata.QualifiedTablePrefix; +import io.trino.metadata.RedirectionAwareTableHandle; +import io.trino.metadata.ResolvedFunction; +import io.trino.metadata.ResolvedIndex; +import io.trino.metadata.TableExecuteHandle; +import io.trino.metadata.TableFunctionHandle; +import io.trino.metadata.TableHandle; +import io.trino.metadata.TableLayout; +import io.trino.metadata.TableMetadata; +import io.trino.metadata.TableProperties; +import io.trino.metadata.TableSchema; +import io.trino.metadata.TableVersion; +import io.trino.metadata.ViewDefinition; +import io.trino.metadata.ViewInfo; +import io.trino.spi.connector.AggregateFunction; +import io.trino.spi.connector.AggregationApplicationResult; +import io.trino.spi.connector.BeginTableExecuteResult; +import io.trino.spi.connector.CatalogHandle; +import io.trino.spi.connector.CatalogSchemaName; +import io.trino.spi.connector.CatalogSchemaTableName; +import io.trino.spi.connector.ColumnHandle; +import io.trino.spi.connector.ColumnMetadata; +import io.trino.spi.connector.ConnectorCapabilities; +import io.trino.spi.connector.ConnectorOutputMetadata; +import io.trino.spi.connector.ConnectorTableMetadata; +import io.trino.spi.connector.Constraint; +import io.trino.spi.connector.ConstraintApplicationResult; +import io.trino.spi.connector.JoinApplicationResult; +import io.trino.spi.connector.JoinStatistics; +import io.trino.spi.connector.JoinType; +import io.trino.spi.connector.LimitApplicationResult; +import io.trino.spi.connector.MaterializedViewFreshness; +import io.trino.spi.connector.ProjectionApplicationResult; +import io.trino.spi.connector.RowChangeParadigm; +import io.trino.spi.connector.SampleApplicationResult; +import io.trino.spi.connector.SampleType; +import io.trino.spi.connector.SortItem; +import io.trino.spi.connector.SystemTable; +import io.trino.spi.connector.TableColumnsMetadata; +import io.trino.spi.connector.TableFunctionApplicationResult; +import io.trino.spi.connector.TableScanRedirectApplicationResult; +import io.trino.spi.connector.TopNApplicationResult; +import io.trino.spi.expression.ConnectorExpression; +import io.trino.spi.function.AggregationFunctionMetadata; +import io.trino.spi.function.FunctionMetadata; +import io.trino.spi.function.OperatorType; +import io.trino.spi.predicate.TupleDomain; +import io.trino.spi.security.GrantInfo; +import io.trino.spi.security.Identity; +import io.trino.spi.security.Privilege; +import io.trino.spi.security.RoleGrant; +import io.trino.spi.security.TrinoPrincipal; +import io.trino.spi.statistics.ComputedStatistics; +import io.trino.spi.statistics.TableStatistics; +import io.trino.spi.statistics.TableStatisticsMetadata; +import io.trino.spi.type.Type; +import io.trino.sql.analyzer.TypeSignatureProvider; +import io.trino.sql.planner.PartitioningHandle; +import io.trino.sql.tree.QualifiedName; + +import javax.inject.Inject; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.OptionalInt; +import java.util.OptionalLong; +import java.util.Set; + +import static io.airlift.tracing.Tracing.attribute; +import static io.trino.tracing.ScopedSpan.scopedSpan; +import static java.util.Objects.requireNonNull; + +public class TracingMetadata + implements Metadata +{ + private final Tracer tracer; + private final Metadata delegate; + + @Inject + public TracingMetadata(Tracer tracer, @ForTracing Metadata delegate) + { + this.delegate = requireNonNull(delegate, "delegate is null"); + this.tracer = requireNonNull(tracer, "tracer is null"); + } + + @VisibleForTesting + public Metadata getDelegate() + { + return delegate; + } + + @Override + public Set getConnectorCapabilities(Session session, CatalogHandle catalogHandle) + { + Span span = startSpan("getConnectorCapabilities", catalogHandle.getCatalogName()); + try (var ignored = scopedSpan(span)) { + return delegate.getConnectorCapabilities(session, catalogHandle); + } + } + + @Override + public boolean catalogExists(Session session, String catalogName) + { + Span span = startSpan("catalogExists", catalogName); + try (var ignored = scopedSpan(span)) { + return delegate.catalogExists(session, catalogName); + } + } + + @Override + public boolean schemaExists(Session session, CatalogSchemaName schema) + { + Span span = startSpan("schemaExists", schema); + try (var ignored = scopedSpan(span)) { + return delegate.schemaExists(session, schema); + } + } + + @Override + public List listSchemaNames(Session session, String catalogName) + { + Span span = startSpan("listSchemaNames", catalogName); + try (var ignored = scopedSpan(span)) { + return delegate.listSchemaNames(session, catalogName); + } + } + + @Override + public Optional getTableHandle(Session session, QualifiedObjectName tableName) + { + Span span = startSpan("getTableHandle", tableName); + try (var ignored = scopedSpan(span)) { + return delegate.getTableHandle(session, tableName); + } + } + + @Override + public Optional getSystemTable(Session session, QualifiedObjectName tableName) + { + Span span = startSpan("getSystemTable", tableName); + try (var ignored = scopedSpan(span)) { + return delegate.getSystemTable(session, tableName); + } + } + + @Override + public Optional getTableHandleForExecute(Session session, TableHandle tableHandle, String procedureName, Map executeProperties) + { + Span span = startSpan("getTableHandleForExecute", tableHandle) + .setAttribute(TrinoAttributes.PROCEDURE, procedureName); + try (var ignored = scopedSpan(span)) { + return delegate.getTableHandleForExecute(session, tableHandle, procedureName, executeProperties); + } + } + + @Override + public Optional getLayoutForTableExecute(Session session, TableExecuteHandle tableExecuteHandle) + { + Span span = startSpan("getLayoutForTableExecute", tableExecuteHandle); + try (var ignored = scopedSpan(span)) { + return delegate.getLayoutForTableExecute(session, tableExecuteHandle); + } + } + + @Override + public BeginTableExecuteResult beginTableExecute(Session session, TableExecuteHandle handle, TableHandle updatedSourceTableHandle) + { + Span span = startSpan("beginTableExecute", handle); + try (var ignored = scopedSpan(span)) { + return delegate.beginTableExecute(session, handle, updatedSourceTableHandle); + } + } + + @Override + public void finishTableExecute(Session session, TableExecuteHandle handle, Collection fragments, List tableExecuteState) + { + Span span = startSpan("finishTableExecute", handle); + try (var ignored = scopedSpan(span)) { + delegate.finishTableExecute(session, handle, fragments, tableExecuteState); + } + } + + @Override + public void executeTableExecute(Session session, TableExecuteHandle handle) + { + Span span = startSpan("executeTableExecute", handle); + try (var ignored = scopedSpan(span)) { + delegate.executeTableExecute(session, handle); + } + } + + @Override + public TableProperties getTableProperties(Session session, TableHandle handle) + { + Span span = startSpan("getTableProperties", handle); + try (var ignored = scopedSpan(span)) { + return delegate.getTableProperties(session, handle); + } + } + + @Override + public TableHandle makeCompatiblePartitioning(Session session, TableHandle table, PartitioningHandle partitioningHandle) + { + Span span = startSpan("makeCompatiblePartitioning", table); + try (var ignored = scopedSpan(span)) { + return delegate.makeCompatiblePartitioning(session, table, partitioningHandle); + } + } + + @Override + public Optional getCommonPartitioning(Session session, PartitioningHandle left, PartitioningHandle right) + { + Span span = startSpan("getCommonPartitioning"); + if (span.isRecording() && left.getCatalogHandle().equals(right.getCatalogHandle()) && left.getCatalogHandle().isPresent()) { + span.setAttribute(TrinoAttributes.CATALOG, left.getCatalogHandle().get().getCatalogName()); + } + try (var ignored = scopedSpan(span)) { + return delegate.getCommonPartitioning(session, left, right); + } + } + + @Override + public Optional getInfo(Session session, TableHandle handle) + { + Span span = startSpan("getInfo", handle); + try (var ignored = scopedSpan(span)) { + return delegate.getInfo(session, handle); + } + } + + @Override + public TableSchema getTableSchema(Session session, TableHandle tableHandle) + { + Span span = startSpan("getTableSchema", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.getTableSchema(session, tableHandle); + } + } + + @Override + public TableMetadata getTableMetadata(Session session, TableHandle tableHandle) + { + Span span = startSpan("getTableMetadata", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.getTableMetadata(session, tableHandle); + } + } + + @Override + public TableStatistics getTableStatistics(Session session, TableHandle tableHandle) + { + Span span = startSpan("getTableStatistics", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.getTableStatistics(session, tableHandle); + } + } + + @Override + public List listTables(Session session, QualifiedTablePrefix prefix) + { + Span span = startSpan("listTables", prefix); + try (var ignored = scopedSpan(span)) { + return delegate.listTables(session, prefix); + } + } + + @Override + public Map getColumnHandles(Session session, TableHandle tableHandle) + { + Span span = startSpan("getColumnHandles", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.getColumnHandles(session, tableHandle); + } + } + + @Override + public ColumnMetadata getColumnMetadata(Session session, TableHandle tableHandle, ColumnHandle columnHandle) + { + Span span = startSpan("getColumnMetadata", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.getColumnMetadata(session, tableHandle, columnHandle); + } + } + + @Override + public List listTableColumns(Session session, QualifiedTablePrefix prefix) + { + Span span = startSpan("listTableColumns", prefix); + try (var ignored = scopedSpan(span)) { + return delegate.listTableColumns(session, prefix); + } + } + + @Override + public void createSchema(Session session, CatalogSchemaName schema, Map properties, TrinoPrincipal principal) + { + Span span = startSpan("createSchema", schema); + try (var ignored = scopedSpan(span)) { + delegate.createSchema(session, schema, properties, principal); + } + } + + @Override + public void dropSchema(Session session, CatalogSchemaName schema) + { + Span span = startSpan("dropSchema", schema); + try (var ignored = scopedSpan(span)) { + delegate.dropSchema(session, schema); + } + } + + @Override + public void renameSchema(Session session, CatalogSchemaName source, String target) + { + Span span = startSpan("renameSchema", source); + try (var ignored = scopedSpan(span)) { + delegate.renameSchema(session, source, target); + } + } + + @Override + public void setSchemaAuthorization(Session session, CatalogSchemaName source, TrinoPrincipal principal) + { + Span span = startSpan("setSchemaAuthorization", source); + try (var ignored = scopedSpan(span)) { + delegate.setSchemaAuthorization(session, source, principal); + } + } + + @Override + public void createTable(Session session, String catalogName, ConnectorTableMetadata tableMetadata, boolean ignoreExisting) + { + Span span = startSpan("createTable", catalogName, tableMetadata); + try (var ignored = scopedSpan(span)) { + delegate.createTable(session, catalogName, tableMetadata, ignoreExisting); + } + } + + @Override + public void renameTable(Session session, TableHandle tableHandle, CatalogSchemaTableName currentTableName, QualifiedObjectName newTableName) + { + Span span = startSpan("renameTable", currentTableName); + try (var ignored = scopedSpan(span)) { + delegate.renameTable(session, tableHandle, currentTableName, newTableName); + } + } + + @Override + public void setTableProperties(Session session, TableHandle tableHandle, Map> properties) + { + Span span = startSpan("setTableProperties", tableHandle); + try (var ignored = scopedSpan(span)) { + delegate.setTableProperties(session, tableHandle, properties); + } + } + + @Override + public void setTableComment(Session session, TableHandle tableHandle, Optional comment) + { + Span span = startSpan("setTableComment", tableHandle); + try (var ignored = scopedSpan(span)) { + delegate.setTableComment(session, tableHandle, comment); + } + } + + @Override + public void setViewComment(Session session, QualifiedObjectName viewName, Optional comment) + { + Span span = startSpan("setViewComment", viewName); + try (var ignored = scopedSpan(span)) { + delegate.setViewComment(session, viewName, comment); + } + } + + @Override + public void setViewColumnComment(Session session, QualifiedObjectName viewName, String columnName, Optional comment) + { + Span span = startSpan("setViewColumnComment", viewName); + try (var ignored = scopedSpan(span)) { + delegate.setViewColumnComment(session, viewName, columnName, comment); + } + } + + @Override + public void setColumnComment(Session session, TableHandle tableHandle, ColumnHandle column, Optional comment) + { + Span span = startSpan("setColumnComment", tableHandle); + try (var ignored = scopedSpan(span)) { + delegate.setColumnComment(session, tableHandle, column, comment); + } + } + + @Override + public void renameColumn(Session session, TableHandle tableHandle, CatalogSchemaTableName table, ColumnHandle source, String target) + { + Span span = startSpan("renameColumn", table); + try (var ignored = scopedSpan(span)) { + delegate.renameColumn(session, tableHandle, table, source, target); + } + } + + @Override + public void addColumn(Session session, TableHandle tableHandle, CatalogSchemaTableName table, ColumnMetadata column) + { + Span span = startSpan("addColumn", table); + try (var ignored = scopedSpan(span)) { + delegate.addColumn(session, tableHandle, table, column); + } + } + + @Override + public void setColumnType(Session session, TableHandle tableHandle, ColumnHandle column, Type type) + { + Span span = startSpan("setColumnType", tableHandle); + try (var ignored = scopedSpan(span)) { + delegate.setColumnType(session, tableHandle, column, type); + } + } + + @Override + public void setTableAuthorization(Session session, CatalogSchemaTableName table, TrinoPrincipal principal) + { + Span span = startSpan("setTableAuthorization", table); + try (var ignored = scopedSpan(span)) { + delegate.setTableAuthorization(session, table, principal); + } + } + + @Override + public void dropColumn(Session session, TableHandle tableHandle, CatalogSchemaTableName table, ColumnHandle column) + { + Span span = startSpan("dropColumn", table); + try (var ignored = scopedSpan(span)) { + delegate.dropColumn(session, tableHandle, table, column); + } + } + + @Override + public void dropField(Session session, TableHandle tableHandle, ColumnHandle column, List fieldPath) + { + Span span = startSpan("dropField", tableHandle); + try (var ignored = scopedSpan(span)) { + delegate.dropField(session, tableHandle, column, fieldPath); + } + } + + @Override + public void dropTable(Session session, TableHandle tableHandle, CatalogSchemaTableName tableName) + { + Span span = startSpan("dropTable", tableName); + try (var ignored = scopedSpan(span)) { + delegate.dropTable(session, tableHandle, tableName); + } + } + + @Override + public void truncateTable(Session session, TableHandle tableHandle) + { + Span span = startSpan("truncateTable", tableHandle); + try (var ignored = scopedSpan(span)) { + delegate.truncateTable(session, tableHandle); + } + } + + @Override + public Optional getNewTableLayout(Session session, String catalogName, ConnectorTableMetadata tableMetadata) + { + Span span = startSpan("getNewTableLayout", catalogName, tableMetadata); + try (var ignored = scopedSpan(span)) { + return delegate.getNewTableLayout(session, catalogName, tableMetadata); + } + } + + @Override + public OutputTableHandle beginCreateTable(Session session, String catalogName, ConnectorTableMetadata tableMetadata, Optional layout) + { + Span span = startSpan("beginCreateTable", catalogName, tableMetadata); + try (var ignored = scopedSpan(span)) { + return delegate.beginCreateTable(session, catalogName, tableMetadata, layout); + } + } + + @Override + public Optional finishCreateTable(Session session, OutputTableHandle tableHandle, Collection fragments, Collection computedStatistics) + { + Span span = startSpan("finishCreateTable", tableHandle.getCatalogHandle().getCatalogName()); + if (span.isRecording()) { + span.setAttribute(TrinoAttributes.TABLE, tableHandle.getConnectorHandle().toString()); + } + try (var ignored = scopedSpan(span)) { + return delegate.finishCreateTable(session, tableHandle, fragments, computedStatistics); + } + } + + @Override + public Optional getInsertLayout(Session session, TableHandle target) + { + Span span = startSpan("getInsertLayout", target); + try (var ignored = scopedSpan(span)) { + return delegate.getInsertLayout(session, target); + } + } + + @Override + public TableStatisticsMetadata getStatisticsCollectionMetadataForWrite(Session session, CatalogHandle catalogHandle, ConnectorTableMetadata tableMetadata) + { + Span span = startSpan("getStatisticsCollectionMetadataForWrite", catalogHandle.getCatalogName(), tableMetadata); + try (var ignored = scopedSpan(span)) { + return delegate.getStatisticsCollectionMetadataForWrite(session, catalogHandle, tableMetadata); + } + } + + @Override + public AnalyzeMetadata getStatisticsCollectionMetadata(Session session, TableHandle tableHandle, Map analyzeProperties) + { + Span span = startSpan("getStatisticsCollectionMetadata", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.getStatisticsCollectionMetadata(session, tableHandle, analyzeProperties); + } + } + + @Override + public AnalyzeTableHandle beginStatisticsCollection(Session session, TableHandle tableHandle) + { + Span span = startSpan("beginStatisticsCollection", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.beginStatisticsCollection(session, tableHandle); + } + } + + @Override + public void finishStatisticsCollection(Session session, AnalyzeTableHandle tableHandle, Collection computedStatistics) + { + Span span = startSpan("finishStatisticsCollection", tableHandle.getCatalogHandle().getCatalogName()); + if (span.isRecording()) { + span.setAttribute(TrinoAttributes.TABLE, tableHandle.getConnectorHandle().toString()); + } + try (var ignored = scopedSpan(span)) { + delegate.finishStatisticsCollection(session, tableHandle, computedStatistics); + } + } + + @Override + public void cleanupQuery(Session session) + { + Span span = startSpan("cleanupQuery"); + try (var ignored = scopedSpan(span)) { + delegate.cleanupQuery(session); + } + } + + @Override + public InsertTableHandle beginInsert(Session session, TableHandle tableHandle, List columns) + { + Span span = startSpan("beginInsert", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.beginInsert(session, tableHandle, columns); + } + } + + @Override + public boolean supportsMissingColumnsOnInsert(Session session, TableHandle tableHandle) + { + Span span = startSpan("supportsMissingColumnsOnInsert", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.supportsMissingColumnsOnInsert(session, tableHandle); + } + } + + @Override + public Optional finishInsert(Session session, InsertTableHandle tableHandle, Collection fragments, Collection computedStatistics) + { + Span span = startSpan("finishInsert", tableHandle.getCatalogHandle().getCatalogName()); + if (span.isRecording()) { + span.setAttribute(TrinoAttributes.TABLE, tableHandle.getConnectorHandle().toString()); + } + try (var ignored = scopedSpan(span)) { + return delegate.finishInsert(session, tableHandle, fragments, computedStatistics); + } + } + + @Override + public boolean delegateMaterializedViewRefreshToConnector(Session session, QualifiedObjectName viewName) + { + Span span = startSpan("delegateMaterializedViewRefreshToConnector", viewName); + try (var ignored = scopedSpan(span)) { + return delegate.delegateMaterializedViewRefreshToConnector(session, viewName); + } + } + + @Override + public ListenableFuture refreshMaterializedView(Session session, QualifiedObjectName viewName) + { + Span span = startSpan("refreshMaterializedView", viewName); + try (var ignored = scopedSpan(span)) { + return delegate.refreshMaterializedView(session, viewName); + } + } + + @Override + public InsertTableHandle beginRefreshMaterializedView(Session session, TableHandle tableHandle, List sourceTableHandles) + { + Span span = startSpan("beginRefreshMaterializedView", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.beginRefreshMaterializedView(session, tableHandle, sourceTableHandles); + } + } + + @Override + public Optional finishRefreshMaterializedView(Session session, TableHandle tableHandle, InsertTableHandle insertTableHandle, Collection fragments, Collection computedStatistics, List sourceTableHandles) + { + Span span = startSpan("finishRefreshMaterializedView", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.finishRefreshMaterializedView(session, tableHandle, insertTableHandle, fragments, computedStatistics, sourceTableHandles); + } + } + + @Override + public Optional applyDelete(Session session, TableHandle tableHandle) + { + Span span = startSpan("applyDelete", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.applyDelete(session, tableHandle); + } + } + + @Override + public OptionalLong executeDelete(Session session, TableHandle tableHandle) + { + Span span = startSpan("executeDelete", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.executeDelete(session, tableHandle); + } + } + + @Override + public RowChangeParadigm getRowChangeParadigm(Session session, TableHandle tableHandle) + { + Span span = startSpan("getRowChangeParadigm", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.getRowChangeParadigm(session, tableHandle); + } + } + + @Override + public ColumnHandle getMergeRowIdColumnHandle(Session session, TableHandle tableHandle) + { + Span span = startSpan("getMergeRowIdColumnHandle", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.getMergeRowIdColumnHandle(session, tableHandle); + } + } + + @Override + public Optional getUpdateLayout(Session session, TableHandle tableHandle) + { + Span span = startSpan("getUpdateLayout", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.getUpdateLayout(session, tableHandle); + } + } + + @Override + public MergeHandle beginMerge(Session session, TableHandle tableHandle) + { + Span span = startSpan("beginMerge", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.beginMerge(session, tableHandle); + } + } + + @Override + public void finishMerge(Session session, MergeHandle tableHandle, Collection fragments, Collection computedStatistics) + { + Span span = startSpan("finishMerge", tableHandle.getTableHandle().getCatalogHandle().getCatalogName()); + if (span.isRecording()) { + span.setAttribute(TrinoAttributes.TABLE, tableHandle.getTableHandle().getConnectorHandle().toString()); + } + try (var ignored = scopedSpan(span)) { + delegate.finishMerge(session, tableHandle, fragments, computedStatistics); + } + } + + @Override + public Optional getCatalogHandle(Session session, String catalogName) + { + Span span = startSpan("getCatalogHandle", catalogName); + try (var ignored = scopedSpan(span)) { + return delegate.getCatalogHandle(session, catalogName); + } + } + + @Override + public List listCatalogs(Session session) + { + Span span = startSpan("listCatalogs"); + try (var ignored = scopedSpan(span)) { + return delegate.listCatalogs(session); + } + } + + @Override + public List listViews(Session session, QualifiedTablePrefix prefix) + { + Span span = startSpan("listViews", prefix); + try (var ignored = scopedSpan(span)) { + return delegate.listViews(session, prefix); + } + } + + @Override + public Map getViews(Session session, QualifiedTablePrefix prefix) + { + Span span = startSpan("getViews", prefix); + try (var ignored = scopedSpan(span)) { + return delegate.getViews(session, prefix); + } + } + + @Override + public boolean isView(Session session, QualifiedObjectName viewName) + { + Span span = startSpan("isView", viewName); + try (var ignored = scopedSpan(span)) { + return delegate.isView(session, viewName); + } + } + + @Override + public Optional getView(Session session, QualifiedObjectName viewName) + { + Span span = startSpan("getView", viewName); + try (var ignored = scopedSpan(span)) { + return delegate.getView(session, viewName); + } + } + + @Override + public Map getSchemaProperties(Session session, CatalogSchemaName schemaName) + { + Span span = startSpan("getSchemaProperties", schemaName); + try (var ignored = scopedSpan(span)) { + return delegate.getSchemaProperties(session, schemaName); + } + } + + @Override + public Optional getSchemaOwner(Session session, CatalogSchemaName schemaName) + { + Span span = startSpan("getSchemaOwner", schemaName); + try (var ignored = scopedSpan(span)) { + return delegate.getSchemaOwner(session, schemaName); + } + } + + @Override + public void createView(Session session, QualifiedObjectName viewName, ViewDefinition definition, boolean replace) + { + Span span = startSpan("createView", viewName); + try (var ignored = scopedSpan(span)) { + delegate.createView(session, viewName, definition, replace); + } + } + + @Override + public void renameView(Session session, QualifiedObjectName existingViewName, QualifiedObjectName newViewName) + { + Span span = startSpan("renameView", existingViewName); + try (var ignored = scopedSpan(span)) { + delegate.renameView(session, existingViewName, newViewName); + } + } + + @Override + public void setViewAuthorization(Session session, CatalogSchemaTableName view, TrinoPrincipal principal) + { + Span span = startSpan("setViewAuthorization", view); + try (var ignored = scopedSpan(span)) { + delegate.setViewAuthorization(session, view, principal); + } + } + + @Override + public void dropView(Session session, QualifiedObjectName viewName) + { + Span span = startSpan("dropView", viewName); + try (var ignored = scopedSpan(span)) { + delegate.dropView(session, viewName); + } + } + + @Override + public Optional resolveIndex(Session session, TableHandle tableHandle, Set indexableColumns, Set outputColumns, TupleDomain tupleDomain) + { + Span span = startSpan("resolveIndex", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.resolveIndex(session, tableHandle, indexableColumns, outputColumns, tupleDomain); + } + } + + @Override + public Optional> applyLimit(Session session, TableHandle table, long limit) + { + Span span = startSpan("applyLimit", table); + try (var ignored = scopedSpan(span)) { + return delegate.applyLimit(session, table, limit); + } + } + + @Override + public Optional> applyFilter(Session session, TableHandle table, Constraint constraint) + { + Span span = startSpan("applyFilter", table); + try (var ignored = scopedSpan(span)) { + return delegate.applyFilter(session, table, constraint); + } + } + + @Override + public Optional> applyProjection(Session session, TableHandle table, List projections, Map assignments) + { + Span span = startSpan("applyProjection", table); + try (var ignored = scopedSpan(span)) { + return delegate.applyProjection(session, table, projections, assignments); + } + } + + @Override + public Optional> applySample(Session session, TableHandle table, SampleType sampleType, double sampleRatio) + { + Span span = startSpan("applySample", table); + try (var ignored = scopedSpan(span)) { + return delegate.applySample(session, table, sampleType, sampleRatio); + } + } + + @Override + public Optional> applyAggregation(Session session, TableHandle table, List aggregations, Map assignments, List> groupingSets) + { + Span span = startSpan("applyAggregation", table); + try (var ignored = scopedSpan(span)) { + return delegate.applyAggregation(session, table, aggregations, assignments, groupingSets); + } + } + + @Override + public Optional> applyJoin(Session session, JoinType joinType, TableHandle left, TableHandle right, ConnectorExpression joinCondition, Map leftAssignments, Map rightAssignments, JoinStatistics statistics) + { + Span span = startSpan("applyJoin"); + if (span.isRecording() && left.getCatalogHandle().equals(right.getCatalogHandle())) { + span.setAttribute(TrinoAttributes.CATALOG, left.getCatalogHandle().getCatalogName()); + } + try (var ignored = scopedSpan(span)) { + return delegate.applyJoin(session, joinType, left, right, joinCondition, leftAssignments, rightAssignments, statistics); + } + } + + @Override + public Optional> applyTopN(Session session, TableHandle handle, long topNCount, List sortItems, Map assignments) + { + Span span = startSpan("applyTopN", handle); + try (var ignored = scopedSpan(span)) { + return delegate.applyTopN(session, handle, topNCount, sortItems, assignments); + } + } + + @Override + public Optional> applyTableFunction(Session session, TableFunctionHandle handle) + { + Span span = startSpan("applyTableFunction") + .setAttribute(TrinoAttributes.CATALOG, handle.getCatalogHandle().getCatalogName()) + .setAttribute(TrinoAttributes.SCHEMA, handle.getSchemaFunctionName().getSchemaName()) + .setAttribute(TrinoAttributes.FUNCTION, handle.getSchemaFunctionName().getFunctionName()); + try (var ignored = scopedSpan(span)) { + return delegate.applyTableFunction(session, handle); + } + } + + @Override + public void validateScan(Session session, TableHandle table) + { + Span span = startSpan("validateScan", table); + try (var ignored = scopedSpan(span)) { + delegate.validateScan(session, table); + } + } + + @Override + public boolean isCatalogManagedSecurity(Session session, String catalog) + { + Span span = startSpan("isCatalogManagedSecurity", catalog); + try (var ignored = scopedSpan(span)) { + return delegate.isCatalogManagedSecurity(session, catalog); + } + } + + @Override + public boolean roleExists(Session session, String role, Optional catalog) + { + Span span = getStartSpan("roleExists", catalog); + try (var ignored = scopedSpan(span)) { + return delegate.roleExists(session, role, catalog); + } + } + + @Override + public void createRole(Session session, String role, Optional grantor, Optional catalog) + { + Span span = getStartSpan("createRole", catalog); + try (var ignored = scopedSpan(span)) { + delegate.createRole(session, role, grantor, catalog); + } + } + + @Override + public void dropRole(Session session, String role, Optional catalog) + { + Span span = getStartSpan("dropRole", catalog); + try (var ignored = scopedSpan(span)) { + delegate.dropRole(session, role, catalog); + } + } + + @Override + public Set listRoles(Session session, Optional catalog) + { + Span span = getStartSpan("listRoles", catalog); + try (var ignored = scopedSpan(span)) { + return delegate.listRoles(session, catalog); + } + } + + @Override + public Set listRoleGrants(Session session, Optional catalog, TrinoPrincipal principal) + { + Span span = getStartSpan("listRoleGrants", catalog); + try (var ignored = scopedSpan(span)) { + return delegate.listRoleGrants(session, catalog, principal); + } + } + + @Override + public void grantRoles(Session session, Set roles, Set grantees, boolean adminOption, Optional grantor, Optional catalog) + { + Span span = getStartSpan("grantRoles", catalog); + try (var ignored = scopedSpan(span)) { + delegate.grantRoles(session, roles, grantees, adminOption, grantor, catalog); + } + } + + @Override + public void revokeRoles(Session session, Set roles, Set grantees, boolean adminOption, Optional grantor, Optional catalog) + { + Span span = getStartSpan("revokeRoles", catalog); + try (var ignored = scopedSpan(span)) { + delegate.revokeRoles(session, roles, grantees, adminOption, grantor, catalog); + } + } + + @Override + public Set listApplicableRoles(Session session, TrinoPrincipal principal, Optional catalog) + { + Span span = getStartSpan("listApplicableRoles", catalog); + try (var ignored = scopedSpan(span)) { + return delegate.listApplicableRoles(session, principal, catalog); + } + } + + @Override + public Set listEnabledRoles(Identity identity) + { + Span span = startSpan("listEnabledRoles"); + try (var ignored = scopedSpan(span)) { + return delegate.listEnabledRoles(identity); + } + } + + @Override + public Set listEnabledRoles(Session session, String catalog) + { + Span span = startSpan("listEnabledRoles", catalog); + try (var ignored = scopedSpan(span)) { + return delegate.listEnabledRoles(session, catalog); + } + } + + @Override + public void grantSchemaPrivileges(Session session, CatalogSchemaName schemaName, Set privileges, TrinoPrincipal grantee, boolean grantOption) + { + Span span = startSpan("grantSchemaPrivileges", schemaName); + try (var ignored = scopedSpan(span)) { + delegate.grantSchemaPrivileges(session, schemaName, privileges, grantee, grantOption); + } + } + + @Override + public void denySchemaPrivileges(Session session, CatalogSchemaName schemaName, Set privileges, TrinoPrincipal grantee) + { + Span span = startSpan("denySchemaPrivileges", schemaName); + try (var ignored = scopedSpan(span)) { + delegate.denySchemaPrivileges(session, schemaName, privileges, grantee); + } + } + + @Override + public void revokeSchemaPrivileges(Session session, CatalogSchemaName schemaName, Set privileges, TrinoPrincipal grantee, boolean grantOption) + { + Span span = startSpan("revokeSchemaPrivileges", schemaName); + try (var ignored = scopedSpan(span)) { + delegate.revokeSchemaPrivileges(session, schemaName, privileges, grantee, grantOption); + } + } + + @Override + public void grantTablePrivileges(Session session, QualifiedObjectName tableName, Set privileges, TrinoPrincipal grantee, boolean grantOption) + { + Span span = startSpan("grantTablePrivileges", tableName); + try (var ignored = scopedSpan(span)) { + delegate.grantTablePrivileges(session, tableName, privileges, grantee, grantOption); + } + } + + @Override + public void denyTablePrivileges(Session session, QualifiedObjectName tableName, Set privileges, TrinoPrincipal grantee) + { + Span span = startSpan("denyTablePrivileges", tableName); + try (var ignored = scopedSpan(span)) { + delegate.denyTablePrivileges(session, tableName, privileges, grantee); + } + } + + @Override + public void revokeTablePrivileges(Session session, QualifiedObjectName tableName, Set privileges, TrinoPrincipal grantee, boolean grantOption) + { + Span span = startSpan("revokeTablePrivileges", tableName); + try (var ignored = scopedSpan(span)) { + delegate.revokeTablePrivileges(session, tableName, privileges, grantee, grantOption); + } + } + + @Override + public List listTablePrivileges(Session session, QualifiedTablePrefix prefix) + { + Span span = startSpan("listTablePrivileges", prefix); + try (var ignored = scopedSpan(span)) { + return delegate.listTablePrivileges(session, prefix); + } + } + + @Override + public Collection listFunctions(Session session) + { + Span span = startSpan("listFunctions"); + try (var ignored = scopedSpan(span)) { + return delegate.listFunctions(session); + } + } + + @Override + public ResolvedFunction decodeFunction(QualifiedName name) + { + // no tracing since it doesn't call any connector + return delegate.decodeFunction(name); + } + + @Override + public ResolvedFunction resolveFunction(Session session, QualifiedName name, List parameterTypes) + { + Span span = startSpan("resolveFunction") + .setAllAttributes(attribute(TrinoAttributes.FUNCTION, extractFunctionName(name))); + try (var ignored = scopedSpan(span)) { + return delegate.resolveFunction(session, name, parameterTypes); + } + } + + @Override + public ResolvedFunction resolveOperator(Session session, OperatorType operatorType, List argumentTypes) + throws OperatorNotFoundException + { + // no tracing since it doesn't call any connector + return delegate.resolveOperator(session, operatorType, argumentTypes); + } + + @Override + public ResolvedFunction getCoercion(Session session, Type fromType, Type toType) + { + // no tracing since it doesn't call any connector + return delegate.getCoercion(session, fromType, toType); + } + + @Override + public ResolvedFunction getCoercion(Session session, OperatorType operatorType, Type fromType, Type toType) + { + // no tracing since it doesn't call any connector + return delegate.getCoercion(session, operatorType, fromType, toType); + } + + @Override + public ResolvedFunction getCoercion(Session session, QualifiedName name, Type fromType, Type toType) + { + // no tracing since it doesn't call any connector + return delegate.getCoercion(session, name, fromType, toType); + } + + @Override + public boolean isAggregationFunction(Session session, QualifiedName name) + { + Span span = startSpan("isAggregationFunction") + .setAllAttributes(attribute(TrinoAttributes.FUNCTION, extractFunctionName(name))); + try (var ignored = scopedSpan(span)) { + return delegate.isAggregationFunction(session, name); + } + } + + @Override + public FunctionMetadata getFunctionMetadata(Session session, ResolvedFunction resolvedFunction) + { + Span span = startSpan("getFunctionMetadata") + .setAttribute(TrinoAttributes.CATALOG, resolvedFunction.getCatalogHandle().getCatalogName()) + .setAttribute(TrinoAttributes.FUNCTION, resolvedFunction.getSignature().getName()); + try (var ignored = scopedSpan(span)) { + return delegate.getFunctionMetadata(session, resolvedFunction); + } + } + + @Override + public AggregationFunctionMetadata getAggregationFunctionMetadata(Session session, ResolvedFunction resolvedFunction) + { + Span span = startSpan("getAggregationFunctionMetadata") + .setAttribute(TrinoAttributes.CATALOG, resolvedFunction.getCatalogHandle().getCatalogName()) + .setAttribute(TrinoAttributes.FUNCTION, resolvedFunction.getSignature().getName()); + try (var ignored = scopedSpan(span)) { + return delegate.getAggregationFunctionMetadata(session, resolvedFunction); + } + } + + @Override + public void createMaterializedView(Session session, QualifiedObjectName viewName, MaterializedViewDefinition definition, boolean replace, boolean ignoreExisting) + { + Span span = startSpan("createMaterializedView", viewName); + try (var ignored = scopedSpan(span)) { + delegate.createMaterializedView(session, viewName, definition, replace, ignoreExisting); + } + } + + @Override + public void dropMaterializedView(Session session, QualifiedObjectName viewName) + { + Span span = startSpan("dropMaterializedView", viewName); + try (var ignored = scopedSpan(span)) { + delegate.dropMaterializedView(session, viewName); + } + } + + @Override + public List listMaterializedViews(Session session, QualifiedTablePrefix prefix) + { + Span span = startSpan("listMaterializedViews", prefix); + try (var ignored = scopedSpan(span)) { + return delegate.listMaterializedViews(session, prefix); + } + } + + @Override + public Map getMaterializedViews(Session session, QualifiedTablePrefix prefix) + { + Span span = startSpan("getMaterializedViews", prefix); + try (var ignored = scopedSpan(span)) { + return delegate.getMaterializedViews(session, prefix); + } + } + + @Override + public boolean isMaterializedView(Session session, QualifiedObjectName viewName) + { + Span span = startSpan("isMaterializedView", viewName); + try (var ignored = scopedSpan(span)) { + return delegate.isMaterializedView(session, viewName); + } + } + + @Override + public Optional getMaterializedView(Session session, QualifiedObjectName viewName) + { + Span span = startSpan("getMaterializedView", viewName); + try (var ignored = scopedSpan(span)) { + return delegate.getMaterializedView(session, viewName); + } + } + + @Override + public MaterializedViewFreshness getMaterializedViewFreshness(Session session, QualifiedObjectName name) + { + Span span = startSpan("getMaterializedViewFreshness", name); + try (var ignored = scopedSpan(span)) { + return delegate.getMaterializedViewFreshness(session, name); + } + } + + @Override + public void renameMaterializedView(Session session, QualifiedObjectName existingViewName, QualifiedObjectName newViewName) + { + Span span = startSpan("renameMaterializedView", existingViewName); + try (var ignored = scopedSpan(span)) { + delegate.renameMaterializedView(session, existingViewName, newViewName); + } + } + + @Override + public void setMaterializedViewProperties(Session session, QualifiedObjectName viewName, Map> properties) + { + Span span = startSpan("setMaterializedViewProperties", viewName); + try (var ignored = scopedSpan(span)) { + delegate.setMaterializedViewProperties(session, viewName, properties); + } + } + + @Override + public Optional applyTableScanRedirect(Session session, TableHandle tableHandle) + { + Span span = startSpan("applyTableScanRedirect", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.applyTableScanRedirect(session, tableHandle); + } + } + + @Override + public RedirectionAwareTableHandle getRedirectionAwareTableHandle(Session session, QualifiedObjectName tableName) + { + Span span = startSpan("getRedirectionAwareTableHandle", tableName); + try (var ignored = scopedSpan(span)) { + return delegate.getRedirectionAwareTableHandle(session, tableName); + } + } + + @Override + public RedirectionAwareTableHandle getRedirectionAwareTableHandle(Session session, QualifiedObjectName tableName, Optional startVersion, Optional endVersion) + { + Span span = startSpan("getRedirectionAwareTableHandle", tableName); + try (var ignored = scopedSpan(span)) { + return delegate.getRedirectionAwareTableHandle(session, tableName, startVersion, endVersion); + } + } + + @Override + public boolean supportsReportingWrittenBytes(Session session, TableHandle tableHandle) + { + Span span = startSpan("supportsReportingWrittenBytes", tableHandle); + try (var ignored = scopedSpan(span)) { + return delegate.supportsReportingWrittenBytes(session, tableHandle); + } + } + + @Override + public boolean supportsReportingWrittenBytes(Session session, QualifiedObjectName tableName, Map tableProperties) + { + Span span = startSpan("supportsReportingWrittenBytes", tableName); + try (var ignored = scopedSpan(span)) { + return delegate.supportsReportingWrittenBytes(session, tableName, tableProperties); + } + } + + @Override + public Optional getTableHandle(Session session, QualifiedObjectName tableName, Optional startVersion, Optional endVersion) + { + Span span = startSpan("getTableHandle", tableName); + try (var ignored = scopedSpan(span)) { + return delegate.getTableHandle(session, tableName, startVersion, endVersion); + } + } + + @Override + public OptionalInt getMaxWriterTasks(Session session, String catalogName) + { + Span span = startSpan("getMaxWriterTasks", catalogName); + try (var ignored = scopedSpan(span)) { + return delegate.getMaxWriterTasks(session, catalogName); + } + } + + private Span startSpan(String methodName) + { + return tracer.spanBuilder("Metadata." + methodName) + .startSpan(); + } + + private Span startSpan(String methodName, String catalogName) + { + return startSpan(methodName) + .setAttribute(TrinoAttributes.CATALOG, catalogName); + } + + private Span getStartSpan(String methodName, Optional catalog) + { + return startSpan(methodName) + .setAllAttributes(attribute(TrinoAttributes.CATALOG, catalog)); + } + + private Span startSpan(String methodName, CatalogSchemaName schema) + { + return startSpan(methodName) + .setAttribute(TrinoAttributes.CATALOG, schema.getCatalogName()) + .setAttribute(TrinoAttributes.SCHEMA, schema.getSchemaName()); + } + + private Span startSpan(String methodName, QualifiedObjectName table) + { + return startSpan(methodName) + .setAttribute(TrinoAttributes.CATALOG, table.getCatalogName()) + .setAttribute(TrinoAttributes.SCHEMA, table.getSchemaName()) + .setAttribute(TrinoAttributes.TABLE, table.getObjectName()); + } + + private Span startSpan(String methodName, CatalogSchemaTableName table) + { + return startSpan(methodName) + .setAttribute(TrinoAttributes.CATALOG, table.getCatalogName()) + .setAttribute(TrinoAttributes.SCHEMA, table.getSchemaTableName().getSchemaName()) + .setAttribute(TrinoAttributes.TABLE, table.getSchemaTableName().getTableName()); + } + + private Span startSpan(String methodName, QualifiedTablePrefix prefix) + { + return startSpan(methodName) + .setAttribute(TrinoAttributes.CATALOG, prefix.getCatalogName()) + .setAllAttributes(attribute(TrinoAttributes.SCHEMA, prefix.getSchemaName())) + .setAllAttributes(attribute(TrinoAttributes.TABLE, prefix.getTableName())); + } + + private Span startSpan(String methodName, String catalogName, ConnectorTableMetadata tableMetadata) + { + return startSpan(methodName) + .setAttribute(TrinoAttributes.CATALOG, catalogName) + .setAttribute(TrinoAttributes.SCHEMA, tableMetadata.getTable().getSchemaName()) + .setAttribute(TrinoAttributes.TABLE, tableMetadata.getTable().getTableName()); + } + + private Span startSpan(String methodName, TableHandle handle) + { + Span span = startSpan(methodName); + if (span.isRecording()) { + span.setAttribute(TrinoAttributes.CATALOG, handle.getCatalogHandle().getCatalogName()); + span.setAttribute(TrinoAttributes.HANDLE, handle.getConnectorHandle().toString()); + } + return span; + } + + private Span startSpan(String methodName, TableExecuteHandle handle) + { + Span span = startSpan(methodName); + if (span.isRecording()) { + span.setAttribute(TrinoAttributes.CATALOG, handle.getCatalogHandle().getCatalogName()); + span.setAttribute(TrinoAttributes.HANDLE, handle.getConnectorHandle().toString()); + } + return span; + } + + private static Optional extractFunctionName(QualifiedName name) + { + try { + return Optional.of(ResolvedFunction.extractFunctionName(name)); + } + catch (IllegalArgumentException e) { + return Optional.empty(); + } + } +} diff --git a/core/trino-main/src/main/java/io/trino/tracing/TrinoAttributes.java b/core/trino-main/src/main/java/io/trino/tracing/TrinoAttributes.java new file mode 100644 index 000000000000..c5cbcd9fdf6f --- /dev/null +++ b/core/trino-main/src/main/java/io/trino/tracing/TrinoAttributes.java @@ -0,0 +1,60 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.tracing; + +import io.opentelemetry.api.common.AttributeKey; + +import java.util.List; + +import static io.opentelemetry.api.common.AttributeKey.booleanKey; +import static io.opentelemetry.api.common.AttributeKey.longKey; +import static io.opentelemetry.api.common.AttributeKey.stringArrayKey; +import static io.opentelemetry.api.common.AttributeKey.stringKey; + +public final class TrinoAttributes +{ + private TrinoAttributes() {} + + public static final AttributeKey QUERY_ID = stringKey("trino.query_id"); + public static final AttributeKey STAGE_ID = stringKey("trino.stage_id"); + public static final AttributeKey TASK_ID = stringKey("trino.task_id"); + public static final AttributeKey PIPELINE_ID = stringKey("trino.pipeline_id"); + public static final AttributeKey SPLIT_ID = stringKey("trino.split_id"); + + public static final AttributeKey QUERY_TYPE = stringKey("trino.query_type"); + + public static final AttributeKey ERROR_CODE = longKey("trino.error_code"); + public static final AttributeKey ERROR_NAME = stringKey("trino.error_name"); + public static final AttributeKey ERROR_TYPE = stringKey("trino.error_type"); + + public static final AttributeKey CATALOG = stringKey("trino.catalog"); + public static final AttributeKey SCHEMA = stringKey("trino.schema"); + public static final AttributeKey TABLE = stringKey("trino.table"); + public static final AttributeKey PROCEDURE = stringKey("trino.procedure"); + public static final AttributeKey FUNCTION = stringKey("trino.function"); + public static final AttributeKey HANDLE = stringKey("trino.handle"); + + public static final AttributeKey OPTIMIZER_NAME = stringKey("trino.optimizer"); + public static final AttributeKey> OPTIMIZER_RULES = stringArrayKey("trino.optimizer.rules"); + + public static final AttributeKey SPLIT_BATCH_MAX_SIZE = longKey("trino.split_batch.max_size"); + public static final AttributeKey SPLIT_BATCH_RESULT_SIZE = longKey("trino.split_batch.result_size"); + + public static final AttributeKey SPLIT_SCHEDULED_TIME_NANOS = longKey("trino.split.scheduled_time_nanos"); + public static final AttributeKey SPLIT_CPU_TIME_NANOS = longKey("trino.split.cpu_time_nanos"); + public static final AttributeKey SPLIT_WAIT_TIME_NANOS = longKey("trino.split.wait_time_nanos"); + public static final AttributeKey SPLIT_BLOCKED = booleanKey("trino.split.blocked"); + + public static final AttributeKey EVENT_STATE = stringKey("state"); +} diff --git a/core/trino-main/src/test/java/io/trino/execution/MockRemoteTaskFactory.java b/core/trino-main/src/test/java/io/trino/execution/MockRemoteTaskFactory.java index c8f97cfe6fc1..43b9416f2fde 100644 --- a/core/trino-main/src/test/java/io/trino/execution/MockRemoteTaskFactory.java +++ b/core/trino-main/src/test/java/io/trino/execution/MockRemoteTaskFactory.java @@ -24,6 +24,7 @@ import io.airlift.stats.TestingGcMonitor; import io.airlift.units.DataSize; import io.airlift.units.Duration; +import io.opentelemetry.api.trace.Span; import io.trino.Session; import io.trino.cost.StatsAndCosts; import io.trino.exchange.ExchangeManagerRegistry; @@ -127,12 +128,24 @@ public MockRemoteTask createTableScanTask(TaskId taskId, InternalNode newNode, L for (Split sourceSplit : splits) { initialSplits.put(sourceId, sourceSplit); } - return createRemoteTask(TEST_SESSION, taskId, newNode, testFragment, initialSplits.build(), PipelinedOutputBuffers.createInitial(BROADCAST), partitionedSplitCountTracker, ImmutableSet.of(), Optional.empty(), true); + return createRemoteTask( + TEST_SESSION, + Span.getInvalid(), + taskId, + newNode, + testFragment, + initialSplits.build(), + PipelinedOutputBuffers.createInitial(BROADCAST), + partitionedSplitCountTracker, + ImmutableSet.of(), + Optional.empty(), + true); } @Override public MockRemoteTask createRemoteTask( Session session, + Span stageSpan, TaskId taskId, InternalNode node, PlanFragment fragment, diff --git a/core/trino-main/src/test/java/io/trino/execution/TaskTestUtils.java b/core/trino-main/src/test/java/io/trino/execution/TaskTestUtils.java index c3c8cc8b2b57..afa33bcf6820 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TaskTestUtils.java +++ b/core/trino-main/src/test/java/io/trino/execution/TaskTestUtils.java @@ -16,6 +16,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import io.airlift.json.ObjectMapperProvider; +import io.opentelemetry.api.trace.Span; import io.trino.client.NodeVersion; import io.trino.connector.CatalogServiceProvider; import io.trino.cost.StatsAndCosts; @@ -182,7 +183,7 @@ public static LocalExecutionPlanner createTestingPlanner() public static TaskInfo updateTask(SqlTask sqlTask, List splitAssignments, OutputBuffers outputBuffers) { - return sqlTask.updateTask(TEST_SESSION, Optional.of(PLAN_FRAGMENT), splitAssignments, outputBuffers, ImmutableMap.of()); + return sqlTask.updateTask(TEST_SESSION, Span.getInvalid(), Optional.of(PLAN_FRAGMENT), splitAssignments, outputBuffers, ImmutableMap.of()); } public static SplitMonitor createTestSplitMonitor() diff --git a/core/trino-main/src/test/java/io/trino/execution/TestCreateMaterializedViewTask.java b/core/trino-main/src/test/java/io/trino/execution/TestCreateMaterializedViewTask.java index 7bbdae3f7c84..51badc9429bd 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestCreateMaterializedViewTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestCreateMaterializedViewTask.java @@ -148,7 +148,8 @@ public void setUp() new AllowAllAccessControl(), queryRunner.getTablePropertyManager(), queryRunner.getAnalyzePropertyManager()), - new StatementRewrite(ImmutableSet.of())); + new StatementRewrite(ImmutableSet.of()), + plannerContext.getTracer()); queryStateMachine = stateMachine(transactionManager, createTestMetadataManager(), new AllowAllAccessControl()); } @@ -276,7 +277,7 @@ public void testCreateDenyPermission() accessControl, new TablePropertyManager(CatalogServiceProvider.fail()), new AnalyzePropertyManager(CatalogServiceProvider.fail())); - AnalyzerFactory analyzerFactory = new AnalyzerFactory(statementAnalyzerFactory, new StatementRewrite(ImmutableSet.of())); + AnalyzerFactory analyzerFactory = new AnalyzerFactory(statementAnalyzerFactory, new StatementRewrite(ImmutableSet.of()), plannerContext.getTracer()); assertThatThrownBy(() -> getFutureValue(new CreateMaterializedViewTask(plannerContext, accessControl, parser, analyzerFactory, materializedViewPropertyManager) .execute(statement, queryStateMachine, ImmutableList.of(), WarningCollector.NOOP))) .isInstanceOf(AccessDeniedException.class) diff --git a/core/trino-main/src/test/java/io/trino/execution/TestCreateViewTask.java b/core/trino-main/src/test/java/io/trino/execution/TestCreateViewTask.java index 45987b926acc..551e83235059 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestCreateViewTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestCreateViewTask.java @@ -63,7 +63,8 @@ public void setUp() new AllowAllAccessControl(), new TablePropertyManager(CatalogServiceProvider.fail()), new AnalyzePropertyManager(CatalogServiceProvider.fail())), - new StatementRewrite(ImmutableSet.of())); + new StatementRewrite(ImmutableSet.of()), + plannerContext.getTracer()); QualifiedObjectName tableName = qualifiedObjectName("mock_table"); metadata.createTable(testSession, CATALOG_NAME, someTable(tableName), false); } diff --git a/core/trino-main/src/test/java/io/trino/execution/TestMemoryRevokingScheduler.java b/core/trino-main/src/test/java/io/trino/execution/TestMemoryRevokingScheduler.java index 8b9986c4e508..b57d796277dc 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestMemoryRevokingScheduler.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestMemoryRevokingScheduler.java @@ -50,6 +50,7 @@ import java.util.concurrent.atomic.AtomicInteger; import static io.airlift.concurrent.Threads.threadsNamed; +import static io.airlift.tracing.Tracing.noopTracer; import static io.airlift.units.DataSize.Unit.GIGABYTE; import static io.airlift.units.DataSize.Unit.MEGABYTE; import static io.trino.execution.SqlTask.createSqlTask; @@ -98,6 +99,7 @@ public void setUp() taskExecutor, planner, createTestSplitMonitor(), + noopTracer(), new TaskManagerConfig()); allOperatorContexts = null; @@ -267,6 +269,7 @@ private SqlTask newSqlTask(QueryId queryId) location, "fake", queryContext, + noopTracer(), sqlTaskExecutionFactory, executor, sqlTask -> {}, diff --git a/core/trino-main/src/test/java/io/trino/execution/TestQueryInfo.java b/core/trino-main/src/test/java/io/trino/execution/TestQueryInfo.java index 7b9394342bee..6be48aaf8947 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestQueryInfo.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestQueryInfo.java @@ -35,6 +35,7 @@ import static io.trino.SessionTestUtils.TEST_SESSION; import static io.trino.execution.QueryState.FINISHED; +import static io.trino.tracing.TracingJsonCodec.tracingJsonCodecFactory; import static org.testng.Assert.assertEquals; public class TestQueryInfo @@ -42,7 +43,7 @@ public class TestQueryInfo @Test public void testQueryInfoRoundTrip() { - JsonCodec codec = JsonCodec.jsonCodec(QueryInfo.class); + JsonCodec codec = tracingJsonCodecFactory().jsonCodec(QueryInfo.class); QueryInfo expected = createQueryInfo(); QueryInfo actual = codec.fromJson(codec.toJsonBytes(expected)); diff --git a/core/trino-main/src/test/java/io/trino/execution/TestSqlStage.java b/core/trino-main/src/test/java/io/trino/execution/TestSqlStage.java index 17122a41da3b..547e65662b83 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestSqlStage.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestSqlStage.java @@ -53,6 +53,7 @@ import java.util.concurrent.ScheduledExecutorService; import static io.airlift.concurrent.Threads.daemonThreadsNamed; +import static io.airlift.tracing.Tracing.noopTracer; import static io.trino.SessionTestUtils.TEST_SESSION; import static io.trino.execution.SqlStage.createSqlStage; import static io.trino.execution.buffer.PipelinedOutputBuffers.BufferType.ARBITRARY; @@ -117,6 +118,7 @@ private void testFinalStageInfoInternal() true, nodeTaskMap, executor, + noopTracer(), new SplitSchedulerStats()); // add listener that fetches stage info when the final status is available diff --git a/core/trino-main/src/test/java/io/trino/execution/TestSqlTask.java b/core/trino-main/src/test/java/io/trino/execution/TestSqlTask.java index 86917ef94f29..380d6cb6291b 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestSqlTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestSqlTask.java @@ -23,6 +23,7 @@ import io.airlift.stats.TestingGcMonitor; import io.airlift.units.DataSize; import io.airlift.units.Duration; +import io.opentelemetry.api.trace.Span; import io.trino.exchange.ExchangeManagerRegistry; import io.trino.execution.buffer.BufferResult; import io.trino.execution.buffer.BufferState; @@ -48,6 +49,7 @@ import java.util.concurrent.atomic.AtomicInteger; import static io.airlift.concurrent.Threads.threadsNamed; +import static io.airlift.tracing.Tracing.noopTracer; import static io.airlift.units.DataSize.Unit.GIGABYTE; import static io.airlift.units.DataSize.Unit.MEGABYTE; import static io.trino.SessionTestUtils.TEST_SESSION; @@ -107,6 +109,7 @@ public void setUp() taskExecutor, planner, createTestSplitMonitor(), + noopTracer(), new TaskManagerConfig()); } @@ -127,6 +130,7 @@ public void testEmptyQuery() SqlTask sqlTask = createInitialTask(); TaskInfo taskInfo = sqlTask.updateTask(TEST_SESSION, + Span.getInvalid(), Optional.of(PLAN_FRAGMENT), ImmutableList.of(), PipelinedOutputBuffers.createInitial(PARTITIONED) @@ -140,6 +144,7 @@ public void testEmptyQuery() assertEquals(taskInfo.getTaskStatus().getVersion(), STARTING_VERSION); taskInfo = sqlTask.updateTask(TEST_SESSION, + Span.getInvalid(), Optional.of(PLAN_FRAGMENT), ImmutableList.of(new SplitAssignment(TABLE_SCAN_NODE_ID, ImmutableSet.of(), true)), PipelinedOutputBuffers.createInitial(PARTITIONED) @@ -160,6 +165,7 @@ public void testSimpleQuery() assertEquals(sqlTask.getTaskStatus().getState(), TaskState.RUNNING); assertEquals(sqlTask.getTaskStatus().getVersion(), STARTING_VERSION); sqlTask.updateTask(TEST_SESSION, + Span.getInvalid(), Optional.of(PLAN_FRAGMENT), ImmutableList.of(new SplitAssignment(TABLE_SCAN_NODE_ID, ImmutableSet.of(SPLIT), true)), PipelinedOutputBuffers.createInitial(PARTITIONED).withBuffer(OUT, 0).withNoMoreBufferIds(), @@ -202,6 +208,7 @@ public void testCancel() SqlTask sqlTask = createInitialTask(); TaskInfo taskInfo = sqlTask.updateTask(TEST_SESSION, + Span.getInvalid(), Optional.of(PLAN_FRAGMENT), ImmutableList.of(), PipelinedOutputBuffers.createInitial(PARTITIONED) @@ -241,6 +248,7 @@ public void testAbort() assertEquals(sqlTask.getTaskStatus().getState(), TaskState.RUNNING); assertEquals(sqlTask.getTaskStatus().getVersion(), STARTING_VERSION); sqlTask.updateTask(TEST_SESSION, + Span.getInvalid(), Optional.of(PLAN_FRAGMENT), ImmutableList.of(new SplitAssignment(TABLE_SCAN_NODE_ID, ImmutableSet.of(SPLIT), true)), PipelinedOutputBuffers.createInitial(PARTITIONED).withBuffer(OUT, 0).withNoMoreBufferIds(), @@ -343,6 +351,7 @@ public void testDynamicFilters() SqlTask sqlTask = createInitialTask(); sqlTask.updateTask( TEST_SESSION, + Span.getInvalid(), Optional.of(PLAN_FRAGMENT_WITH_DYNAMIC_FILTER_SOURCE), ImmutableList.of(new SplitAssignment(TABLE_SCAN_NODE_ID, ImmutableSet.of(SPLIT), false)), PipelinedOutputBuffers.createInitial(PARTITIONED) @@ -372,6 +381,7 @@ public void testDynamicFilterFetchAfterTaskDone() OutputBuffers outputBuffers = PipelinedOutputBuffers.createInitial(PARTITIONED).withBuffer(OUT, 0).withNoMoreBufferIds(); sqlTask.updateTask( TEST_SESSION, + Span.getInvalid(), Optional.of(PLAN_FRAGMENT_WITH_DYNAMIC_FILTER_SOURCE), ImmutableList.of(new SplitAssignment(TABLE_SCAN_NODE_ID, ImmutableSet.of(), false)), outputBuffers, @@ -419,6 +429,7 @@ private SqlTask createInitialTask() location, "fake", queryContext, + noopTracer(), sqlTaskExecutionFactory, taskNotificationExecutor, sqlTask -> {}, diff --git a/core/trino-main/src/test/java/io/trino/execution/TestSqlTaskExecution.java b/core/trino-main/src/test/java/io/trino/execution/TestSqlTaskExecution.java index 0af9054d98fc..aedcb033b15b 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestSqlTaskExecution.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestSqlTaskExecution.java @@ -24,6 +24,7 @@ import io.airlift.stats.TestingGcMonitor; import io.airlift.units.DataSize; import io.airlift.units.Duration; +import io.opentelemetry.api.trace.Span; import io.trino.execution.buffer.BufferResult; import io.trino.execution.buffer.BufferState; import io.trino.execution.buffer.OutputBuffer; @@ -66,6 +67,7 @@ import static com.google.common.base.Preconditions.checkState; import static io.airlift.concurrent.Threads.threadsNamed; import static io.airlift.slice.SizeOf.instanceSize; +import static io.airlift.tracing.Tracing.noopTracer; import static io.airlift.units.DataSize.Unit.GIGABYTE; import static io.airlift.units.DataSize.Unit.MEGABYTE; import static io.trino.SessionTestUtils.TEST_SESSION; @@ -136,10 +138,12 @@ public void testSimple() SqlTaskExecution sqlTaskExecution = new SqlTaskExecution( taskStateMachine, taskContext, + Span.getInvalid(), outputBuffer, localExecutionPlan, taskExecutor, createTestSplitMonitor(), + noopTracer(), taskNotificationExecutor); sqlTaskExecution.start(); diff --git a/core/trino-main/src/test/java/io/trino/execution/TestSqlTaskManager.java b/core/trino-main/src/test/java/io/trino/execution/TestSqlTaskManager.java index 039872c61ea9..624eaec8f6f7 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestSqlTaskManager.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestSqlTaskManager.java @@ -25,6 +25,7 @@ import io.airlift.units.DataSize; import io.airlift.units.DataSize.Unit; import io.airlift.units.Duration; +import io.opentelemetry.api.trace.Span; import io.trino.Session; import io.trino.connector.CatalogProperties; import io.trino.connector.ConnectorServices; @@ -69,6 +70,7 @@ import static com.google.common.util.concurrent.Futures.immediateVoidFuture; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static io.airlift.tracing.Tracing.noopTracer; import static io.trino.SessionTestUtils.TEST_SESSION; import static io.trino.SystemSessionProperties.QUERY_MAX_MEMORY_PER_NODE; import static io.trino.execution.TaskTestUtils.PLAN_FRAGMENT; @@ -340,6 +342,7 @@ public void testSessionPropertyMemoryLimitOverride() .setSystemProperty(QUERY_MAX_MEMORY_PER_NODE, "1B") .build(), reduceLimitsId, + Span.getInvalid(), Optional.of(PLAN_FRAGMENT), ImmutableList.of(new SplitAssignment(TABLE_SCAN_NODE_ID, ImmutableSet.of(SPLIT), true)), PipelinedOutputBuffers.createInitial(PARTITIONED).withBuffer(OUT, 0).withNoMoreBufferIds(), @@ -353,6 +356,7 @@ public void testSessionPropertyMemoryLimitOverride() .setSystemProperty(QUERY_MAX_MEMORY_PER_NODE, "10B") .build(), increaseLimitsId, + Span.getInvalid(), Optional.of(PLAN_FRAGMENT), ImmutableList.of(new SplitAssignment(TABLE_SCAN_NODE_ID, ImmutableSet.of(SPLIT), true)), PipelinedOutputBuffers.createInitial(PARTITIONED).withBuffer(OUT, 0).withNoMoreBufferIds(), @@ -384,6 +388,7 @@ private SqlTaskManager createSqlTaskManager(TaskManagerConfig taskManagerConfig, localSpillManager, new NodeSpillConfig(), new TestingGcMonitor(), + noopTracer(), new ExchangeManagerRegistry()); } @@ -408,6 +413,7 @@ private SqlTaskManager createSqlTaskManager( localSpillManager, new NodeSpillConfig(), new TestingGcMonitor(), + noopTracer(), new ExchangeManagerRegistry(), stuckSplitStackTracePredicate); } @@ -416,6 +422,7 @@ private TaskInfo createTask(SqlTaskManager sqlTaskManager, TaskId taskId, Immuta { return sqlTaskManager.updateTask(TEST_SESSION, taskId, + Span.getInvalid(), Optional.of(PLAN_FRAGMENT), ImmutableList.of(new SplitAssignment(TABLE_SCAN_NODE_ID, splits, true)), outputBuffers, @@ -428,6 +435,7 @@ private TaskInfo createTask(SqlTaskManager sqlTaskManager, TaskId taskId, Output .addTaskContext(new TaskStateMachine(taskId, directExecutor()), testSessionBuilder().build(), () -> {}, false, false); return sqlTaskManager.updateTask(TEST_SESSION, taskId, + Span.getInvalid(), Optional.of(PLAN_FRAGMENT), ImmutableList.of(), outputBuffers, @@ -512,6 +520,18 @@ public void waitForFinish() finishedFuture.get(10, SECONDS); } + @Override + public int getPipelineId() + { + return 0; + } + + @Override + public Span getPipelineSpan() + { + return Span.getInvalid(); + } + @Override public synchronized boolean isFinished() { diff --git a/core/trino-main/src/test/java/io/trino/execution/TestStageStateMachine.java b/core/trino-main/src/test/java/io/trino/execution/TestStageStateMachine.java index 195036d70b8c..5c6d45ed3b7d 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestStageStateMachine.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestStageStateMachine.java @@ -15,6 +15,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import io.opentelemetry.api.trace.Span; import io.trino.cost.StatsAndCosts; import io.trino.execution.scheduler.SplitSchedulerStats; import io.trino.sql.planner.Partitioning; @@ -35,6 +36,7 @@ import java.util.concurrent.ExecutorService; import static io.airlift.concurrent.Threads.daemonThreadsNamed; +import static io.airlift.tracing.Tracing.noopTracer; import static io.trino.spi.type.VarcharType.VARCHAR; import static io.trino.sql.planner.SystemPartitioningHandle.SINGLE_DISTRIBUTION; import static io.trino.sql.planner.SystemPartitioningHandle.SOURCE_DISTRIBUTION; @@ -236,7 +238,14 @@ private static void assertState(StageStateMachine stateMachine, StageState expec private StageStateMachine createStageStateMachine() { - return new StageStateMachine(STAGE_ID, PLAN_FRAGMENT, ImmutableMap.of(), executor, new SplitSchedulerStats()); + return new StageStateMachine( + STAGE_ID, + PLAN_FRAGMENT, + ImmutableMap.of(), + executor, + noopTracer(), + Span.getInvalid(), + new SplitSchedulerStats()); } private static PlanFragment createValuesPlan() diff --git a/core/trino-main/src/test/java/io/trino/execution/TestingRemoteTaskFactory.java b/core/trino-main/src/test/java/io/trino/execution/TestingRemoteTaskFactory.java index 0423053a8492..0c767b2d3074 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestingRemoteTaskFactory.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestingRemoteTaskFactory.java @@ -23,6 +23,7 @@ import io.airlift.stats.TDigest; import io.airlift.units.DataSize; import io.airlift.units.Duration; +import io.opentelemetry.api.trace.Span; import io.trino.Session; import io.trino.execution.NodeTaskMap.PartitionedSplitCountTracker; import io.trino.execution.StateMachine.StateChangeListener; @@ -71,6 +72,7 @@ public class TestingRemoteTaskFactory @Override public synchronized RemoteTask createRemoteTask( Session session, + Span stageSpan, TaskId taskId, InternalNode node, PlanFragment fragment, diff --git a/core/trino-main/src/test/java/io/trino/execution/executor/SimulationSplit.java b/core/trino-main/src/test/java/io/trino/execution/executor/SimulationSplit.java index c80d8e7a33e6..54ac4c2cd2b1 100644 --- a/core/trino-main/src/test/java/io/trino/execution/executor/SimulationSplit.java +++ b/core/trino-main/src/test/java/io/trino/execution/executor/SimulationSplit.java @@ -16,6 +16,7 @@ import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; import io.airlift.units.Duration; +import io.opentelemetry.api.trace.Span; import io.trino.execution.SplitRunner; import java.util.concurrent.RejectedExecutionException; @@ -111,6 +112,18 @@ void setKilled() task.setKilled(); } + @Override + public int getPipelineId() + { + return 0; + } + + @Override + public Span getPipelineSpan() + { + return Span.getInvalid(); + } + @Override public boolean isFinished() { diff --git a/core/trino-main/src/test/java/io/trino/execution/executor/TestTaskExecutor.java b/core/trino-main/src/test/java/io/trino/execution/executor/TestTaskExecutor.java index 888d30c0231a..484b58231c44 100644 --- a/core/trino-main/src/test/java/io/trino/execution/executor/TestTaskExecutor.java +++ b/core/trino-main/src/test/java/io/trino/execution/executor/TestTaskExecutor.java @@ -19,6 +19,7 @@ import com.google.common.util.concurrent.SettableFuture; import io.airlift.testing.TestingTicker; import io.airlift.units.Duration; +import io.opentelemetry.api.trace.Span; import io.trino.execution.SplitRunner; import io.trino.execution.StageId; import io.trino.execution.TaskId; @@ -627,6 +628,18 @@ public String getInfo() return "testing-split"; } + @Override + public int getPipelineId() + { + return 0; + } + + @Override + public Span getPipelineSpan() + { + return Span.getInvalid(); + } + @Override public boolean isFinished() { diff --git a/core/trino-main/src/test/java/io/trino/execution/scheduler/TestScaledWriterScheduler.java b/core/trino-main/src/test/java/io/trino/execution/scheduler/TestScaledWriterScheduler.java index bcc4cd70162e..36dc620509d6 100644 --- a/core/trino-main/src/test/java/io/trino/execution/scheduler/TestScaledWriterScheduler.java +++ b/core/trino-main/src/test/java/io/trino/execution/scheduler/TestScaledWriterScheduler.java @@ -18,6 +18,7 @@ import com.google.common.collect.Multimap; import io.airlift.units.DataSize; import io.airlift.units.Duration; +import io.opentelemetry.api.trace.Span; import io.trino.client.NodeVersion; import io.trino.cost.StatsAndCosts; import io.trino.execution.ExecutionFailureInfo; @@ -284,6 +285,12 @@ public int getAttemptId() throw new UnsupportedOperationException(); } + @Override + public Span getStageSpan() + { + throw new UnsupportedOperationException(); + } + @Override public void beginScheduling() { diff --git a/core/trino-main/src/test/java/io/trino/execution/scheduler/TestSourcePartitionedScheduler.java b/core/trino-main/src/test/java/io/trino/execution/scheduler/TestSourcePartitionedScheduler.java index bdf008a1b559..7815f284bd3d 100644 --- a/core/trino-main/src/test/java/io/trino/execution/scheduler/TestSourcePartitionedScheduler.java +++ b/core/trino-main/src/test/java/io/trino/execution/scheduler/TestSourcePartitionedScheduler.java @@ -80,6 +80,7 @@ import java.util.function.Supplier; import static io.airlift.concurrent.Threads.daemonThreadsNamed; +import static io.airlift.tracing.Tracing.noopTracer; import static io.trino.SessionTestUtils.TEST_SESSION; import static io.trino.execution.scheduler.NodeSchedulerConfig.SplitsBalancingPolicy.NODE; import static io.trino.execution.scheduler.NodeSchedulerConfig.SplitsBalancingPolicy.STAGE; @@ -757,6 +758,7 @@ private StageExecution createStageExecution(PlanFragment fragment, NodeTaskMap n true, nodeTaskMap, queryExecutor, + noopTracer(), new SplitSchedulerStats()); ImmutableMap.Builder outputBuffers = ImmutableMap.builder(); outputBuffers.put(fragment.getId(), new PartitionedPipelinedOutputBufferManager(FIXED_HASH_DISTRIBUTION, 1)); diff --git a/core/trino-main/src/test/java/io/trino/execution/scheduler/policy/TestPhasedExecutionSchedule.java b/core/trino-main/src/test/java/io/trino/execution/scheduler/policy/TestPhasedExecutionSchedule.java index 2bdff4245615..0cda09066639 100644 --- a/core/trino-main/src/test/java/io/trino/execution/scheduler/policy/TestPhasedExecutionSchedule.java +++ b/core/trino-main/src/test/java/io/trino/execution/scheduler/policy/TestPhasedExecutionSchedule.java @@ -18,6 +18,7 @@ import com.google.common.graph.EndpointPair; import com.google.common.graph.Graph; import com.google.common.util.concurrent.ListenableFuture; +import io.opentelemetry.api.trace.Span; import io.trino.execution.DynamicFilterConfig; import io.trino.execution.ExecutionFailureInfo; import io.trino.execution.RemoteTask; @@ -334,6 +335,12 @@ public int getAttemptId() throw new UnsupportedOperationException(); } + @Override + public Span getStageSpan() + { + throw new UnsupportedOperationException(); + } + @Override public void beginScheduling() { diff --git a/core/trino-main/src/test/java/io/trino/server/TestQueryResource.java b/core/trino-main/src/test/java/io/trino/server/TestQueryResource.java index e6b246349dd1..5c6284f54e71 100644 --- a/core/trino-main/src/test/java/io/trino/server/TestQueryResource.java +++ b/core/trino-main/src/test/java/io/trino/server/TestQueryResource.java @@ -41,7 +41,6 @@ import static io.airlift.http.client.StaticBodyGenerator.createStaticBodyGenerator; import static io.airlift.http.client.StatusResponseHandler.createStatusResponseHandler; import static io.airlift.json.JsonCodec.jsonCodec; -import static io.airlift.json.JsonCodec.listJsonCodec; import static io.airlift.testing.Closeables.closeAll; import static io.trino.client.ProtocolHeaders.TRINO_HEADERS; import static io.trino.execution.QueryState.FAILED; @@ -54,6 +53,7 @@ import static io.trino.testing.TestingAccessControlManager.TestingPrivilegeType.KILL_QUERY; import static io.trino.testing.TestingAccessControlManager.TestingPrivilegeType.VIEW_QUERY; import static io.trino.testing.TestingAccessControlManager.privilege; +import static io.trino.tracing.TracingJsonCodec.tracingJsonCodecFactory; import static java.nio.charset.StandardCharsets.UTF_8; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -66,6 +66,8 @@ @Test(singleThreaded = true) public class TestQueryResource { + private static final JsonCodec> BASIC_QUERY_INFO_CODEC = tracingJsonCodecFactory().listJsonCodec(BasicQueryInfo.class); + private HttpClient client; private TestingTrinoServer server; @@ -286,7 +288,7 @@ private List getQueryInfos(String path) .setUri(server.resolve(path)) .setHeader(TRINO_HEADERS.requestUser(), "unknown") .build(); - return client.execute(request, createJsonResponseHandler(listJsonCodec(BasicQueryInfo.class))); + return client.execute(request, createJsonResponseHandler(BASIC_QUERY_INFO_CODEC)); } private static void assertStateCounts(Iterable infos, int expectedFinished, int expectedFailed, int expectedRunning) diff --git a/core/trino-main/src/test/java/io/trino/server/TestQuerySessionSupplier.java b/core/trino-main/src/test/java/io/trino/server/TestQuerySessionSupplier.java index 99b09b9c5880..35934c80ff55 100644 --- a/core/trino-main/src/test/java/io/trino/server/TestQuerySessionSupplier.java +++ b/core/trino-main/src/test/java/io/trino/server/TestQuerySessionSupplier.java @@ -20,6 +20,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.ListMultimap; import io.airlift.jaxrs.testing.GuavaMultivaluedMap; +import io.opentelemetry.api.trace.Span; import io.trino.Session; import io.trino.metadata.Metadata; import io.trino.metadata.SessionPropertyManager; @@ -79,7 +80,7 @@ public void testCreateSession() { SessionContext context = SESSION_CONTEXT_FACTORY.createSessionContext(TEST_HEADERS, Optional.empty(), Optional.of("testRemote"), Optional.empty()); QuerySessionSupplier sessionSupplier = createSessionSupplier(new SqlEnvironmentConfig()); - Session session = sessionSupplier.createSession(new QueryId("test_query_id"), context); + Session session = sessionSupplier.createSession(new QueryId("test_query_id"), Span.getInvalid(), context); assertEquals(session.getQueryId(), new QueryId("test_query_id")); assertEquals(session.getUser(), "testUser"); @@ -142,7 +143,7 @@ public void testInvalidTimeZone() .build()); SessionContext context = SESSION_CONTEXT_FACTORY.createSessionContext(headers, Optional.empty(), Optional.of("remoteAddress"), Optional.empty()); QuerySessionSupplier sessionSupplier = createSessionSupplier(new SqlEnvironmentConfig()); - assertThatThrownBy(() -> sessionSupplier.createSession(new QueryId("test_query_id"), context)) + assertThatThrownBy(() -> sessionSupplier.createSession(new QueryId("test_query_id"), Span.getInvalid(), context)) .isInstanceOf(TrinoException.class) .hasMessage("Time zone not supported: unknown_timezone"); } @@ -237,7 +238,7 @@ private static Session createSession(ListMultimap headers, SqlEn MultivaluedMap headerMap = new GuavaMultivaluedMap<>(headers); SessionContext context = SESSION_CONTEXT_FACTORY.createSessionContext(headerMap, Optional.empty(), Optional.of("testRemote"), Optional.empty()); QuerySessionSupplier sessionSupplier = createSessionSupplier(config); - return sessionSupplier.createSession(new QueryId("test_query_id"), context); + return sessionSupplier.createSession(new QueryId("test_query_id"), Span.getInvalid(), context); } private static QuerySessionSupplier createSessionSupplier(SqlEnvironmentConfig config) diff --git a/core/trino-main/src/test/java/io/trino/server/TestQueryStateInfoResource.java b/core/trino-main/src/test/java/io/trino/server/TestQueryStateInfoResource.java index 16e6c01b712b..20fa7d2af6a3 100644 --- a/core/trino-main/src/test/java/io/trino/server/TestQueryStateInfoResource.java +++ b/core/trino-main/src/test/java/io/trino/server/TestQueryStateInfoResource.java @@ -44,6 +44,7 @@ import static io.trino.execution.QueryState.RUNNING; import static io.trino.testing.TestingAccessControlManager.TestingPrivilegeType.VIEW_QUERY; import static io.trino.testing.TestingAccessControlManager.privilege; +import static io.trino.tracing.TracingJsonCodec.tracingJsonCodecFactory; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.concurrent.TimeUnit.MINUTES; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -57,6 +58,7 @@ public class TestQueryStateInfoResource { private static final String LONG_LASTING_QUERY = "SELECT * FROM tpch.sf1.lineitem"; private static final JsonCodec QUERY_RESULTS_JSON_CODEC = jsonCodec(QueryResults.class); + private static final JsonCodec> BASIC_QUERY_INFO_CODEC = tracingJsonCodecFactory().listJsonCodec(BasicQueryInfo.class); private TestingTrinoServer server; private HttpClient client; @@ -94,7 +96,7 @@ public void setUp() .setUri(uriBuilderFrom(server.getBaseUrl()).replacePath("/v1/query").build()) .setHeader(TRINO_HEADERS.requestUser(), "unknown") .build(), - createJsonResponseHandler(listJsonCodec(BasicQueryInfo.class))); + createJsonResponseHandler(BASIC_QUERY_INFO_CODEC)); if (queryInfos.size() == 2) { if (queryInfos.stream().allMatch(info -> info.getState() == RUNNING)) { break; diff --git a/core/trino-main/src/test/java/io/trino/server/remotetask/TestHttpRemoteTask.java b/core/trino-main/src/test/java/io/trino/server/remotetask/TestHttpRemoteTask.java index da7fa28f5db4..0db0b1abfff4 100644 --- a/core/trino-main/src/test/java/io/trino/server/remotetask/TestHttpRemoteTask.java +++ b/core/trino-main/src/test/java/io/trino/server/remotetask/TestHttpRemoteTask.java @@ -30,6 +30,8 @@ import io.airlift.json.JsonCodec; import io.airlift.json.JsonModule; import io.airlift.units.Duration; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.Span; import io.trino.Session; import io.trino.block.BlockJsonSerde; import io.trino.client.NodeVersion; @@ -117,6 +119,9 @@ import static io.airlift.testing.Assertions.assertGreaterThan; import static io.airlift.testing.Assertions.assertGreaterThanOrEqual; import static io.airlift.testing.Assertions.assertLessThan; +import static io.airlift.tracing.SpanSerialization.SpanDeserializer; +import static io.airlift.tracing.SpanSerialization.SpanSerializer; +import static io.airlift.tracing.Tracing.noopTracer; import static io.trino.SessionTestUtils.TEST_SESSION; import static io.trino.SystemSessionProperties.REMOTE_TASK_ADAPTIVE_UPDATE_REQUEST_SIZE_ENABLED; import static io.trino.SystemSessionProperties.REMOTE_TASK_GUARANTEED_SPLITS_PER_REQUEST; @@ -511,6 +516,7 @@ private RemoteTask createRemoteTask(HttpRemoteTaskFactory httpRemoteTaskFactory, { return httpRemoteTaskFactory.createRemoteTask( session, + Span.getInvalid(), new TaskId(new StageId("test", 1), 2, 0), new InternalNode("node-id", URI.create("http://fake.invalid/"), new NodeVersion("version"), false), TaskTestUtils.PLAN_FRAGMENT, @@ -555,6 +561,10 @@ public void configure(Binder binder) binder.bind(TypeManager.class).toInstance(TESTING_TYPE_MANAGER); binder.bind(BlockEncodingManager.class).in(SINGLETON); binder.bind(BlockEncodingSerde.class).to(InternalBlockEncodingSerde.class).in(SINGLETON); + + binder.bind(OpenTelemetry.class).toInstance(OpenTelemetry.noop()); + jsonBinder(binder).addSerializerBinding(Span.class).to(SpanSerializer.class); + jsonBinder(binder).addDeserializerBinding(Span.class).to(SpanDeserializer.class); } @Provides @@ -579,6 +589,7 @@ private HttpRemoteTaskFactory createHttpRemoteTaskFactory( taskInfoCodec, taskUpdateRequestCodec, failTaskRequestCodec, + noopTracer(), new RemoteTaskStats(), dynamicFilterService); } diff --git a/core/trino-main/src/test/java/io/trino/sql/analyzer/TestAnalyzer.java b/core/trino-main/src/test/java/io/trino/sql/analyzer/TestAnalyzer.java index cb3e626328d7..edd5e4083a6c 100644 --- a/core/trino-main/src/test/java/io/trino/sql/analyzer/TestAnalyzer.java +++ b/core/trino-main/src/test/java/io/trino/sql/analyzer/TestAnalyzer.java @@ -7058,7 +7058,7 @@ public ConnectorTransactionHandle getConnectorTransaction(TransactionId transact tablePropertyManager, analyzePropertyManager, new TableProceduresPropertyManager(CatalogServiceProvider.fail("procedures are not supported in testing analyzer"))); - AnalyzerFactory analyzerFactory = new AnalyzerFactory(statementAnalyzerFactory, statementRewrite); + AnalyzerFactory analyzerFactory = new AnalyzerFactory(statementAnalyzerFactory, statementRewrite, plannerContext.getTracer()); return analyzerFactory.createAnalyzer( session, emptyList(), diff --git a/core/trino-main/src/test/java/io/trino/sql/planner/TestingPlannerContext.java b/core/trino-main/src/test/java/io/trino/sql/planner/TestingPlannerContext.java index 636b5630d33a..f62df59bb9f9 100644 --- a/core/trino-main/src/test/java/io/trino/sql/planner/TestingPlannerContext.java +++ b/core/trino-main/src/test/java/io/trino/sql/planner/TestingPlannerContext.java @@ -46,6 +46,7 @@ import java.util.List; import static com.google.common.base.Preconditions.checkState; +import static io.airlift.tracing.Tracing.noopTracer; import static io.trino.client.NodeVersion.UNKNOWN; import static java.util.Objects.requireNonNull; @@ -144,7 +145,8 @@ public PlannerContext build() typeOperators, blockEncodingSerde, typeManager, - functionManager); + functionManager, + noopTracer()); } } } diff --git a/core/trino-main/src/test/java/io/trino/tracing/TestTracingConnectorMetadata.java b/core/trino-main/src/test/java/io/trino/tracing/TestTracingConnectorMetadata.java new file mode 100644 index 000000000000..9d8bf40ac2ae --- /dev/null +++ b/core/trino-main/src/test/java/io/trino/tracing/TestTracingConnectorMetadata.java @@ -0,0 +1,28 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.tracing; + +import io.trino.spi.connector.ConnectorMetadata; +import org.junit.jupiter.api.Test; + +import static io.trino.spi.testing.InterfaceTestUtils.assertAllMethodsOverridden; + +public class TestTracingConnectorMetadata +{ + @Test + public void testEverythingImplemented() + { + assertAllMethodsOverridden(ConnectorMetadata.class, TracingConnectorMetadata.class); + } +} diff --git a/core/trino-main/src/test/java/io/trino/tracing/TestTracingMetadata.java b/core/trino-main/src/test/java/io/trino/tracing/TestTracingMetadata.java new file mode 100644 index 000000000000..606180de7259 --- /dev/null +++ b/core/trino-main/src/test/java/io/trino/tracing/TestTracingMetadata.java @@ -0,0 +1,28 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.tracing; + +import io.trino.metadata.Metadata; +import org.junit.jupiter.api.Test; + +import static io.trino.spi.testing.InterfaceTestUtils.assertAllMethodsOverridden; + +public class TestTracingMetadata +{ + @Test + public void testEverythingImplemented() + { + assertAllMethodsOverridden(Metadata.class, TracingMetadata.class); + } +} diff --git a/core/trino-main/src/test/java/io/trino/tracing/TracingJsonCodec.java b/core/trino-main/src/test/java/io/trino/tracing/TracingJsonCodec.java new file mode 100644 index 000000000000..f796a3f5977e --- /dev/null +++ b/core/trino-main/src/test/java/io/trino/tracing/TracingJsonCodec.java @@ -0,0 +1,41 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.tracing; + +import io.airlift.json.JsonCodecFactory; +import io.airlift.json.ObjectMapperProvider; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.Span; + +import java.util.Map; + +import static io.airlift.tracing.SpanSerialization.SpanDeserializer; +import static io.airlift.tracing.SpanSerialization.SpanSerializer; + +public final class TracingJsonCodec +{ + private TracingJsonCodec() {} + + public static JsonCodecFactory tracingJsonCodecFactory() + { + return new JsonCodecFactory(tracingObjectMapperProvider()); + } + + public static ObjectMapperProvider tracingObjectMapperProvider() + { + return new ObjectMapperProvider() + .withJsonSerializers(Map.of(Span.class, new SpanSerializer(OpenTelemetry.noop()))) + .withJsonDeserializers(Map.of(Span.class, new SpanDeserializer(OpenTelemetry.noop()))); + } +} diff --git a/core/trino-spi/pom.xml b/core/trino-spi/pom.xml index b67431a14314..bd2537d9e441 100644 --- a/core/trino-spi/pom.xml +++ b/core/trino-spi/pom.xml @@ -36,7 +36,17 @@ true - + + io.opentelemetry + opentelemetry-api + + + + io.opentelemetry + opentelemetry-context + runtime + + org.openjdk.jol jol-core @@ -241,6 +251,12 @@ method void io.trino.spi.connector.ConnectorTableProperties::<init>(io.trino.spi.predicate.TupleDomain<io.trino.spi.connector.ColumnHandle>, java.util.Optional<io.trino.spi.connector.ConnectorTablePartitioning>, java.util.Optional<io.trino.spi.connector.DiscretePredicates>, java.util.List<io.trino.spi.connector.LocalProperty<io.trino.spi.connector.ColumnHandle>>) Replaced with the ConnectorTablePartitioning#singleSplitPerPartition. Soft migration is not straightforward. + + true + java.class.externalClassExposedInAPI + io.opentelemetry:opentelemetry-(api|context):.* + Trino SPI depends on OpenTelemetry + diff --git a/core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorContext.java b/core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorContext.java index 288fc373da67..07f8a7f4742d 100644 --- a/core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorContext.java +++ b/core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorContext.java @@ -13,6 +13,8 @@ */ package io.trino.spi.connector; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.Tracer; import io.trino.spi.NodeManager; import io.trino.spi.PageIndexerFactory; import io.trino.spi.PageSorter; @@ -26,6 +28,16 @@ default CatalogHandle getCatalogHandle() throw new UnsupportedOperationException(); } + default OpenTelemetry getOpenTelemetry() + { + throw new UnsupportedOperationException(); + } + + default Tracer getTracer() + { + throw new UnsupportedOperationException(); + } + default NodeManager getNodeManager() { throw new UnsupportedOperationException(); diff --git a/core/trino-spi/src/test/java/io/trino/spi/testing/InterfaceTestUtils.java b/core/trino-spi/src/test/java/io/trino/spi/testing/InterfaceTestUtils.java index 4c89dd5c7f4c..b3bb9cd432fd 100644 --- a/core/trino-spi/src/test/java/io/trino/spi/testing/InterfaceTestUtils.java +++ b/core/trino-spi/src/test/java/io/trino/spi/testing/InterfaceTestUtils.java @@ -26,8 +26,8 @@ import static com.google.common.collect.Sets.difference; import static com.google.common.reflect.Reflection.newProxy; import static java.lang.String.format; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Fail.fail; public final class InterfaceTestUtils { @@ -86,7 +86,7 @@ public static void assertProperForwardingMethodsAreCalled(Class } C forwardingInstance = forwardingInstanceFactory.apply( newProxy(iface, (proxy, expectedMethod, expectedArguments) -> { - assertEquals(actualMethod.getName(), expectedMethod.getName()); + assertThat(actualMethod.getName()).isEqualTo(expectedMethod.getName()); // TODO assert arguments if (actualMethod.getReturnType().isPrimitive()) { diff --git a/lib/trino-filesystem/pom.xml b/lib/trino-filesystem/pom.xml index d210a5952439..1dec78357a8d 100644 --- a/lib/trino-filesystem/pom.xml +++ b/lib/trino-filesystem/pom.xml @@ -37,6 +37,21 @@ guava + + io.opentelemetry + opentelemetry-api + + + + io.opentelemetry + opentelemetry-context + + + + io.opentelemetry + opentelemetry-semconv + + com.google.code.findbugs @@ -45,6 +60,13 @@ + + io.trino + trino-spi + test-jar + test + + org.assertj assertj-core diff --git a/lib/trino-filesystem/src/main/java/io/trino/filesystem/tracing/FileSystemAttributes.java b/lib/trino-filesystem/src/main/java/io/trino/filesystem/tracing/FileSystemAttributes.java new file mode 100644 index 000000000000..bb8fddf09ccb --- /dev/null +++ b/lib/trino-filesystem/src/main/java/io/trino/filesystem/tracing/FileSystemAttributes.java @@ -0,0 +1,30 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.filesystem.tracing; + +import io.opentelemetry.api.common.AttributeKey; + +import static io.opentelemetry.api.common.AttributeKey.longKey; +import static io.opentelemetry.api.common.AttributeKey.stringKey; + +public final class FileSystemAttributes +{ + private FileSystemAttributes() {} + + public static final AttributeKey FILE_LOCATION = stringKey("trino.file.location"); + public static final AttributeKey FILE_SIZE = longKey("trino.file.size"); + public static final AttributeKey FILE_LOCATION_COUNT = longKey("trino.file.location_count"); + public static final AttributeKey FILE_READ_SIZE = longKey("trino.file.read_size"); + public static final AttributeKey FILE_READ_POSITION = longKey("trino.file.read_position"); +} diff --git a/lib/trino-filesystem/src/main/java/io/trino/filesystem/tracing/Tracing.java b/lib/trino-filesystem/src/main/java/io/trino/filesystem/tracing/Tracing.java new file mode 100644 index 000000000000..a205c52d6586 --- /dev/null +++ b/lib/trino-filesystem/src/main/java/io/trino/filesystem/tracing/Tracing.java @@ -0,0 +1,70 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.filesystem.tracing; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; + +import java.util.Optional; + +final class Tracing +{ + private Tracing() {} + + public static Attributes attribute(AttributeKey key, Optional optionalValue) + { + return optionalValue.map(value -> Attributes.of(key, value)) + .orElseGet(Attributes::empty); + } + + public static void withTracing(Span span, CheckedRunnable runnable) + throws E + { + withTracing(span, () -> { + runnable.run(); + return null; + }); + } + + public static T withTracing(Span span, CheckedSupplier supplier) + throws E + { + try (var ignored = span.makeCurrent()) { + return supplier.get(); + } + catch (Throwable t) { + span.setStatus(StatusCode.ERROR, t.getMessage()); + span.recordException(t, Attributes.of(SemanticAttributes.EXCEPTION_ESCAPED, true)); + throw t; + } + finally { + span.end(); + } + } + + public interface CheckedRunnable + { + void run() + throws E; + } + + public interface CheckedSupplier + { + T get() + throws E; + } +} diff --git a/lib/trino-filesystem/src/main/java/io/trino/filesystem/tracing/TracingFileSystem.java b/lib/trino-filesystem/src/main/java/io/trino/filesystem/tracing/TracingFileSystem.java new file mode 100644 index 000000000000..0dbe7788285b --- /dev/null +++ b/lib/trino-filesystem/src/main/java/io/trino/filesystem/tracing/TracingFileSystem.java @@ -0,0 +1,109 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.filesystem.tracing; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.trino.filesystem.FileIterator; +import io.trino.filesystem.TrinoFileSystem; +import io.trino.filesystem.TrinoInputFile; +import io.trino.filesystem.TrinoOutputFile; + +import java.io.IOException; +import java.util.Collection; +import java.util.Optional; + +import static io.trino.filesystem.tracing.Tracing.withTracing; +import static java.util.Objects.requireNonNull; + +final class TracingFileSystem + implements TrinoFileSystem +{ + private final Tracer tracer; + private final TrinoFileSystem delegate; + + public TracingFileSystem(Tracer tracer, TrinoFileSystem delegate) + { + this.tracer = requireNonNull(tracer, "tracer is null"); + this.delegate = requireNonNull(delegate, "delegate is null"); + } + + @Override + public TrinoInputFile newInputFile(String location) + { + return new TracingInputFile(tracer, delegate.newInputFile(location), Optional.empty()); + } + + @Override + public TrinoInputFile newInputFile(String location, long length) + { + return new TracingInputFile(tracer, delegate.newInputFile(location, length), Optional.of(length)); + } + + @Override + public TrinoOutputFile newOutputFile(String location) + { + return new TracingOutputFile(tracer, delegate.newOutputFile(location)); + } + + @Override + public void deleteFile(String location) + throws IOException + { + Span span = tracer.spanBuilder("FileSystem.deleteFile") + .setAttribute(FileSystemAttributes.FILE_LOCATION, location) + .startSpan(); + withTracing(span, () -> delegate.deleteFile(location)); + } + + @Override + public void deleteFiles(Collection locations) + throws IOException + { + Span span = tracer.spanBuilder("FileSystem.deleteFiles") + .setAttribute(FileSystemAttributes.FILE_LOCATION_COUNT, (long) locations.size()) + .startSpan(); + withTracing(span, () -> delegate.deleteFiles(locations)); + } + + @Override + public void deleteDirectory(String location) + throws IOException + { + Span span = tracer.spanBuilder("FileSystem.deleteDirectory") + .setAttribute(FileSystemAttributes.FILE_LOCATION, location) + .startSpan(); + withTracing(span, () -> delegate.deleteDirectory(location)); + } + + @Override + public void renameFile(String source, String target) + throws IOException + { + Span span = tracer.spanBuilder("FileSystem.renameFile") + .setAttribute(FileSystemAttributes.FILE_LOCATION, source) + .startSpan(); + withTracing(span, () -> delegate.renameFile(source, target)); + } + + @Override + public FileIterator listFiles(String location) + throws IOException + { + Span span = tracer.spanBuilder("FileSystem.listFiles") + .setAttribute(FileSystemAttributes.FILE_LOCATION, location) + .startSpan(); + return withTracing(span, () -> delegate.listFiles(location)); + } +} diff --git a/lib/trino-filesystem/src/main/java/io/trino/filesystem/tracing/TracingFileSystemFactory.java b/lib/trino-filesystem/src/main/java/io/trino/filesystem/tracing/TracingFileSystemFactory.java new file mode 100644 index 000000000000..a7fe3b3a001f --- /dev/null +++ b/lib/trino-filesystem/src/main/java/io/trino/filesystem/tracing/TracingFileSystemFactory.java @@ -0,0 +1,47 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.filesystem.tracing; + +import io.opentelemetry.api.trace.Tracer; +import io.trino.filesystem.TrinoFileSystem; +import io.trino.filesystem.TrinoFileSystemFactory; +import io.trino.spi.connector.ConnectorSession; +import io.trino.spi.security.ConnectorIdentity; + +import static java.util.Objects.requireNonNull; + +public final class TracingFileSystemFactory + implements TrinoFileSystemFactory +{ + private final Tracer tracer; + private final TrinoFileSystemFactory delegate; + + public TracingFileSystemFactory(Tracer tracer, TrinoFileSystemFactory delegate) + { + this.tracer = requireNonNull(tracer, "tracer is null"); + this.delegate = requireNonNull(delegate, "delegate is null"); + } + + @Override + public TrinoFileSystem create(ConnectorIdentity identity) + { + return new TracingFileSystem(tracer, delegate.create(identity)); + } + + @Override + public TrinoFileSystem create(ConnectorSession session) + { + return new TracingFileSystem(tracer, delegate.create(session)); + } +} diff --git a/lib/trino-filesystem/src/main/java/io/trino/filesystem/tracing/TracingInput.java b/lib/trino-filesystem/src/main/java/io/trino/filesystem/tracing/TracingInput.java new file mode 100644 index 000000000000..91e766c0a0ee --- /dev/null +++ b/lib/trino-filesystem/src/main/java/io/trino/filesystem/tracing/TracingInput.java @@ -0,0 +1,97 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.filesystem.tracing; + +import io.airlift.slice.Slice; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanBuilder; +import io.opentelemetry.api.trace.Tracer; +import io.trino.filesystem.TrinoInput; + +import java.io.IOException; +import java.util.Optional; + +import static io.trino.filesystem.tracing.Tracing.attribute; +import static io.trino.filesystem.tracing.Tracing.withTracing; +import static java.util.Objects.requireNonNull; + +final class TracingInput + implements TrinoInput +{ + private final Tracer tracer; + private final TrinoInput delegate; + private final String location; + private final Optional fileLength; + + public TracingInput(Tracer tracer, TrinoInput delegate, String location, Optional fileLength) + { + this.tracer = requireNonNull(tracer, "tracer is null"); + this.delegate = requireNonNull(delegate, "delegate is null"); + this.location = requireNonNull(location, "location is null"); + this.fileLength = requireNonNull(fileLength, "fileLength is null"); + } + + @Override + public void readFully(long position, byte[] buffer, int bufferOffset, int bufferLength) + throws IOException + { + Span span = spanBuilder("Input.readFully", bufferLength) + .setAttribute(FileSystemAttributes.FILE_READ_POSITION, position) + .startSpan(); + withTracing(span, () -> delegate.readFully(position, buffer, bufferOffset, bufferLength)); + } + + @Override + public int readTail(byte[] buffer, int bufferOffset, int bufferLength) + throws IOException + { + Span span = spanBuilder("Input.readTail", bufferLength) + .startSpan(); + return withTracing(span, () -> delegate.readTail(buffer, bufferOffset, bufferLength)); + } + + @Override + public Slice readFully(long position, int length) + throws IOException + { + Span span = spanBuilder("Input.readFully", length) + .setAttribute(FileSystemAttributes.FILE_READ_POSITION, position) + .startSpan(); + return withTracing(span, () -> delegate.readFully(position, length)); + } + + @Override + public Slice readTail(int length) + throws IOException + { + Span span = spanBuilder("Input.readTail", length) + .startSpan(); + return withTracing(span, () -> delegate.readTail(length)); + } + + @Override + public void close() + throws IOException + { + delegate.close(); + } + + private SpanBuilder spanBuilder(String name, long readLength) + { + return tracer.spanBuilder(name) + .setAttribute(FileSystemAttributes.FILE_LOCATION, location) + .setAllAttributes(attribute(FileSystemAttributes.FILE_SIZE, fileLength)) + .setAttribute(FileSystemAttributes.FILE_READ_SIZE, readLength); + } +} diff --git a/lib/trino-filesystem/src/main/java/io/trino/filesystem/tracing/TracingInputFile.java b/lib/trino-filesystem/src/main/java/io/trino/filesystem/tracing/TracingInputFile.java new file mode 100644 index 000000000000..2a3a0df65108 --- /dev/null +++ b/lib/trino-filesystem/src/main/java/io/trino/filesystem/tracing/TracingInputFile.java @@ -0,0 +1,106 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.filesystem.tracing; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.trino.filesystem.TrinoInput; +import io.trino.filesystem.TrinoInputFile; +import io.trino.filesystem.TrinoInputStream; + +import java.io.IOException; +import java.time.Instant; +import java.util.Optional; + +import static io.trino.filesystem.tracing.Tracing.attribute; +import static io.trino.filesystem.tracing.Tracing.withTracing; +import static java.util.Objects.requireNonNull; + +final class TracingInputFile + implements TrinoInputFile +{ + private final Tracer tracer; + private final TrinoInputFile delegate; + private final Optional length; + + public TracingInputFile(Tracer tracer, TrinoInputFile delegate, Optional length) + { + this.tracer = requireNonNull(tracer, "tracer is null"); + this.delegate = requireNonNull(delegate, "delegate is null"); + this.length = requireNonNull(length, "length is null"); + } + + @Override + public TrinoInput newInput() + throws IOException + { + Span span = tracer.spanBuilder("InputFile.newInput") + .setAttribute(FileSystemAttributes.FILE_LOCATION, location()) + .setAllAttributes(attribute(FileSystemAttributes.FILE_SIZE, length)) + .startSpan(); + return withTracing(span, () -> new TracingInput(tracer, delegate.newInput(), location(), length)); + } + + @Override + public TrinoInputStream newStream() + throws IOException + { + Span span = tracer.spanBuilder("InputFile.newStream") + .setAttribute(FileSystemAttributes.FILE_LOCATION, location()) + .setAllAttributes(attribute(FileSystemAttributes.FILE_SIZE, length)) + .startSpan(); + return withTracing(span, delegate::newStream); + } + + @Override + public long length() + throws IOException + { + // skip tracing if length is cached, but delegate anyway + if (length.isPresent()) { + return delegate.length(); + } + + Span span = tracer.spanBuilder("InputFile.length") + .setAttribute(FileSystemAttributes.FILE_LOCATION, location()) + .startSpan(); + return withTracing(span, delegate::length); + } + + @Override + public Instant lastModified() + throws IOException + { + Span span = tracer.spanBuilder("InputFile.lastModified") + .setAttribute(FileSystemAttributes.FILE_LOCATION, location()) + .startSpan(); + return withTracing(span, delegate::lastModified); + } + + @Override + public boolean exists() + throws IOException + { + Span span = tracer.spanBuilder("InputFile.exists") + .setAttribute(FileSystemAttributes.FILE_LOCATION, location()) + .startSpan(); + return withTracing(span, delegate::exists); + } + + @Override + public String location() + { + return delegate.location(); + } +} diff --git a/lib/trino-filesystem/src/main/java/io/trino/filesystem/tracing/TracingOutputFile.java b/lib/trino-filesystem/src/main/java/io/trino/filesystem/tracing/TracingOutputFile.java new file mode 100644 index 000000000000..f815dc2c6d76 --- /dev/null +++ b/lib/trino-filesystem/src/main/java/io/trino/filesystem/tracing/TracingOutputFile.java @@ -0,0 +1,84 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.filesystem.tracing; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.trino.filesystem.TrinoOutputFile; +import io.trino.memory.context.AggregatedMemoryContext; + +import java.io.IOException; +import java.io.OutputStream; + +import static io.trino.filesystem.tracing.Tracing.withTracing; +import static java.util.Objects.requireNonNull; + +final class TracingOutputFile + implements TrinoOutputFile +{ + private final Tracer tracer; + private final TrinoOutputFile delegate; + + public TracingOutputFile(Tracer tracer, TrinoOutputFile delegate) + { + this.tracer = requireNonNull(tracer, "tracer is null"); + this.delegate = requireNonNull(delegate, "delete is null"); + } + + @Override + public OutputStream create() + throws IOException + { + Span span = tracer.spanBuilder("OutputFile.create") + .setAttribute(FileSystemAttributes.FILE_LOCATION, location()) + .startSpan(); + return withTracing(span, () -> delegate.create()); + } + + @Override + public OutputStream createOrOverwrite() + throws IOException + { + Span span = tracer.spanBuilder("OutputFile.createOrOverwrite") + .setAttribute(FileSystemAttributes.FILE_LOCATION, location()) + .startSpan(); + return withTracing(span, () -> delegate.createOrOverwrite()); + } + + @Override + public OutputStream create(AggregatedMemoryContext memoryContext) + throws IOException + { + Span span = tracer.spanBuilder("OutputFile.create") + .setAttribute(FileSystemAttributes.FILE_LOCATION, location()) + .startSpan(); + return withTracing(span, () -> delegate.create(memoryContext)); + } + + @Override + public OutputStream createOrOverwrite(AggregatedMemoryContext memoryContext) + throws IOException + { + Span span = tracer.spanBuilder("OutputFile.createOrOverwrite") + .setAttribute(FileSystemAttributes.FILE_LOCATION, location()) + .startSpan(); + return withTracing(span, () -> delegate.createOrOverwrite(memoryContext)); + } + + @Override + public String location() + { + return delegate.location(); + } +} diff --git a/lib/trino-filesystem/src/test/java/io/trino/filesystem/tracing/TestTracing.java b/lib/trino-filesystem/src/test/java/io/trino/filesystem/tracing/TestTracing.java new file mode 100644 index 000000000000..aa49de4e2e10 --- /dev/null +++ b/lib/trino-filesystem/src/test/java/io/trino/filesystem/tracing/TestTracing.java @@ -0,0 +1,36 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.filesystem.tracing; + +import io.trino.filesystem.TrinoFileSystem; +import io.trino.filesystem.TrinoFileSystemFactory; +import io.trino.filesystem.TrinoInput; +import io.trino.filesystem.TrinoInputFile; +import io.trino.filesystem.TrinoOutputFile; +import org.junit.jupiter.api.Test; + +import static io.trino.spi.testing.InterfaceTestUtils.assertAllMethodsOverridden; + +public class TestTracing +{ + @Test + public void testEverythingImplemented() + { + assertAllMethodsOverridden(TrinoFileSystemFactory.class, TracingFileSystemFactory.class); + assertAllMethodsOverridden(TrinoFileSystem.class, TracingFileSystem.class); + assertAllMethodsOverridden(TrinoInputFile.class, TracingInputFile.class); + assertAllMethodsOverridden(TrinoInput.class, TracingInput.class); + assertAllMethodsOverridden(TrinoOutputFile.class, TracingOutputFile.class); + } +} diff --git a/lib/trino-hdfs/pom.xml b/lib/trino-hdfs/pom.xml index 36f8ba836a6d..6fb8dafbcc1b 100644 --- a/lib/trino-hdfs/pom.xml +++ b/lib/trino-hdfs/pom.xml @@ -88,6 +88,11 @@ guice + + io.opentelemetry + opentelemetry-api + + javax.inject javax.inject diff --git a/lib/trino-hdfs/src/main/java/io/trino/filesystem/hdfs/HdfsFileSystemModule.java b/lib/trino-hdfs/src/main/java/io/trino/filesystem/hdfs/HdfsFileSystemModule.java index 541944202fdd..fad80b165b24 100644 --- a/lib/trino-hdfs/src/main/java/io/trino/filesystem/hdfs/HdfsFileSystemModule.java +++ b/lib/trino-hdfs/src/main/java/io/trino/filesystem/hdfs/HdfsFileSystemModule.java @@ -15,7 +15,11 @@ import com.google.inject.Binder; import com.google.inject.Module; +import com.google.inject.Provides; +import com.google.inject.Singleton; +import io.opentelemetry.api.trace.Tracer; import io.trino.filesystem.TrinoFileSystemFactory; +import io.trino.filesystem.tracing.TracingFileSystemFactory; import static com.google.inject.Scopes.SINGLETON; @@ -25,6 +29,13 @@ public class HdfsFileSystemModule @Override public void configure(Binder binder) { - binder.bind(TrinoFileSystemFactory.class).to(HdfsFileSystemFactory.class).in(SINGLETON); + binder.bind(HdfsFileSystemFactory.class).in(SINGLETON); + } + + @Provides + @Singleton + public TrinoFileSystemFactory createFileSystemFactory(Tracer tracer, HdfsFileSystemFactory delegate) + { + return new TracingFileSystemFactory(tracer, delegate); } } diff --git a/lib/trino-hdfs/src/main/java/io/trino/hdfs/HdfsEnvironment.java b/lib/trino-hdfs/src/main/java/io/trino/hdfs/HdfsEnvironment.java index a75cdc5d0a24..84a73d112343 100644 --- a/lib/trino-hdfs/src/main/java/io/trino/hdfs/HdfsEnvironment.java +++ b/lib/trino-hdfs/src/main/java/io/trino/hdfs/HdfsEnvironment.java @@ -13,6 +13,8 @@ */ package io.trino.hdfs; +import com.google.common.annotations.VisibleForTesting; +import io.opentelemetry.api.OpenTelemetry; import io.trino.hadoop.HadoopNative; import io.trino.hdfs.authentication.GenericExceptionAction; import io.trino.hdfs.authentication.HdfsAuthentication; @@ -28,6 +30,7 @@ import java.io.IOException; import java.util.Optional; +import static io.trino.hdfs.FileSystemUtils.getRawFileSystem; import static java.util.Objects.requireNonNull; public class HdfsEnvironment @@ -37,18 +40,27 @@ public class HdfsEnvironment FileSystemManager.registerCache(TrinoFileSystemCache.INSTANCE); } + private final OpenTelemetry openTelemetry; private final HdfsConfiguration hdfsConfiguration; private final HdfsAuthentication hdfsAuthentication; private final Optional newDirectoryPermissions; private final boolean newFileInheritOwnership; private final boolean verifyChecksum; + @VisibleForTesting + public HdfsEnvironment(HdfsConfiguration hdfsConfiguration, HdfsConfig config, HdfsAuthentication hdfsAuthentication) + { + this(OpenTelemetry.noop(), hdfsConfiguration, config, hdfsAuthentication); + } + @Inject public HdfsEnvironment( + OpenTelemetry openTelemetry, HdfsConfiguration hdfsConfiguration, HdfsConfig config, HdfsAuthentication hdfsAuthentication) { + this.openTelemetry = requireNonNull(openTelemetry, "openTelemetry is null"); this.hdfsConfiguration = requireNonNull(hdfsConfiguration, "hdfsConfiguration is null"); this.newFileInheritOwnership = config.isNewFileInheritOwnership(); this.verifyChecksum = config.isVerifyChecksum(); @@ -73,6 +85,9 @@ public FileSystem getFileSystem(ConnectorIdentity identity, Path path, Configura return hdfsAuthentication.doAs(identity, () -> { FileSystem fileSystem = path.getFileSystem(configuration); fileSystem.setVerifyChecksum(verifyChecksum); + if (getRawFileSystem(fileSystem) instanceof OpenTelemetryAwareFileSystem fs) { + fs.setOpenTelemetry(openTelemetry); + } return fileSystem; }); } diff --git a/lib/trino-hdfs/src/main/java/io/trino/hdfs/OpenTelemetryAwareFileSystem.java b/lib/trino-hdfs/src/main/java/io/trino/hdfs/OpenTelemetryAwareFileSystem.java new file mode 100644 index 000000000000..479b631d0bf5 --- /dev/null +++ b/lib/trino-hdfs/src/main/java/io/trino/hdfs/OpenTelemetryAwareFileSystem.java @@ -0,0 +1,21 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.hdfs; + +import io.opentelemetry.api.OpenTelemetry; + +public interface OpenTelemetryAwareFileSystem +{ + void setOpenTelemetry(OpenTelemetry openTelemetry); +} diff --git a/plugin/trino-accumulo/pom.xml b/plugin/trino-accumulo/pom.xml index cf3499267aab..c64615e42aff 100644 --- a/plugin/trino-accumulo/pom.xml +++ b/plugin/trino-accumulo/pom.xml @@ -226,6 +226,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-atop/pom.xml b/plugin/trino-atop/pom.xml index ce0f126a688b..36923fde16b4 100644 --- a/plugin/trino-atop/pom.xml +++ b/plugin/trino-atop/pom.xml @@ -106,6 +106,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-bigquery/pom.xml b/plugin/trino-bigquery/pom.xml index dbfb14ec2482..a77334db2b46 100644 --- a/plugin/trino-bigquery/pom.xml +++ b/plugin/trino-bigquery/pom.xml @@ -282,6 +282,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-blackhole/pom.xml b/plugin/trino-blackhole/pom.xml index cda8b9efca37..2352386c137c 100644 --- a/plugin/trino-blackhole/pom.xml +++ b/plugin/trino-blackhole/pom.xml @@ -70,6 +70,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-cassandra/pom.xml b/plugin/trino-cassandra/pom.xml index cdb8f6079425..5ea8346645cb 100644 --- a/plugin/trino-cassandra/pom.xml +++ b/plugin/trino-cassandra/pom.xml @@ -145,6 +145,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-clickhouse/pom.xml b/plugin/trino-clickhouse/pom.xml index 4cac7ed5919b..1febbf5c1eb0 100644 --- a/plugin/trino-clickhouse/pom.xml +++ b/plugin/trino-clickhouse/pom.xml @@ -96,6 +96,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-delta-lake/pom.xml b/plugin/trino-delta-lake/pom.xml index fc8cd92893e3..26e570acd19d 100644 --- a/plugin/trino-delta-lake/pom.xml +++ b/plugin/trino-delta-lake/pom.xml @@ -227,6 +227,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-delta-lake/src/main/java/io/trino/plugin/deltalake/InternalDeltaLakeConnectorFactory.java b/plugin/trino-delta-lake/src/main/java/io/trino/plugin/deltalake/InternalDeltaLakeConnectorFactory.java index ae1708827841..6bd03ec8eb1e 100644 --- a/plugin/trino-delta-lake/src/main/java/io/trino/plugin/deltalake/InternalDeltaLakeConnectorFactory.java +++ b/plugin/trino-delta-lake/src/main/java/io/trino/plugin/deltalake/InternalDeltaLakeConnectorFactory.java @@ -22,6 +22,8 @@ import io.airlift.bootstrap.LifeCycleManager; import io.airlift.event.client.EventModule; import io.airlift.json.JsonModule; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.Tracer; import io.trino.filesystem.TrinoFileSystemFactory; import io.trino.filesystem.hdfs.HdfsFileSystemModule; import io.trino.hdfs.HdfsModule; @@ -98,6 +100,8 @@ public static Connector createConnector( new DeltaLakeModule(), new DeltaLakeSecurityModule(), binder -> { + binder.bind(OpenTelemetry.class).toInstance(context.getOpenTelemetry()); + binder.bind(Tracer.class).toInstance(context.getTracer()); binder.bind(NodeVersion.class).toInstance(new NodeVersion(context.getNodeManager().getCurrentNode().getVersion())); binder.bind(NodeManager.class).toInstance(context.getNodeManager()); binder.bind(TypeManager.class).toInstance(context.getTypeManager()); diff --git a/plugin/trino-delta-lake/src/test/java/io/trino/plugin/deltalake/TestDeltaLakeDynamicFiltering.java b/plugin/trino-delta-lake/src/test/java/io/trino/plugin/deltalake/TestDeltaLakeDynamicFiltering.java index 65a7097c6b6e..db379ee1f784 100644 --- a/plugin/trino-delta-lake/src/test/java/io/trino/plugin/deltalake/TestDeltaLakeDynamicFiltering.java +++ b/plugin/trino-delta-lake/src/test/java/io/trino/plugin/deltalake/TestDeltaLakeDynamicFiltering.java @@ -16,6 +16,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import io.opentelemetry.api.trace.Span; import io.trino.Session; import io.trino.execution.DynamicFilterConfig; import io.trino.execution.QueryStats; @@ -131,7 +132,7 @@ public void testIncompleteDynamicFilterTimeout() Optional tableHandle = runner.getMetadata().getTableHandle(session, tableName); assertTrue(tableHandle.isPresent()); SplitSource splitSource = runner.getSplitManager() - .getSplits(session, tableHandle.get(), new IncompleteDynamicFilter(), alwaysTrue()); + .getSplits(session, Span.getInvalid(), tableHandle.get(), new IncompleteDynamicFilter(), alwaysTrue()); List splits = new ArrayList<>(); while (!splitSource.isFinished()) { splits.addAll(splitSource.getNextBatch(1000).get().getSplits()); diff --git a/plugin/trino-delta-lake/src/test/java/io/trino/plugin/deltalake/TestDeltaLakeMetadata.java b/plugin/trino-delta-lake/src/test/java/io/trino/plugin/deltalake/TestDeltaLakeMetadata.java index 06f152f0f665..93df2d2a06b5 100644 --- a/plugin/trino-delta-lake/src/test/java/io/trino/plugin/deltalake/TestDeltaLakeMetadata.java +++ b/plugin/trino-delta-lake/src/test/java/io/trino/plugin/deltalake/TestDeltaLakeMetadata.java @@ -19,10 +19,11 @@ import com.google.inject.AbstractModule; import com.google.inject.Injector; import com.google.inject.Provides; +import com.google.inject.Scopes; import io.airlift.bootstrap.Bootstrap; import io.airlift.json.JsonModule; +import io.trino.filesystem.TrinoFileSystemFactory; import io.trino.filesystem.hdfs.HdfsFileSystemFactory; -import io.trino.filesystem.hdfs.HdfsFileSystemModule; import io.trino.hdfs.HdfsEnvironment; import io.trino.plugin.base.CatalogName; import io.trino.plugin.deltalake.metastore.DeltaLakeMetastore; @@ -163,7 +164,7 @@ public void setUp() // test setup binder -> { binder.bind(HdfsEnvironment.class).toInstance(HDFS_ENVIRONMENT); - binder.install(new HdfsFileSystemModule()); + binder.bind(TrinoFileSystemFactory.class).to(HdfsFileSystemFactory.class).in(Scopes.SINGLETON); }, new AbstractModule() { diff --git a/plugin/trino-druid/pom.xml b/plugin/trino-druid/pom.xml index d8f1bbc05f01..69287c4ac7a6 100644 --- a/plugin/trino-druid/pom.xml +++ b/plugin/trino-druid/pom.xml @@ -89,6 +89,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-elasticsearch/pom.xml b/plugin/trino-elasticsearch/pom.xml index e565ddfa7d47..793021c74bc1 100644 --- a/plugin/trino-elasticsearch/pom.xml +++ b/plugin/trino-elasticsearch/pom.xml @@ -258,6 +258,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-example-http/pom.xml b/plugin/trino-example-http/pom.xml index 4f4ecaf98cb1..e81f62caf5d0 100644 --- a/plugin/trino-example-http/pom.xml +++ b/plugin/trino-example-http/pom.xml @@ -90,6 +90,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-example-jdbc/pom.xml b/plugin/trino-example-jdbc/pom.xml index dd40066f1999..602c5c3249ca 100644 --- a/plugin/trino-example-jdbc/pom.xml +++ b/plugin/trino-example-jdbc/pom.xml @@ -81,6 +81,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-exchange-filesystem/pom.xml b/plugin/trino-exchange-filesystem/pom.xml index a5029dce98f8..d0ecf34c8b31 100644 --- a/plugin/trino-exchange-filesystem/pom.xml +++ b/plugin/trino-exchange-filesystem/pom.xml @@ -329,6 +329,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-exchange-hdfs/pom.xml b/plugin/trino-exchange-hdfs/pom.xml index c08625016113..582fc370f64c 100644 --- a/plugin/trino-exchange-hdfs/pom.xml +++ b/plugin/trino-exchange-hdfs/pom.xml @@ -101,6 +101,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-geospatial/pom.xml b/plugin/trino-geospatial/pom.xml index 489a745fdf21..381bb0369c3f 100644 --- a/plugin/trino-geospatial/pom.xml +++ b/plugin/trino-geospatial/pom.xml @@ -74,6 +74,12 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-google-sheets/pom.xml b/plugin/trino-google-sheets/pom.xml index 3af8fd6508a4..0a98f5cd379f 100644 --- a/plugin/trino-google-sheets/pom.xml +++ b/plugin/trino-google-sheets/pom.xml @@ -152,6 +152,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-hive-hadoop2/pom.xml b/plugin/trino-hive-hadoop2/pom.xml index 26b82b918e38..4feebfc08be4 100644 --- a/plugin/trino-hive-hadoop2/pom.xml +++ b/plugin/trino-hive-hadoop2/pom.xml @@ -126,6 +126,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-hive/pom.xml b/plugin/trino-hive/pom.xml index 46806cbbeb73..e1a2751c597c 100644 --- a/plugin/trino-hive/pom.xml +++ b/plugin/trino-hive/pom.xml @@ -230,6 +230,16 @@ failsafe + + io.opentelemetry + opentelemetry-api + + + + io.opentelemetry.instrumentation + opentelemetry-aws-sdk-1.11 + + it.unimi.dsi fastutil diff --git a/plugin/trino-hive/src/main/java/io/trino/plugin/hive/HiveModule.java b/plugin/trino-hive/src/main/java/io/trino/plugin/hive/HiveModule.java index 89df99798772..5528c703fefb 100644 --- a/plugin/trino-hive/src/main/java/io/trino/plugin/hive/HiveModule.java +++ b/plugin/trino-hive/src/main/java/io/trino/plugin/hive/HiveModule.java @@ -20,8 +20,6 @@ import com.google.inject.Scopes; import com.google.inject.multibindings.Multibinder; import io.airlift.event.client.EventClient; -import io.trino.filesystem.TrinoFileSystemFactory; -import io.trino.filesystem.hdfs.HdfsFileSystemFactory; import io.trino.hdfs.TrinoFileSystemCache; import io.trino.hdfs.TrinoFileSystemCacheStats; import io.trino.plugin.base.CatalogName; @@ -127,7 +125,6 @@ public void configure(Binder binder) .as(generator -> generator.generatedNameOf(io.trino.plugin.hive.fs.TrinoFileSystemCache.class)); configBinder(binder).bindConfig(HiveFormatsConfig.class); - binder.bind(TrinoFileSystemFactory.class).to(HdfsFileSystemFactory.class).in(Scopes.SINGLETON); Multibinder pageSourceFactoryBinder = newSetBinder(binder, HivePageSourceFactory.class); pageSourceFactoryBinder.addBinding().to(CsvPageSourceFactory.class).in(Scopes.SINGLETON); diff --git a/plugin/trino-hive/src/main/java/io/trino/plugin/hive/InternalHiveConnectorFactory.java b/plugin/trino-hive/src/main/java/io/trino/plugin/hive/InternalHiveConnectorFactory.java index 0631707d7cd2..2dbc966f44ce 100644 --- a/plugin/trino-hive/src/main/java/io/trino/plugin/hive/InternalHiveConnectorFactory.java +++ b/plugin/trino-hive/src/main/java/io/trino/plugin/hive/InternalHiveConnectorFactory.java @@ -23,6 +23,8 @@ import io.airlift.bootstrap.LifeCycleManager; import io.airlift.event.client.EventModule; import io.airlift.json.JsonModule; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.Tracer; import io.trino.filesystem.hdfs.HdfsFileSystemModule; import io.trino.hdfs.HdfsModule; import io.trino.hdfs.authentication.HdfsAuthenticationModule; @@ -123,6 +125,8 @@ public static Connector createConnector( new HiveProcedureModule(), new MBeanServerModule(), binder -> { + binder.bind(OpenTelemetry.class).toInstance(context.getOpenTelemetry()); + binder.bind(Tracer.class).toInstance(context.getTracer()); binder.bind(NodeVersion.class).toInstance(new NodeVersion(context.getNodeManager().getCurrentNode().getVersion())); binder.bind(NodeManager.class).toInstance(context.getNodeManager()); binder.bind(VersionEmbedder.class).toInstance(context.getVersionEmbedder()); diff --git a/plugin/trino-hive/src/main/java/io/trino/plugin/hive/s3/TrinoS3FileSystem.java b/plugin/trino-hive/src/main/java/io/trino/plugin/hive/s3/TrinoS3FileSystem.java index f145aabb2926..027dc7e2c04f 100644 --- a/plugin/trino-hive/src/main/java/io/trino/plugin/hive/s3/TrinoS3FileSystem.java +++ b/plugin/trino-hive/src/main/java/io/trino/plugin/hive/s3/TrinoS3FileSystem.java @@ -18,6 +18,8 @@ import com.amazonaws.AmazonServiceException; import com.amazonaws.ClientConfiguration; import com.amazonaws.Protocol; +import com.amazonaws.Request; +import com.amazonaws.Response; import com.amazonaws.SdkClientException; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.AWSCredentialsProvider; @@ -32,6 +34,7 @@ import com.amazonaws.event.ProgressEvent; import com.amazonaws.event.ProgressEventType; import com.amazonaws.event.ProgressListener; +import com.amazonaws.handlers.RequestHandler2; import com.amazonaws.metrics.RequestMetricCollector; import com.amazonaws.regions.DefaultAwsRegionProviderChain; import com.amazonaws.regions.Region; @@ -83,9 +86,12 @@ import io.airlift.log.Logger; import io.airlift.units.DataSize; import io.airlift.units.Duration; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.awssdk.v1_11.AwsSdkTelemetry; import io.trino.hdfs.FSDataInputStreamTail; import io.trino.hdfs.FileSystemWithBatchDelete; import io.trino.hdfs.MemoryAwareFileSystem; +import io.trino.hdfs.OpenTelemetryAwareFileSystem; import io.trino.memory.context.AggregatedMemoryContext; import io.trino.memory.context.LocalMemoryContext; import org.apache.hadoop.conf.Configurable; @@ -181,7 +187,7 @@ public class TrinoS3FileSystem extends FileSystem - implements FileSystemWithBatchDelete, MemoryAwareFileSystem + implements FileSystemWithBatchDelete, MemoryAwareFileSystem, OpenTelemetryAwareFileSystem { public static final String S3_USER_AGENT_PREFIX = "trino.s3.user-agent-prefix"; public static final String S3_CREDENTIALS_PROVIDER = "trino.s3.credentials-provider"; @@ -275,6 +281,7 @@ public class TrinoS3FileSystem private String s3RoleSessionName; private final ExecutorService uploadExecutor = newCachedThreadPool(threadsNamed("s3-upload-%s")); + private final ForwardingRequestHandler forwardingRequestHandler = new ForwardingRequestHandler(); @Override public void initialize(URI uri, Configuration conf) @@ -386,6 +393,17 @@ private void closeSuper() super.close(); } + @Override + public void setOpenTelemetry(OpenTelemetry openTelemetry) + { + requireNonNull(openTelemetry, "openTelemetry is null"); + forwardingRequestHandler.setDelegateIfAbsent(() -> + AwsSdkTelemetry.builder(openTelemetry) + .setCaptureExperimentalSpanAttributes(true) + .build() + .newRequestHandler()); + } + @Override public String getScheme() { @@ -1077,6 +1095,8 @@ else if (region != null) { clientBuilder.setForceGlobalBucketAccessEnabled(true); } + clientBuilder.setRequestHandlers(forwardingRequestHandler); + return clientBuilder.build(); } @@ -2048,4 +2068,41 @@ private enum DeletePrefixResult ALL_KEYS_DELETED, DELETE_KEYS_FAILURE } + + private static class ForwardingRequestHandler + extends RequestHandler2 + { + private volatile RequestHandler2 delegate; + + public synchronized void setDelegateIfAbsent(Supplier supplier) + { + if (delegate == null) { + delegate = supplier.get(); + } + } + + @Override + public void beforeRequest(Request request) + { + if (delegate != null) { + delegate.beforeRequest(request); + } + } + + @Override + public void afterResponse(Request request, Response response) + { + if (delegate != null) { + delegate.afterResponse(request, response); + } + } + + @Override + public void afterError(Request request, Response response, Exception e) + { + if (delegate != null) { + delegate.afterError(request, response, e); + } + } + } } diff --git a/plugin/trino-http-event-listener/pom.xml b/plugin/trino-http-event-listener/pom.xml index b2e1d75f4739..f853b93b783d 100644 --- a/plugin/trino-http-event-listener/pom.xml +++ b/plugin/trino-http-event-listener/pom.xml @@ -103,6 +103,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + io.trino diff --git a/plugin/trino-hudi/pom.xml b/plugin/trino-hudi/pom.xml index 0325a72112b6..e12b97b7ea97 100644 --- a/plugin/trino-hudi/pom.xml +++ b/plugin/trino-hudi/pom.xml @@ -261,6 +261,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-hudi/src/main/java/io/trino/plugin/hudi/InternalHudiConnectorFactory.java b/plugin/trino-hudi/src/main/java/io/trino/plugin/hudi/InternalHudiConnectorFactory.java index fc20cc4f2919..9988d4579b5a 100644 --- a/plugin/trino-hudi/src/main/java/io/trino/plugin/hudi/InternalHudiConnectorFactory.java +++ b/plugin/trino-hudi/src/main/java/io/trino/plugin/hudi/InternalHudiConnectorFactory.java @@ -21,6 +21,8 @@ import io.airlift.bootstrap.LifeCycleManager; import io.airlift.event.client.EventModule; import io.airlift.json.JsonModule; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.Tracer; import io.trino.filesystem.hdfs.HdfsFileSystemModule; import io.trino.hdfs.HdfsModule; import io.trino.hdfs.authentication.HdfsAuthenticationModule; @@ -76,6 +78,8 @@ public static Connector createConnector( new HdfsFileSystemModule(), new MBeanServerModule(), binder -> { + binder.bind(OpenTelemetry.class).toInstance(context.getOpenTelemetry()); + binder.bind(Tracer.class).toInstance(context.getTracer()); binder.bind(NodeVersion.class).toInstance(new NodeVersion(context.getNodeManager().getCurrentNode().getVersion())); binder.bind(NodeManager.class).toInstance(context.getNodeManager()); binder.bind(TypeManager.class).toInstance(context.getTypeManager()); diff --git a/plugin/trino-iceberg/pom.xml b/plugin/trino-iceberg/pom.xml index bb0dfe04b66d..87be6398dba3 100644 --- a/plugin/trino-iceberg/pom.xml +++ b/plugin/trino-iceberg/pom.xml @@ -332,6 +332,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/InternalIcebergConnectorFactory.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/InternalIcebergConnectorFactory.java index 8de2fee63e44..384cdcb229e6 100644 --- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/InternalIcebergConnectorFactory.java +++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/InternalIcebergConnectorFactory.java @@ -21,6 +21,8 @@ import io.airlift.bootstrap.LifeCycleManager; import io.airlift.event.client.EventModule; import io.airlift.json.JsonModule; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.Tracer; import io.trino.filesystem.TrinoFileSystemFactory; import io.trino.filesystem.hdfs.HdfsFileSystemModule; import io.trino.hdfs.HdfsModule; @@ -93,6 +95,8 @@ public static Connector createConnector( new HdfsAuthenticationModule(), new MBeanServerModule(), binder -> { + binder.bind(OpenTelemetry.class).toInstance(context.getOpenTelemetry()); + binder.bind(Tracer.class).toInstance(context.getTracer()); binder.bind(NodeVersion.class).toInstance(new NodeVersion(context.getNodeManager().getCurrentNode().getVersion())); binder.bind(NodeManager.class).toInstance(context.getNodeManager()); binder.bind(TypeManager.class).toInstance(context.getTypeManager()); diff --git a/plugin/trino-ignite/pom.xml b/plugin/trino-ignite/pom.xml index 036f578127f8..f9d8905b9194 100644 --- a/plugin/trino-ignite/pom.xml +++ b/plugin/trino-ignite/pom.xml @@ -116,6 +116,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-jmx/pom.xml b/plugin/trino-jmx/pom.xml index 3e2858b77874..cf7a4c5256a3 100644 --- a/plugin/trino-jmx/pom.xml +++ b/plugin/trino-jmx/pom.xml @@ -104,6 +104,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-kafka/pom.xml b/plugin/trino-kafka/pom.xml index 576072a7720e..59cf0ef733b0 100644 --- a/plugin/trino-kafka/pom.xml +++ b/plugin/trino-kafka/pom.xml @@ -197,6 +197,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-kinesis/pom.xml b/plugin/trino-kinesis/pom.xml index d599a8c3d18d..cc2edf20bfb0 100644 --- a/plugin/trino-kinesis/pom.xml +++ b/plugin/trino-kinesis/pom.xml @@ -142,6 +142,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-kudu/pom.xml b/plugin/trino-kudu/pom.xml index 58530cd052c5..2bc28aa90daf 100644 --- a/plugin/trino-kudu/pom.xml +++ b/plugin/trino-kudu/pom.xml @@ -126,6 +126,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/TestKuduIntegrationDynamicFilter.java b/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/TestKuduIntegrationDynamicFilter.java index 31ce6614458d..2acefbe75459 100644 --- a/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/TestKuduIntegrationDynamicFilter.java +++ b/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/TestKuduIntegrationDynamicFilter.java @@ -16,6 +16,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.primitives.Ints; +import io.opentelemetry.api.trace.Span; import io.trino.Session; import io.trino.execution.QueryStats; import io.trino.metadata.QualifiedObjectName; @@ -101,7 +102,7 @@ public void testIncompleteDynamicFilterTimeout() Optional tableHandle = runner.getMetadata().getTableHandle(session, tableName); assertTrue(tableHandle.isPresent()); SplitSource splitSource = runner.getSplitManager() - .getSplits(session, tableHandle.get(), new IncompleteDynamicFilter(), alwaysTrue()); + .getSplits(session, Span.getInvalid(), tableHandle.get(), new IncompleteDynamicFilter(), alwaysTrue()); List splits = new ArrayList<>(); while (!splitSource.isFinished()) { splits.addAll(splitSource.getNextBatch(1000).get().getSplits()); diff --git a/plugin/trino-local-file/pom.xml b/plugin/trino-local-file/pom.xml index 1b2644016fee..f7f5ea1a35cd 100644 --- a/plugin/trino-local-file/pom.xml +++ b/plugin/trino-local-file/pom.xml @@ -85,6 +85,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-mariadb/pom.xml b/plugin/trino-mariadb/pom.xml index 365aa7c89472..3ba9cad82270 100644 --- a/plugin/trino-mariadb/pom.xml +++ b/plugin/trino-mariadb/pom.xml @@ -90,6 +90,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-memory/pom.xml b/plugin/trino-memory/pom.xml index fd2e77f89814..d07114b8a825 100644 --- a/plugin/trino-memory/pom.xml +++ b/plugin/trino-memory/pom.xml @@ -102,6 +102,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-ml/pom.xml b/plugin/trino-ml/pom.xml index bdf4d9000c28..64b7b1390f2b 100644 --- a/plugin/trino-ml/pom.xml +++ b/plugin/trino-ml/pom.xml @@ -81,6 +81,12 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-mongodb/pom.xml b/plugin/trino-mongodb/pom.xml index e346423badc4..2cc4b423acee 100644 --- a/plugin/trino-mongodb/pom.xml +++ b/plugin/trino-mongodb/pom.xml @@ -127,6 +127,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-mysql-event-listener/pom.xml b/plugin/trino-mysql-event-listener/pom.xml index 190f1d7e4faa..8a38595758e9 100644 --- a/plugin/trino-mysql-event-listener/pom.xml +++ b/plugin/trino-mysql-event-listener/pom.xml @@ -98,6 +98,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-mysql/pom.xml b/plugin/trino-mysql/pom.xml index d538af7a5be9..754bf2266ad9 100644 --- a/plugin/trino-mysql/pom.xml +++ b/plugin/trino-mysql/pom.xml @@ -104,6 +104,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-oracle/pom.xml b/plugin/trino-oracle/pom.xml index 19f378010ade..cf186b71d353 100644 --- a/plugin/trino-oracle/pom.xml +++ b/plugin/trino-oracle/pom.xml @@ -115,6 +115,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-password-authenticators/pom.xml b/plugin/trino-password-authenticators/pom.xml index 7b8fa75f66af..54f6318396c4 100644 --- a/plugin/trino-password-authenticators/pom.xml +++ b/plugin/trino-password-authenticators/pom.xml @@ -98,6 +98,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-phoenix5/pom.xml b/plugin/trino-phoenix5/pom.xml index 34d3cfd9765f..26f938522399 100644 --- a/plugin/trino-phoenix5/pom.xml +++ b/plugin/trino-phoenix5/pom.xml @@ -158,6 +158,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-pinot/pom.xml b/plugin/trino-pinot/pom.xml index 045ef8d77f82..922fb6e9ea1a 100755 --- a/plugin/trino-pinot/pom.xml +++ b/plugin/trino-pinot/pom.xml @@ -519,6 +519,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-postgresql/pom.xml b/plugin/trino-postgresql/pom.xml index 6dbd3a82cc8f..60a5740bc209 100644 --- a/plugin/trino-postgresql/pom.xml +++ b/plugin/trino-postgresql/pom.xml @@ -117,6 +117,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-prometheus/pom.xml b/plugin/trino-prometheus/pom.xml index f184088d09d1..e88d5b3989cf 100644 --- a/plugin/trino-prometheus/pom.xml +++ b/plugin/trino-prometheus/pom.xml @@ -134,6 +134,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-raptor-legacy/pom.xml b/plugin/trino-raptor-legacy/pom.xml index 320df8713d12..7b0bc522c2a5 100644 --- a/plugin/trino-raptor-legacy/pom.xml +++ b/plugin/trino-raptor-legacy/pom.xml @@ -181,6 +181,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-redis/pom.xml b/plugin/trino-redis/pom.xml index 632570617ab6..3abd1d5e2f02 100644 --- a/plugin/trino-redis/pom.xml +++ b/plugin/trino-redis/pom.xml @@ -133,6 +133,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-redshift/pom.xml b/plugin/trino-redshift/pom.xml index 9ece89160b67..fd838d1c45fd 100644 --- a/plugin/trino-redshift/pom.xml +++ b/plugin/trino-redshift/pom.xml @@ -102,6 +102,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-resource-group-managers/pom.xml b/plugin/trino-resource-group-managers/pom.xml index 8c35a6fdc823..691d4fa5ecc4 100644 --- a/plugin/trino-resource-group-managers/pom.xml +++ b/plugin/trino-resource-group-managers/pom.xml @@ -157,6 +157,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-session-property-managers/pom.xml b/plugin/trino-session-property-managers/pom.xml index de1296e148b9..8aaa59b75e0f 100644 --- a/plugin/trino-session-property-managers/pom.xml +++ b/plugin/trino-session-property-managers/pom.xml @@ -138,6 +138,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-singlestore/pom.xml b/plugin/trino-singlestore/pom.xml index fb5f9aea1432..0ac6c81be106 100644 --- a/plugin/trino-singlestore/pom.xml +++ b/plugin/trino-singlestore/pom.xml @@ -102,6 +102,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-sqlserver/pom.xml b/plugin/trino-sqlserver/pom.xml index e2a2438e114c..e34a47cebcef 100644 --- a/plugin/trino-sqlserver/pom.xml +++ b/plugin/trino-sqlserver/pom.xml @@ -119,6 +119,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-thrift/pom.xml b/plugin/trino-thrift/pom.xml index d4137518686d..5cbc1e0db432 100644 --- a/plugin/trino-thrift/pom.xml +++ b/plugin/trino-thrift/pom.xml @@ -147,6 +147,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-tpcds/pom.xml b/plugin/trino-tpcds/pom.xml index ed43cebb3342..6f020e0f9d5b 100644 --- a/plugin/trino-tpcds/pom.xml +++ b/plugin/trino-tpcds/pom.xml @@ -104,6 +104,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/plugin/trino-tpch/pom.xml b/plugin/trino-tpch/pom.xml index 39e283ecb19b..74868173ddba 100644 --- a/plugin/trino-tpch/pom.xml +++ b/plugin/trino-tpch/pom.xml @@ -61,6 +61,18 @@ provided + + io.opentelemetry + opentelemetry-api + provided + + + + io.opentelemetry + opentelemetry-context + provided + + org.openjdk.jol jol-core diff --git a/pom.xml b/pom.xml index 10215805dace..67a9a8c97398 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ io.airlift airbase - 134 + 135 io.trino @@ -49,7 +49,7 @@ 1.10.2 2.7.7-1 4.11.1 - 228 + 229 9.0.0 1.11.1 ${dep.airlift.version} @@ -70,10 +70,8 @@ 1.1.0 3.22.2 4.5.0 - 1.8.0 4.1.79.Final 5.13.0 - 2.0.7 79 @@ -833,8 +831,10 @@ io.airlift - bootstrap + bom + pom ${dep.airlift.version} + import @@ -843,144 +843,18 @@ 1.4 - - io.airlift - concurrent - ${dep.airlift.version} - - - - io.airlift - configuration - ${dep.airlift.version} - - - - io.airlift - dbpool - ${dep.airlift.version} - - - - io.airlift - discovery - ${dep.airlift.version} - - - - io.airlift - event - ${dep.airlift.version} - - - - io.airlift - http-client - ${dep.airlift.version} - - - - io.airlift - http-server - ${dep.airlift.version} - - - - io.airlift - jaxrs - ${dep.airlift.version} - - - - io.airlift - jaxrs-testing - ${dep.airlift.version} - - - org.hamcrest - hamcrest - - - - - - io.airlift - jmx - ${dep.airlift.version} - - - - io.airlift - jmx-http - ${dep.airlift.version} - - io.airlift joni 2.1.5.3 - - io.airlift - json - ${dep.airlift.version} - - - - io.airlift - log - ${dep.airlift.version} - - - - io.airlift - log-manager - ${dep.airlift.version} - - - - io.airlift - node - ${dep.airlift.version} - - - - io.airlift - openmetrics - ${dep.airlift.version} - - io.airlift parameternames 1.4 - - io.airlift - security - ${dep.airlift.version} - - - - io.airlift - stats - ${dep.airlift.version} - - - - io.airlift - testing - ${dep.airlift.version} - - - - io.airlift - trace-token - ${dep.airlift.version} - - io.airlift units @@ -990,7 +864,7 @@ io.airlift.discovery discovery-server - 1.32 + 1.33 io.airlift @@ -1926,24 +1800,6 @@ 19.0.0 - - org.jetbrains.kotlin - kotlin-stdlib - ${dep.kotlin.version} - - - - org.jetbrains.kotlin - kotlin-stdlib-common - ${dep.kotlin.version} - - - - org.jetbrains.kotlin - kotlin-stdlib-jdk8 - ${dep.kotlin.version} - - org.locationtech.jts jts-core @@ -2366,14 +2222,6 @@ true - - org.apache.maven.plugins - maven-compiler-plugin - - false - - - org.apache.maven.plugins maven-surefire-plugin diff --git a/service/trino-proxy/pom.xml b/service/trino-proxy/pom.xml index 7cf7e461d84f..28373707ac07 100644 --- a/service/trino-proxy/pom.xml +++ b/service/trino-proxy/pom.xml @@ -88,6 +88,11 @@ trace-token + + io.airlift + tracing + + io.airlift units diff --git a/service/trino-proxy/src/main/java/io/trino/proxy/TrinoProxy.java b/service/trino-proxy/src/main/java/io/trino/proxy/TrinoProxy.java index bed547e9e297..242786b0b9e5 100644 --- a/service/trino-proxy/src/main/java/io/trino/proxy/TrinoProxy.java +++ b/service/trino-proxy/src/main/java/io/trino/proxy/TrinoProxy.java @@ -25,10 +25,15 @@ import io.airlift.log.Logger; import io.airlift.node.NodeModule; import io.airlift.tracetoken.TraceTokenModule; +import io.airlift.tracing.TracingModule; import org.weakref.jmx.guice.MBeanModule; +import static com.google.common.base.MoreObjects.firstNonNull; + public final class TrinoProxy { + private static final String VERSION = firstNonNull(TrinoProxy.class.getPackage().getImplementationVersion(), "unknown"); + private TrinoProxy() {} public static void start(Module... extraModules) @@ -42,6 +47,7 @@ public static void start(Module... extraModules) .add(new JmxModule()) .add(new LogJmxModule()) .add(new TraceTokenModule()) + .add(new TracingModule("trino-proxy", VERSION)) .add(new EventModule()) .add(new ProxyModule()) .add(extraModules) diff --git a/testing/trino-benchmark/pom.xml b/testing/trino-benchmark/pom.xml index 419427fcba81..b42425c374cf 100644 --- a/testing/trino-benchmark/pom.xml +++ b/testing/trino-benchmark/pom.xml @@ -83,6 +83,11 @@ guava + + io.opentelemetry + opentelemetry-api + + io.airlift diff --git a/testing/trino-benchmark/src/main/java/io/trino/benchmark/AbstractOperatorBenchmark.java b/testing/trino-benchmark/src/main/java/io/trino/benchmark/AbstractOperatorBenchmark.java index 6cd816c7a2e9..186d3b0edb20 100644 --- a/testing/trino-benchmark/src/main/java/io/trino/benchmark/AbstractOperatorBenchmark.java +++ b/testing/trino-benchmark/src/main/java/io/trino/benchmark/AbstractOperatorBenchmark.java @@ -19,6 +19,7 @@ import io.airlift.stats.CpuTimer; import io.airlift.stats.TestingGcMonitor; import io.airlift.units.DataSize; +import io.opentelemetry.api.trace.Span; import io.trino.Session; import io.trino.execution.StageId; import io.trino.execution.TaskId; @@ -82,7 +83,6 @@ import static io.trino.SystemSessionProperties.getFilterAndProjectMinOutputPageSize; import static io.trino.execution.executor.PrioritizedSplitRunner.SPLIT_RUN_QUANTA; import static io.trino.spi.connector.Constraint.alwaysTrue; -import static io.trino.spi.connector.DynamicFilter.EMPTY; import static io.trino.spi.type.BigintType.BIGINT; import static io.trino.sql.analyzer.TypeSignatureProvider.fromTypes; import static io.trino.sql.planner.TypeAnalyzer.createTestingTypeAnalyzer; @@ -209,7 +209,7 @@ public OperatorFactory duplicate() private Split getLocalQuerySplit(Session session, TableHandle handle) { - SplitSource splitSource = localQueryRunner.getSplitManager().getSplits(session, handle, EMPTY, alwaysTrue()); + SplitSource splitSource = localQueryRunner.getSplitManager().getSplits(session, Span.getInvalid(), handle, DynamicFilter.EMPTY, alwaysTrue()); List splits = new ArrayList<>(); while (!splitSource.isFinished()) { splits.addAll(getNextBatch(splitSource)); diff --git a/testing/trino-testing/pom.xml b/testing/trino-testing/pom.xml index 5b0ee3f0e57a..7100f1439a1c 100644 --- a/testing/trino-testing/pom.xml +++ b/testing/trino-testing/pom.xml @@ -135,6 +135,11 @@ okhttp + + io.opentelemetry + opentelemetry-api + + joda-time joda-time diff --git a/testing/trino-testing/src/main/java/io/trino/testing/AbstractTestEngineOnlyQueries.java b/testing/trino-testing/src/main/java/io/trino/testing/AbstractTestEngineOnlyQueries.java index 6290d1ef6215..3e65390b297d 100644 --- a/testing/trino-testing/src/main/java/io/trino/testing/AbstractTestEngineOnlyQueries.java +++ b/testing/trino-testing/src/main/java/io/trino/testing/AbstractTestEngineOnlyQueries.java @@ -23,6 +23,7 @@ import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import com.google.common.collect.Ordering; +import io.opentelemetry.api.trace.Span; import io.trino.Session; import io.trino.SystemSessionProperties; import io.trino.spi.session.PropertyMetadata; @@ -5332,6 +5333,7 @@ public void testShowSession() { Session session = new Session( getSession().getQueryId(), + Span.getInvalid(), Optional.empty(), getSession().isClientTransactionSupport(), getSession().getIdentity(), diff --git a/testing/trino-tests/pom.xml b/testing/trino-tests/pom.xml index fd840a6b9ade..7a250edfaf55 100644 --- a/testing/trino-tests/pom.xml +++ b/testing/trino-tests/pom.xml @@ -100,6 +100,12 @@ runtime + + io.opentelemetry + opentelemetry-api + runtime + + javax.inject javax.inject diff --git a/testing/trino-tests/src/test/java/io/trino/execution/QueryRunnerUtil.java b/testing/trino-tests/src/test/java/io/trino/execution/QueryRunnerUtil.java index 87ddb86786a3..ca1786c26a67 100644 --- a/testing/trino-tests/src/test/java/io/trino/execution/QueryRunnerUtil.java +++ b/testing/trino-tests/src/test/java/io/trino/execution/QueryRunnerUtil.java @@ -14,6 +14,7 @@ package io.trino.execution; import com.google.common.collect.ImmutableSet; +import io.opentelemetry.api.trace.Span; import io.trino.Session; import io.trino.dispatcher.DispatchManager; import io.trino.server.BasicQueryInfo; @@ -35,7 +36,7 @@ private QueryRunnerUtil() {} public static QueryId createQuery(DistributedQueryRunner queryRunner, Session session, String sql) { DispatchManager dispatchManager = queryRunner.getCoordinator().getDispatchManager(); - getFutureValue(dispatchManager.createQuery(session.getQueryId(), Slug.createNew(), TestingSessionContext.fromSession(session), sql)); + getFutureValue(dispatchManager.createQuery(session.getQueryId(), Span.getInvalid(), Slug.createNew(), TestingSessionContext.fromSession(session), sql)); return session.getQueryId(); } diff --git a/testing/trino-tests/src/test/java/io/trino/tests/TestMetadataManager.java b/testing/trino-tests/src/test/java/io/trino/tests/TestMetadataManager.java index e9cb45d476a7..b883f3429532 100644 --- a/testing/trino-tests/src/test/java/io/trino/tests/TestMetadataManager.java +++ b/testing/trino-tests/src/test/java/io/trino/tests/TestMetadataManager.java @@ -15,6 +15,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import io.opentelemetry.api.trace.Span; import io.trino.connector.MockConnectorFactory; import io.trino.connector.MockConnectorTableHandle; import io.trino.dispatcher.DispatchManager; @@ -30,6 +31,7 @@ import io.trino.testing.DistributedQueryRunner; import io.trino.testing.TestingSessionContext; import io.trino.tests.tpch.TpchQueryRunnerBuilder; +import io.trino.tracing.TracingMetadata; import io.trino.transaction.TransactionBuilder; import org.intellij.lang.annotations.Language; import org.testng.annotations.AfterClass; @@ -87,7 +89,7 @@ public Iterable getConnectorFactories() } }); queryRunner.createCatalog("upper_case_schema_catalog", "mock"); - metadataManager = (MetadataManager) queryRunner.getMetadata(); + metadataManager = (MetadataManager) ((TracingMetadata) queryRunner.getMetadata()).getDelegate(); } @AfterClass(alwaysRun = true) @@ -138,6 +140,7 @@ public void testMetadataIsClearedAfterQueryCanceled() QueryId queryId = dispatchManager.createQueryId(); dispatchManager.createQuery( queryId, + Span.getInvalid(), Slug.createNew(), TestingSessionContext.fromSession(TEST_SESSION), "SELECT * FROM lineitem") diff --git a/testing/trino-tests/src/test/java/io/trino/tests/TestQueryManager.java b/testing/trino-tests/src/test/java/io/trino/tests/TestQueryManager.java index 5c919b934bd6..b89ad0ca78b9 100644 --- a/testing/trino-tests/src/test/java/io/trino/tests/TestQueryManager.java +++ b/testing/trino-tests/src/test/java/io/trino/tests/TestQueryManager.java @@ -13,6 +13,7 @@ */ package io.trino.tests; +import io.opentelemetry.api.trace.Span; import io.trino.Session; import io.trino.client.ClientCapabilities; import io.trino.dispatcher.DispatchManager; @@ -73,6 +74,7 @@ public void testFailQuery() QueryId queryId = dispatchManager.createQueryId(); dispatchManager.createQuery( queryId, + Span.getInvalid(), Slug.createNew(), TestingSessionContext.fromSession(TEST_SESSION), "SELECT * FROM lineitem")