A Cassandra protocol data packet
Inheritance: IDisposable
示例#1
0
        /// <summary>
        ///   Submits a frame, and waits until response is received
        /// </summary>
        /// <param name="frame"> The frame to send. </param>
        /// <param name="logger">logger to write progress to</param>
        /// <param name="load"> the load indication of the request. Used for balancing queries over nodes and connections </param>
        /// <param name="isConnecting">indicates if this request is send as part of connection setup protocol</param>
        /// <returns> </returns>
        internal async Task<Frame> SendRequestAsync(Frame frame, Logger logger, int load = 1, bool isConnecting = false)
        {
            try
            {
                //make sure we're already connected
                if (!isConnecting)
                    await OpenAsync(logger).ConfigureAwait(false);

                //make sure we are connected
                if (!IsConnected)
                    throw new IOException("Not connected");

                //count the operation
                Interlocked.Increment(ref _activeRequests);

                //increase the load
                UpdateLoad(load, logger);

                logger.LogVerbose("Waiting for connection lock on {0}...", this);

                //wait until allowed to submit a frame
                await _frameSubmitLock.WaitAsync().ConfigureAwait(false);

                //get a task that gets completed when a response is received
                var waitTask = new TaskCompletionSource<Frame>();

                //get a stream id, and store wait task under that id
                sbyte id;
                lock (_availableQueryIds)
                {
                    id = _availableQueryIds.Dequeue();
                    _openRequests.Add(id, waitTask);
                }

                try
                {
                    //send frame
                    frame.Stream = id;

                    //serialize frame outside lock
                    Stream frameBytes = frame.GetFrameBytes(_allowCompression && !isConnecting, _cluster.Config.CompressionTreshold);

                    await _writeLock.WaitAsync().ConfigureAwait(false);
                    try
                    {
                        //final check to make sure we're connected
                        if (_connectionState != 1)
                            throw new IOException("Not connected");

                        logger.LogVerbose("Sending {0} Frame with Id {1}, to {2}", frame.OpCode, id, this);

                        await frameBytes.CopyToAsync(_writeStream).ConfigureAwait(false);
                    }
                    finally
                    {
                        _writeLock.Release();
                        frameBytes.Dispose();
                    }

                    //wait until response is received
                    Frame response = await waitTask.Task.ConfigureAwait(false);

                    logger.LogVerbose("{0} response for frame with Id {1} received from {2}", response.OpCode, id, Address);

                    //throw error if result is an error
                    var error = response as ErrorFrame;
                    if (error != null)
                    {
                        throw error.Exception;
                    }

                    //return response
                    return response;
                }
                finally
                {
                    //return request slot to the pool
                    lock (_availableQueryIds)
                    {
                        _openRequests.Remove(id);
                        _availableQueryIds.Enqueue(id);
                    }

                    //allow another frame to be send
                    _frameSubmitLock.Release();

                    //reduce load, we are done
                    Interlocked.Decrement(ref _activeRequests);
                    UpdateLoad(-load, logger);
                }
            }
            catch (ProtocolException pex)
            {
                switch (pex.Code)
                {
                    case ErrorCode.IsBootstrapping:
                    case ErrorCode.Overloaded:

                        using (logger.ThreadBinding())
                        {
                            //IO or node status related error, dispose this connection
                            Dispose(true, pex);
                            throw;
                        }

                    default:
                        //some other Cql error (syntax ok?), simply rethrow
                        throw;
                }
            }
            catch (Exception ex)
            {
                using (logger.ThreadBinding())
                {
                    //connection collapsed, dispose this connection
                    Dispose(true, ex);
                    throw;
                }
            }
        }
