private async Task PrepareOnTheRestOfTheNodes(PrepareRequest request, IInternalSession session)
        {
            Host         host;
            HostDistance distance;
            var          lbp        = session.Cluster.Configuration.Policies.LoadBalancingPolicy;
            var          tasks      = new List <Task>();
            var          triedHosts = new Dictionary <IPEndPoint, Exception>();

            while ((host = GetNextHost(lbp, out distance)) != null)
            {
                var connection = await RequestHandler
                                 .GetConnectionFromHostAsync(host, distance, session, triedHosts).ConfigureAwait(false);

                if (connection == null)
                {
                    continue;
                }
                // For each valid connection, send a the request in parallel
                tasks.Add(connection.Send(request));
            }
            try
            {
                await Task.WhenAll(tasks).ConfigureAwait(false);
            }
            catch
            {
                // Don't consider individual failures
            }
        }
        /// <summary>
        /// Executes the prepare request on the first host selected by the load balancing policy.
        /// When <see cref="QueryOptions.IsPrepareOnAllHosts"/> is enabled, it prepares on the rest of the hosts in
        /// parallel.
        /// </summary>
        internal static async Task <PreparedStatement> Prepare(IInternalSession session, Serializer serializer,
                                                               PrepareRequest request)
        {
            var cluster = session.InternalCluster;
            var lbp     = cluster.Configuration.Policies.LoadBalancingPolicy;
            var handler = new PrepareHandler(serializer, lbp.NewQueryPlan(session.Keyspace, null).GetEnumerator());
            var ps      = await handler.Prepare(request, session, null).ConfigureAwait(false);

            var psAdded = cluster.PreparedQueries.GetOrAdd(ps.Id, ps);

            if (ps != psAdded)
            {
                Logger.Warning("Re-preparing already prepared query is generally an anti-pattern and will likely " +
                               "affect performance. Consider preparing the statement only once. Query='{0}'", ps.Cql);
                ps = psAdded;
            }
            var prepareOnAllHosts = cluster.Configuration.QueryOptions.IsPrepareOnAllHosts();

            if (!prepareOnAllHosts)
            {
                return(ps);
            }
            await handler.PrepareOnTheRestOfTheNodes(request, session).ConfigureAwait(false);

            return(ps);
        }
Beispiel #3
0
        private async Task <PreparedStatement> GetPreparedStatement(
            Response response, PrepareRequest request, string keyspace, ICluster cluster)
        {
            if (response == null)
            {
                throw new DriverInternalError("Response can not be null");
            }
            var resultResponse = response as ResultResponse;

            if (resultResponse == null)
            {
                throw new DriverInternalError("Excepted ResultResponse, obtained " + response.GetType().FullName);
            }
            var output = resultResponse.Output;

            if (!(output is OutputPrepared))
            {
                throw new DriverInternalError("Expected prepared response, obtained " + output.GetType().FullName);
            }
            var prepared = (OutputPrepared)output;
            var ps       = new PreparedStatement(prepared.Metadata, prepared.QueryId, prepared.ResultMetadataId,
                                                 request.Query, keyspace, _serializer)
            {
                IncomingPayload = resultResponse.CustomPayload
            };

            await FillRoutingInfo(ps, cluster).ConfigureAwait(false);

            return(ps);
        }
        private async Task <PreparedStatement> Prepare(PrepareRequest request, IInternalSession session,
                                                       Dictionary <IPEndPoint, Exception> triedHosts)
        {
            if (triedHosts == null)
            {
                triedHosts = new Dictionary <IPEndPoint, Exception>();
            }
            // It may throw a NoHostAvailableException which we should yield to the caller
            var connection = await GetNextConnection(session, triedHosts)
                             .ConfigureAwait(false);

            Response response;

            try
            {
                response = await connection.Send(request).ConfigureAwait(false);
            }
            catch (Exception ex) when(CanBeRetried(ex))
            {
                triedHosts[connection.Address] = ex;
                return(await Prepare(request, session, triedHosts).ConfigureAwait(false));
            }
            return(await GetPreparedStatement(response, request, connection.Keyspace, session.Cluster)
                   .ConfigureAwait(false));
        }
        internal static async Task PrepareAllQueries(Host host, ICollection <PreparedStatement> preparedQueries,
                                                     IEnumerable <IInternalSession> sessions)
        {
            if (preparedQueries.Count == 0)
            {
                return;
            }
            // Get the first connection for that host, in any of the existings connection pool
            var connection = sessions.SelectMany(s => s.GetExistingPool(host.Address)?.ConnectionsSnapshot)
                             .FirstOrDefault();

            if (connection == null)
            {
                Logger.Info($"Could not re-prepare queries on {host.Address} as there wasn't an open connection to" +
                            " the node");
                return;
            }
            Logger.Info($"Re-preparing {preparedQueries.Count} queries on {host.Address}");
            var tasks = new List <Task>(preparedQueries.Count);

            using (var semaphore = new SemaphoreSlim(64, 64))
            {
                foreach (var query in preparedQueries.Select(ps => ps.Cql))
                {
                    var request = new PrepareRequest(query);
                    await semaphore.WaitAsync().ConfigureAwait(false);

                    async Task SendSingle()
                    {
                        try
                        {
                            await connection.Send(request).ConfigureAwait(false);
                        }
                        finally
                        {
                            // ReSharper disable once AccessToDisposedClosure
                            // There is no risk of being disposed as the list of tasks is awaited upon below
                            semaphore.Release();
                        }
                    }

                    tasks.Add(Task.Run(SendSingle));
                }
                try
                {
                    await Task.WhenAll(tasks).ConfigureAwait(false);
                }
                catch (Exception ex)
                {
                    Logger.Error($"There was an error when re-preparing queries on {host.Address}", ex);
                }
            }
        }
