class QueryStateMachine {

    public boolean transitionToFailed(Throwable throwable)
    {
        cleanupQueryQuietly();
        queryStateTimer.endQuery();

        // NOTE: The failure cause must be set before triggering the state change, so
        // listeners can observe the exception. This is safe because the failure cause
        // can only be observed if the transition to FAILED is successful.
        requireNonNull(throwable, "throwable is null");
        failureCause.compareAndSet(null, toFailure(throwable));

        QueryState oldState = queryState.trySet(FAILED);
        if (oldState.isDone()) {
            QUERY_STATE_LOG.debug(throwable, "Failure after query %s finished", queryId);
            return false;
        }

        try {
            QUERY_STATE_LOG.debug(throwable, "Query %s failed", queryId);
            session.getTransactionId().ifPresent(transactionId -> {
                try {
                    if (transactionManager.transactionExists(transactionId) && transactionManager.isAutoCommit(transactionId)) {
                        transactionManager.asyncAbort(transactionId);
                        return;
                    }
                }
                catch (RuntimeException e) {
                    // This shouldn't happen but be safe and just fail the transaction directly
                    QUERY_STATE_LOG.error(e, "Error aborting transaction for failed query. Transaction will be failed directly");
                }
                transactionManager.fail(transactionId);
            });
        }
        finally {
            // if the query has not started, then there is no final query info to wait for
            if (oldState.ordinal() <= PLANNING.ordinal()) {
                finalQueryInfo.compareAndSet(Optional.empty(), Optional.of(getQueryInfo(Optional.empty())));
            }
        }

        return true;
    }

    private  QueryStateMachine(
            String query,
            Optional<String> preparedQuery,
            Session session,
            URI self,
            ResourceGroupId resourceGroup,
            TransactionManager transactionManager,
            Executor executor,
            Ticker ticker,
            Metadata metadata,
            WarningCollector warningCollector,
            Optional<QueryType> queryType);

}