示例#2
0
        /// <summary>
        ///  Submits a frame, and waits until response is received (complex version)
        /// </summary>
        /// <param name="frame"> The frame to send. </param>
        /// <param name="logger"> logger to write progress to </param>
        /// <param name="load"> the load indication of the request. Used for balancing queries over nodes and connections </param>
        /// <param name="token"> The token. </param>
        /// <returns></returns>
        private async Task<Frame> SendRequestAsyncComplex(Frame frame, Logger logger, int load, CancellationToken token)
        {
            //make sure connection is open
            await OpenAsync(logger).AutoConfigureAwait();

            //send request
            var requestTask = SendRequestAsyncInternal(frame, logger, load, token);
            
            //take fast path if we can skip cancellation support
            if(!token.CanBeCanceled)
                return await requestTask.AutoConfigureAwait();
            
            //setup task that completes when token is set to cancelled
            var cancelTask = new TaskCompletionSource<bool>();
            using (token.Register(s => ((TaskCompletionSource<bool>)s).TrySetResult(true), cancelTask))
            {
                //wait for either sendTask or cancellation task to complete
                if (requestTask != await Task.WhenAny(requestTask, cancelTask.Task).AutoConfigureAwait())
                {
                    //ignore/log any exception of the handled task
                    // ReSharper disable once UnusedVariable
                    var logError = requestTask.ContinueWith((sendTask, log) =>
                    {
                        if (sendTask.Exception == null)
                            return;

                        var logger1 = (Logger)log;
                        logger1.LogWarning(
                            "Cancelled query threw exception: {0}",
                            sendTask.Exception.InnerException);
                    }, logger,
                                                     TaskContinuationOptions.OnlyOnFaulted |
                                                     TaskContinuationOptions.ExecuteSynchronously);

                    //get this request cancelled
                    throw new OperationCanceledException(token);
                }
            }
            return await requestTask.AutoConfigureAwait();
        }