Beispiel #6
0
        public async Task <PreparedStatement> Prepare(
            PrepareRequest request, IInternalSession session, IEnumerator <Host> queryPlan)
        {
            var prepareResult = await SendRequestToOneNode(session, queryPlan, request).ConfigureAwait(false);

            if (session.Cluster.Configuration.QueryOptions.IsPrepareOnAllHosts())
            {
                await _reprepareHandler.ReprepareOnAllNodesWithExistingConnections(session, request, prepareResult).ConfigureAwait(false);
            }

            return(prepareResult.PreparedStatement);
        }
        /// <inheritdoc />
        public async Task ReprepareOnAllNodesWithExistingConnections(
            IInternalSession session, PrepareRequest request, PrepareResult prepareResult)
        {
            var pools        = session.GetPools();
            var hosts        = session.InternalCluster.AllHosts();
            var poolsByHosts = pools.Join(
                hosts, po => po.Key,
                h => h.Address,
                (pair, host) => new { host, pair.Value }).ToDictionary(k => k.host, k => k.Value);

            if (poolsByHosts.Count == 0)
            {
                PrepareHandler.Logger.Warning("Could not prepare query on all hosts because there are no connection pools.");
                return;
            }

            using (var semaphore = new SemaphoreSlim(64, 64))
            {
                var tasks = new List <Task>(poolsByHosts.Count);
                foreach (var poolKvp in poolsByHosts)
                {
                    if (poolKvp.Key.Address.Equals(prepareResult.HostAddress))
                    {
                        continue;
                    }

                    if (prepareResult.TriedHosts.ContainsKey(poolKvp.Key.Address))
                    {
                        PrepareHandler.Logger.Warning(
                            $"An error occured while attempting to prepare query on {{0}}:{Environment.NewLine}{{1}}",
                            poolKvp.Key.Address,
                            prepareResult.TriedHosts[poolKvp.Key.Address]);
                        continue;
                    }

                    await semaphore.WaitAsync().ConfigureAwait(false);

                    tasks.Add(ReprepareOnSingleNodeAsync(poolKvp, prepareResult.PreparedStatement, request, semaphore, false));
                }

                await Task.WhenAll(tasks).ConfigureAwait(false);
            }
        }
