/// <summary> /// Executes the query async. /// </summary> /// <typeparam name="T"> class representing the rows returned </typeparam> /// <returns> </returns> public async Task <CqlDataReader <T> > ExecuteReaderAsync <T>() where T : class, new() { var logger = _connection.LoggerManager.GetLogger("CqlSharp.CqlCommand.ExecuteReader"); logger.LogVerbose("Waiting on Throttle"); //wait until allowed _connection.Throttle.Wait(); try { //capture current command state QueryExecutionState state = CaptureState(); logger.LogVerbose("State captured, start executing query"); ResultFrame result = await RunWithRetry(ExecuteInternalAsync, state, logger).ConfigureAwait(false); if (result.ResultOpcode != ResultOpcode.Rows) { var ex = new CqlException("Can not create a DataReader for non-select query."); logger.LogError("Error executing reader: {0}", ex); throw ex; } var reader = new CqlDataReader <T>(result); logger.LogQuery("Query {0} returned {1} results", _cql, reader.Count); return(reader); } finally { _connection.Throttle.Release(); } }
/// <summary> /// Executes the non-query async. /// </summary> /// <returns> A ICqlQueryResult of type rows, Void, SchemaChange or SetKeySpace </returns> /// <exception cref="CqlException">Unexpected type of result received</exception> public async Task <ICqlQueryResult> ExecuteNonQueryAsync() { var logger = _connection.LoggerManager.GetLogger("CqlSharp.CqlCommand.ExecuteNonQuery"); logger.LogVerbose("Waiting on Throttle"); //wait until allowed _connection.Throttle.Wait(); try { //capture current command state QueryExecutionState state = CaptureState(); logger.LogVerbose("State captured, start executing query"); ResultFrame result = await RunWithRetry(ExecuteInternalAsync, state, logger).ConfigureAwait(false); switch (result.ResultOpcode) { case ResultOpcode.Rows: var reader = new CqlDataReader(result); logger.LogQuery("Query {0} returned {1} results", _cql, reader.Count); return(reader); case ResultOpcode.Void: logger.LogQuery("Query {0} executed succesfully", _cql); return(new CqlVoid { TracingId = result.TracingId }); case ResultOpcode.SchemaChange: logger.LogQuery("Query {0} resulted in {1}.{2} {3}", _cql, result.Keyspace, result.Table, result.Change); return(new CqlSchemaChange { TracingId = result.TracingId, Keyspace = result.Keyspace, Table = result.Table, Change = result.Change }); case ResultOpcode.SetKeyspace: logger.LogQuery("Query {0} resulted in keyspace set to {1}", _cql, result.Keyspace); return(new CqlSetKeyspace { TracingId = result.TracingId, Keyspace = result.Keyspace }); default: throw new CqlException("Unexpected type of result received"); } } finally { _connection.Throttle.Release(); } }
/// <summary> /// Captures the state. /// </summary> /// <returns> </returns> private QueryExecutionState CaptureState() { var state = new QueryExecutionState { Values = _parameters == null ? null : _parameters.Values, TracingEnabled = EnableTracing, UseBuffering = UseBuffering, PartitionKey = PartitionKey != null?PartitionKey.Copy() : null, Load = Load }; return(state); }
/// <summary> /// Prepares the query async on the given connection. Returns immediatly if the query is already /// prepared. /// </summary> /// <param name="connection">The connection.</param> /// <param name="state">captured state</param> /// <param name="logger">The logger.</param> /// <returns></returns> /// <exception cref="CqlException">Unexpected frame received + response.OpCode</exception> /// <exception cref="System.Exception">Unexpected frame received + response.OpCode</exception> private async Task <ResultFrame> PrepareInternalAsync(Connection connection, QueryExecutionState state, Logger logger) { //check if already prepared for this connection ResultFrame result; var prepareResults = _connection.GetPrepareResultsFor(_cql); if (!prepareResults.TryGetValue(connection.Address, out result)) { //create prepare frame var query = new PrepareFrame(_cql); //update frame with tracing option if requested if (state.TracingEnabled) { query.Flags |= FrameFlags.Tracing; } logger.LogVerbose("No prepare results available. Sending prepare {0} using {1}", _cql, connection); //send prepare request Frame response = await connection.SendRequestAsync(query, logger).ConfigureAwait(false); result = response as ResultFrame; if (result == null) { throw new CqlException("Unexpected frame received " + response.OpCode); } prepareResults[connection.Address] = result; } else { logger.LogVerbose("Reusing cached preparation results"); } //set as prepared _prepared = true; //set parameters collection if not done so before if (_parameters == null) { _parameters = new CqlParameterCollection(result.Schema); } return(result); }
/// <summary> /// Executes the query async on the given connection /// </summary> /// <param name="connection">The connection.</param> /// <param name="state">The state.</param> /// <param name="logger">The logger.</param> /// <returns></returns> /// <exception cref="CqlException">Unexpected frame received</exception> private async Task <ResultFrame> ExecuteInternalAsync(Connection connection, QueryExecutionState state, Logger logger) { Frame query; if (_prepared) { ResultFrame prepareResult = await PrepareInternalAsync(connection, state, logger).ConfigureAwait(false); query = new ExecuteFrame(prepareResult.PreparedQueryId, _level, state.Values); logger.LogVerbose("Sending execute {0} using {1}", _cql, connection); } else { query = new QueryFrame(_cql, _level); logger.LogVerbose("Sending query {0} using {1}", _cql, connection); } //update frame with tracing option if requested if (state.TracingEnabled) { query.Flags |= FrameFlags.Tracing; } Frame response = await connection.SendRequestAsync(query, logger, state.Load).ConfigureAwait(false); var result = response as ResultFrame; if (result != null) { //read all the data into a buffer, if requested if (state.UseBuffering) { logger.LogVerbose("Buffering used, reading all data"); await result.BufferDataAsync().ConfigureAwait(false); } return(result); } throw new CqlException("Unexpected frame received " + response.OpCode); }
/// <summary> /// Runs the given function, and retries it on a new connection when I/O or node errors occur /// </summary> /// <param name="executeFunc">The function to execute.</param> /// <param name="state">The state.</param> /// <param name="logger">The logger.</param> /// <returns></returns> /// <exception cref="CqlException">Failed to return query result after max amount of attempts</exception> private async Task <ResultFrame> RunWithRetry( Func <Connection, QueryExecutionState, Logger, Task <ResultFrame> > executeFunc, QueryExecutionState state, Logger logger) { int attempts = _connection.Config.MaxQueryRetries; //keep trying until faulted for (int attempt = 0; attempt < attempts; attempt++) { //get me a connection Connection connection; using (logger.ThreadBinding()) connection = _connection.GetConnection(state.PartitionKey); //execute try { return(await executeFunc(connection, state, logger).ConfigureAwait(false)); } catch (ProtocolException pex) { if (attempt == attempts - 1) { logger.LogError("Query failed after {0} attempts with error {1}", attempts, pex); throw; } switch (pex.Code) { case ErrorCode.IsBootstrapping: case ErrorCode.Overloaded: //IO or node status related error, go for rerun logger.LogWarning("Query to {0} failed because server returned {1}, going for retry", connection, pex.Code.ToString()); continue; default: logger.LogWarning("Query failed with {0} error: {1}", pex.Code.ToString(), pex.Message); //some other Cql error (syntax ok?), quit throw; } } catch (Exception ex) { if (attempt == attempts - 1) { //out of attempts logger.LogError("Query failed after {0} attempts with error {1}", attempts, ex); throw; } if (_connection.Config.ConnectionStrategy == ConnectionStrategy.Exclusive) { //using exclusive connection strategy. If connection fails, do not recover logger.LogError("Query failed on exclusive connection with error {0}", ex); throw; } //connection probable collapsed, go an try again logger.LogWarning("Query to {0} failed, going for retry. {1}", connection, ex); } } throw new CqlException("Failed to return query result after max amount of attempts"); }