示例#3
0
        /// <summary>
        /// Sends the request async internal. Cancellation supported until request is send, after which answer must be handled
        /// to avoid connection corruption.
        /// </summary>
        /// <param name="frame"> The frame. </param>
        /// <param name="logger"> The logger. </param>
        /// <param name="load"> The load. </param>
        /// <param name="token"> The token. </param>
        /// <returns> </returns>
        /// <exception cref="System.IO.IOException">Not connected</exception>
        private async Task<Frame> SendRequestAsyncInternal(Frame frame, Logger logger, int load, CancellationToken token)
        {
            try
            {
                //make sure we aren't disposed
                if(_connectionState == ConnectionState.Closed)
                    throw new ObjectDisposedException(ToString());

                //count the operation
                Interlocked.Increment(ref _activeRequests);

                if (_connectionState == ConnectionState.Connected)
                {
                    //increase the load
                    UpdateLoad(load, logger);
                    
                    //wait until frame id is available to submit a frame
                    logger.LogVerbose("Waiting for connection lock on {0}...", this);
                    if (Scheduler.RunningSynchronously)
                        _frameSubmitLock.Wait(token);
                    else
                        await _frameSubmitLock.WaitAsync(token).AutoConfigureAwait();
                }

                //get a task that gets completed when a response is received
                var waitTask = new TaskCompletionSource<Frame>();

                //get a stream id, and store wait task under that id
                short id;
                lock (_availableQueryIds)
                {
                    id = _availableQueryIds.Count > 0 ? _availableQueryIds.Dequeue() : _usedQueryIds++;
                    _openRequests.Add(id, waitTask);
                }

                try
                {
                    //send frame
                    frame.Stream = id;

                    //set protocol version in use
                    frame.ProtocolVersion = Node.ProtocolVersion;

                    //serialize frame outside lock
                    PoolMemoryStream frameBytes = frame.GetFrameBytes(_allowCompression && (_connectionState!=ConnectionState.Connecting),
                                                                      _config.CompressionTreshold);

                    //wait to get access to stream
                    if (Scheduler.RunningSynchronously)
                        _writeLock.Wait(token);
                    else
                        await _writeLock.WaitAsync(token).AutoConfigureAwait();

                    try
                    {
                        //make very sure we aren't disposed
                        if (_connectionState == ConnectionState.Closed)
                            throw new ObjectDisposedException(ToString());

                        logger.LogVerbose("Sending {0} Frame with Id {1} over {2}", frame.OpCode, id, this);

                        //write frame to stream, don't use cancelToken to prevent half-written frames
                        if (Scheduler.RunningSynchronously)
                            frameBytes.CopyTo(_writeStream);
                        else
                            await frameBytes.CopyToAsync(_writeStream).AutoConfigureAwait();

                        //unblock readloop to read result
                        _readLock.Release();
                    }
                    finally
                    {
                        _writeLock.Release();
                        frameBytes.Dispose();
                    }

                    //wait until response is received
                    Frame response = await waitTask.Task.AutoConfigureAwait();

                    logger.LogVerbose("Received {0} Frame with Id {1} on {2}", response.OpCode, id,
                                      this);

                    //read frame content
                    await response.ReadFrameContentAsync().AutoConfigureAwait();

                    //throw error if result is an error
                    var error = response as ErrorFrame;
                    if (error != null)
                    {
                        //dispose error frame
                        error.Dispose();

                        //throw exception
                        throw error.Exception;
                    }

                    //check for keyspace change
                    var keyspaceChange = response as ResultFrame;
                    if (keyspaceChange != null && keyspaceChange.CqlResultType == CqlResultType.SetKeyspace)
                    {
                        logger.LogVerbose("{0} changed KeySpace to \"{1}\"", this, keyspaceChange.Keyspace);
                        CurrentKeySpace = keyspaceChange.Keyspace;
                    }

                    //dispose frame, when cancellation requested
                    if (token.IsCancellationRequested)
                    {
                        response.Dispose();
                        throw new OperationCanceledException(token);
                    }

                    //return response
                    return response;
                }
                finally
                {
                    //return request slot to the pool
                    lock (_availableQueryIds)
                    {
                        _openRequests.Remove(id);
                        _availableQueryIds.Enqueue(id);
                    }

                    if (_connectionState == ConnectionState.Connected)
                    {
                        //allow another frame to be send
                        _frameSubmitLock.Release();

                        //reduce load, we are done
                        UpdateLoad(-load, logger);
                    }

                    //decrease the amount of operations
                    Interlocked.Decrement(ref _activeRequests);
                }
            }
            catch (OperationCanceledException)
            {
                throw;
            }
            catch (ProtocolException pex)
            {
                switch (pex.Code)
                {
                    case ErrorCode.IsBootstrapping:
                    case ErrorCode.Overloaded:

                        using (logger.ThreadBinding())
                        {
                            //IO or node status related error, dispose this connection
                            Close(true);
                            throw;
                        }

                    default:
                        //some other Cql error (syntax ok?), simply rethrow
                        throw;
                }
            }
            catch (ObjectDisposedException odex)
            {
                throw new IOException("Connection closed while processing request", odex);
            }
            catch (Exception)
            {
                using (logger.ThreadBinding())
                {
                    //connection collapsed, dispose this connection
                    Close(true);
                    throw;
                }
            }
        }
示例#4
0
        /// <summary>
        /// Submits a frame, and waits until response is received
        /// </summary>
        /// <param name="frame"> The frame to send. </param>
        /// <param name="logger"> logger to write progress to </param>
        /// <param name="load"> the load indication of the request. Used for balancing queries over nodes and connections </param>
        /// <param name="token"> The token. </param>
        /// <returns> </returns>
        public Task<Frame> SendRequestAsync(Frame frame, Logger logger, int load, CancellationToken token)
        {
            if (_connectionState == ConnectionState.Closed)
                throw new ObjectDisposedException(ToString());

            if(_connectionState == ConnectionState.Connected && !token.CanBeCanceled)
                return SendRequestAsyncInternal(frame, logger, load, token);

            return SendRequestAsyncComplex(frame, logger, load, token);
        }