Beispiel #8
0
        /// <summary>
        /// Sends a prepare request before retrying the statement
        /// </summary>
        private void PrepareAndRetry(PreparedQueryNotFoundException ex, Host host)
        {
            BoundStatement boundStatement = null;

            if (_parent.Statement is BoundStatement statement1)
            {
                boundStatement = statement1;
            }
            else if (_parent.Statement is BatchStatement batch)
            {
                bool SearchBoundStatement(Statement s) =>
                s is BoundStatement statement && statement.PreparedStatement.Id.SequenceEqual(ex.UnknownId);

                boundStatement = (BoundStatement)batch.Queries.FirstOrDefault(SearchBoundStatement);
            }
            if (boundStatement == null)
            {
                throw new DriverInternalError("Expected Bound or batch statement");
            }

            var preparedKeyspace = boundStatement.PreparedStatement.Keyspace;
            var request          = new PrepareRequest(_parent.Serializer, boundStatement.PreparedStatement.Cql, preparedKeyspace, null);

            if (!_parent.Serializer.ProtocolVersion.SupportsKeyspaceInRequest() &&
                preparedKeyspace != null && _session.Keyspace != preparedKeyspace)
            {
                Logger.Warning(string.Format("The statement was prepared using another keyspace, changing the keyspace temporarily to" +
                                             " {0} and back to {1}. Use keyspace and table identifiers in your queries and avoid switching keyspaces.",
                                             preparedKeyspace, _session.Keyspace));

                _connection
                .SetKeyspace(preparedKeyspace)
                .ContinueSync(_ =>
                {
                    Send(request, host, NewReprepareResponseHandler(ex));
                    return(true);
                });
                return;
            }
            Send(request, host, NewReprepareResponseHandler(ex));
        }
Beispiel #9
0
        /// <summary>
        /// Sends a prepare request before retrying the statement
        /// </summary>
        private void PrepareAndRetry(byte[] id)
        {
            Logger.Info(String.Format("Query {0} is not prepared on {1}, preparing before retrying executing.", BitConverter.ToString(id), _connection.Address));
            BoundStatement boundStatement = null;

            if (_parent.Statement is BoundStatement)
            {
                boundStatement = (BoundStatement)_parent.Statement;
            }
            else if (_parent.Statement is BatchStatement)
            {
                var batch = (BatchStatement)_parent.Statement;

                bool SearchBoundStatement(Statement s) =>
                s is BoundStatement && ((BoundStatement)s).PreparedStatement.Id.SequenceEqual(id);

                boundStatement = (BoundStatement)batch.Queries.FirstOrDefault(SearchBoundStatement);
            }
            if (boundStatement == null)
            {
                throw new DriverInternalError("Expected Bound or batch statement");
            }
            var request = new PrepareRequest(boundStatement.PreparedStatement.Cql);

            if (boundStatement.PreparedStatement.Keyspace != null && _session.Keyspace != boundStatement.PreparedStatement.Keyspace)
            {
                Logger.Warning(String.Format("The statement was prepared using another keyspace, changing the keyspace temporarily to" +
                                             " {0} and back to {1}. Use keyspace and table identifiers in your queries and avoid switching keyspaces.",
                                             boundStatement.PreparedStatement.Keyspace, _session.Keyspace));

                _connection
                .SetKeyspace(boundStatement.PreparedStatement.Keyspace)
                .ContinueSync(_ =>
                {
                    Send(request, ReprepareResponseHandler);
                    return(true);
                });
                return;
            }
            Send(request, ReprepareResponseHandler);
        }
Beispiel #10
0
        private async Task <PrepareResult> SendRequestToOneNode(IInternalSession session, IEnumerator <Host> queryPlan, PrepareRequest request)
        {
            var triedHosts = new Dictionary <IPEndPoint, Exception>();

            while (true)
            {
                // It may throw a NoHostAvailableException which we should yield to the caller
                var hostConnectionTuple = await GetNextConnection(session, queryPlan, triedHosts).ConfigureAwait(false);

                var connection = hostConnectionTuple.Item2;
                var host       = hostConnectionTuple.Item1;
                try
                {
                    var result = await connection.Send(request).ConfigureAwait(false);

                    return(new PrepareResult
                    {
                        PreparedStatement = await GetPreparedStatement(result, request, request.Keyspace ?? connection.Keyspace, session.Cluster).ConfigureAwait(false),
                        TriedHosts = triedHosts,
                        HostAddress = host.Address
                    });
                }
                catch (Exception ex) when(PrepareHandler.CanBeRetried(ex))
                {
                    triedHosts[host.Address] = ex;
                }
            }
        }