Exemple #1
0
        /// <summary>
        /// Connects synchronously to the host and starts reading
        /// </summary>
        /// <exception cref="SocketException">Throws a SocketException when the connection could not be established with the host</exception>
        public Task <bool> Connect()
        {
            var tcs = TaskHelper.TaskCompletionSourceWithTimeout <bool>(
                Options.ConnectTimeoutMillis,
                () => new SocketException((int)SocketError.TimedOut));
            var socketConnectTask = tcs.Task;
            var eventArgs         = new SocketAsyncEventArgs
            {
                RemoteEndPoint = IPEndPoint
            };

            eventArgs.Completed += (sender, e) =>
            {
                if (e.SocketError != SocketError.Success)
                {
                    tcs.TrySetException(new SocketException((int)e.SocketError));
                    return;
                }
                tcs.TrySetResult(true);
                e.Dispose();
            };

            try
            {
                _socket.ConnectAsync(eventArgs);
            }
            catch (Exception ex)
            {
                return(TaskHelper.FromException <bool>(ex));
            }
            //Prepare read and write
            //There are 2 modes: using SocketAsyncEventArgs (most performant) and Stream mode with APM methods
            if (SSLOptions == null && !Options.UseStreamMode)
            {
                return(socketConnectTask.ContinueSync(_ =>
                {
                    _logger.Verbose("Socket connected, start reading using SocketEventArgs interface");
                    //using SocketAsyncEventArgs
                    _receiveSocketEvent = new SocketAsyncEventArgs();
                    _receiveSocketEvent.SetBuffer(_receiveBuffer, 0, _receiveBuffer.Length);
                    _receiveSocketEvent.Completed += OnReceiveCompleted;
                    _sendSocketEvent = new SocketAsyncEventArgs();
                    _sendSocketEvent.Completed += OnSendCompleted;
                    ReceiveAsync();
                    return true;
                }));
            }
            if (SSLOptions == null)
            {
                return(socketConnectTask.ContinueSync(_ =>
                {
                    _logger.Verbose("Socket connected, start reading using Stream interface");
                    //Stream mode: not the most performant but it is a choice
                    _socketStream = new NetworkStream(_socket);
                    ReceiveAsync();
                    return true;
                }));
            }
            return(socketConnectTask.Then(_ => ConnectSsl()));
        }
Exemple #2
0
        private Task <QueryTrace> GetQueryTrace(QueryTrace trace, HashedWheelTimer timer, int attempt)
        {
            if (attempt >= TraceMaxAttempts)
            {
                return(TaskHelper.FromException <QueryTrace>(
                           new TraceRetrievalException(string.Format("Unable to retrieve complete query trace after {0} tries", TraceMaxAttempts))));
            }
            var sessionQuery  = string.Format(SelectTraceSessions, trace.TraceId);
            var fetchAndAdapt = Cc
                                .QueryAsync(sessionQuery)
                                .ContinueSync(rs =>
            {
                var sessionRow = rs.FirstOrDefault();
                if (sessionRow == null || sessionRow.IsNull("duration"))
                {
                    return(null);
                }
                trace.RequestType    = sessionRow.GetValue <string>("request");
                trace.DurationMicros = sessionRow.GetValue <int>("duration");
                trace.Coordinator    = sessionRow.GetValue <IPAddress>("coordinator");
                trace.Parameters     = sessionRow.GetValue <IDictionary <string, string> >("parameters");
                trace.StartedAt      = sessionRow.GetValue <DateTimeOffset>("started_at").ToFileTime();
                if (sessionRow.GetColumn("client") != null)
                {
                    //client column was not present in previous
                    trace.ClientAddress = sessionRow.GetValue <IPAddress>("client");
                }
                return(trace);
            });

            return(fetchAndAdapt.Then(loadedTrace =>
            {
                if (loadedTrace == null)
                {
                    //Trace session was not loaded
                    return TaskHelper
                    .ScheduleExecution(() => GetQueryTrace(trace, timer, attempt + 1), timer, TraceAttemptDelay)
                    .Unwrap();
                }
                var eventsQuery = string.Format(SelectTraceEvents, trace.TraceId);
                return Cc
                .QueryAsync(eventsQuery)
                .ContinueSync(rs =>
                {
                    var events = rs
                                 .Select(row => new QueryTrace.Event(
                                             row.GetValue <string>("activity"),
                                             row.GetValue <TimeUuid>("event_id").GetDate(),
                                             row.GetValue <IPAddress>("source"),
                                             row.GetValue <int?>("source_elapsed") ?? 0,
                                             row.GetValue <string>("thread")))
                                 .ToList();
                    loadedTrace.Events = events;
                    return loadedTrace;
                });
            }));
        }
Exemple #3
0
        private Task <bool> IterateAndConnect(IEnumerator <Host> hostsEnumerator, Dictionary <IPEndPoint, Exception> triedHosts)
        {
            var available = hostsEnumerator.MoveNext();

            if (!available)
            {
                return(TaskHelper.FromException <bool>(new NoHostAvailableException(triedHosts)));
            }
            var host = hostsEnumerator.Current;
            var c    = new Connection(ProtocolVersion, host.Address, _config);

            return(((Task)c
                    .Open())
                   .ContinueWith(t =>
            {
                if (t.Status == TaskStatus.RanToCompletion)
                {
                    _connection = c;
                    _host = host;
                    _logger.Info("Connection established to {0}", c.Address);
                    return TaskHelper.ToTask(true);
                }
                if (t.IsFaulted && t.Exception != null)
                {
                    var ex = t.Exception.InnerException;
                    if (ex is UnsupportedProtocolVersionException)
                    {
                        //Use the protocol version used to parse the response message
                        var nextVersion = c.ProtocolVersion;
                        if (nextVersion >= ProtocolVersion)
                        {
                            //Processor could reorder instructions in such way that the connection protocol version is not up to date.
                            nextVersion = (byte)(ProtocolVersion - 1);
                        }
                        _logger.Info(String.Format("Unsupported protocol version {0}, trying with version {1}", ProtocolVersion, nextVersion));
                        ProtocolVersion = nextVersion;
                        c.Dispose();
                        if (ProtocolVersion < 1)
                        {
                            throw new DriverInternalError("Invalid protocol version");
                        }
                        //Retry using the new protocol version
                        return Connect(true);
                    }
                    //There was a socket exception or an authentication exception
                    triedHosts.Add(host.Address, ex);
                    c.Dispose();
                    return IterateAndConnect(hostsEnumerator, triedHosts);
                }
                throw new TaskCanceledException("The ControlConnection could not be connected.");
            }, TaskContinuationOptions.ExecuteSynchronously)
                   .Unwrap());
        }
Exemple #4
0
 public override Task <MaterializedViewMetadata> GetView(string keyspaceName, string viewName)
 {
     return(TaskHelper.FromException <MaterializedViewMetadata>(new NotSupportedException("Materialized views are supported in Cassandra 3.0 or above")));
 }
        /// <summary>
        /// Sets the keyspace of the connection.
        /// If the keyspace is different from the current value, it sends a Query request to change it
        /// </summary>
        public Task <bool> SetKeyspace(string value)
        {
            if (String.IsNullOrEmpty(value) || _keyspace == value)
            {
                //No need to switch
                return(TaskHelper.Completed);
            }
            Task <bool> keyspaceSwitch;

            try
            {
                if (!_keyspaceSwitchSemaphore.Wait(0))
                {
                    //Could not enter semaphore
                    //It is very likely that the connection is already switching keyspace
                    keyspaceSwitch = _keyspaceSwitchTask;
                    if (keyspaceSwitch != null)
                    {
                        return(keyspaceSwitch.Then(_ =>
                        {
                            //validate if the new keyspace is the expected
                            if (_keyspace != value)
                            {
                                //multiple concurrent switches to different keyspace
                                return SetKeyspace(value);
                            }
                            return TaskHelper.Completed;
                        }));
                    }
                    _keyspaceSwitchSemaphore.Wait();
                }
            }
            catch (ObjectDisposedException)
            {
                //The semaphore was disposed, this connection is closed
                return(TaskHelper.FromException <bool>(new SocketException((int)SocketError.NotConnected)));
            }
            //Semaphore entered
            if (_keyspace == value)
            {
                //While waiting to enter the semaphore, the connection switched keyspace
                try
                {
                    _keyspaceSwitchSemaphore.Release();
                }
                catch (ObjectDisposedException)
                {
                    //this connection is now closed but the switch completed successfully
                }
                return(TaskHelper.Completed);
            }
            var request = new QueryRequest(_serializer.ProtocolVersion, string.Format("USE \"{0}\"", value), false, QueryProtocolOptions.Default);

            Logger.Info("Connection to host {0} switching to keyspace {1}", Address, value);
            keyspaceSwitch = _keyspaceSwitchTask = Send(request).ContinueSync(r =>
            {
                _keyspace = value;
                try
                {
                    _keyspaceSwitchSemaphore.Release();
                }
                catch (ObjectDisposedException)
                {
                    //this connection is now closed but the switch completed successfully
                }
                _keyspaceSwitchTask = null;
                return(true);
            });
            return(keyspaceSwitch);
        }
Exemple #6
0
        /// <summary>
        /// Initializes the connection.
        /// </summary>
        /// <exception cref="SocketException">Throws a SocketException when the connection could not be established with the host</exception>
        /// <exception cref="AuthenticationException" />
        /// <exception cref="UnsupportedProtocolVersionException"></exception>
        public Task <Response> Open()
        {
            _freeOperations    = new ConcurrentStack <short>(Enumerable.Range(0, MaxConcurrentRequests).Select(s => (short)s).Reverse());
            _pendingOperations = new ConcurrentDictionary <short, OperationState>();
            _writeQueue        = new ConcurrentQueue <OperationState>();

            if (Options.CustomCompressor != null)
            {
                Compressor = Options.CustomCompressor;
            }
            else if (Options.Compression == CompressionType.LZ4)
            {
#if !NETCORE
                Compressor = new LZ4Compressor();
#else
                return(TaskHelper.FromException <Response>(new NotSupportedException("Lz4 compression not supported under .NETCore")));
#endif
            }
            else if (Options.Compression == CompressionType.Snappy)
            {
                Compressor = new SnappyCompressor();
            }

            //Init TcpSocket
            _tcpSocket.Init();
            _tcpSocket.Error   += CancelPending;
            _tcpSocket.Closing += () => CancelPending(null);
            //Read and write event handlers are going to be invoked using IO Threads
            _tcpSocket.Read           += ReadHandler;
            _tcpSocket.WriteCompleted += WriteCompletedHandler;
            var protocolVersion = _serializer.ProtocolVersion;
            return(_tcpSocket
                   .Connect()
                   .Then(_ => Startup())
                   .ContinueWith(t =>
            {
                if (t.IsFaulted && t.Exception != null)
                {
                    //Adapt the inner exception and rethrow
                    var ex = t.Exception.InnerException;
                    if (ex is ProtocolErrorException)
                    {
                        //As we are starting up, check for protocol version errors
                        //There is no other way than checking the error message from Cassandra
                        if (ex.Message.Contains("Invalid or unsupported protocol version"))
                        {
                            throw new UnsupportedProtocolVersionException(protocolVersion, ex);
                        }
                    }
                    if (ex is ServerErrorException && protocolVersion.CanStartupResponseErrorBeWrapped() &&
                        ex.Message.Contains("ProtocolException: Invalid or unsupported protocol version"))
                    {
                        //For some versions of Cassandra, the error is wrapped into a server error
                        //See CASSANDRA-9451
                        throw new UnsupportedProtocolVersionException(protocolVersion, ex);
                    }
                    // ReSharper disable once PossibleNullReferenceException
                    throw ex;
                }
                return t.Result;
            }, TaskContinuationOptions.ExecuteSynchronously)
                   .Then(response =>
            {
                if (response is AuthenticateResponse)
                {
                    return StartAuthenticationFlow(((AuthenticateResponse)response).Authenticator);
                }
                if (response is ReadyResponse)
                {
                    return TaskHelper.ToTask(response);
                }
                throw new DriverInternalError("Expected READY or AUTHENTICATE, obtained " + response.GetType().Name);
            }));
        }
        /// <summary>
        /// Create the min amount of connections, if the pool is empty
        /// </summary>
        /// <exception cref="System.Net.Sockets.SocketException" />
        internal Task <Connection[]> MaybeCreateCorePool()
        {
            var coreConnections = _config.GetPoolingOptions(ProtocolVersion).GetCoreConnectionsPerHost(_distance);

            if (!_connections.Any(c => c.IsClosed) && _connections.Count >= coreConnections)
            {
                //Pool has the appropriate size
                return(TaskHelper.ToTask(_connections.ToArray()));
            }
            if (!_poolModificationSemaphore.Wait(0))
            {
                //Couldn't enter semaphore, check if there is a connection available to yield
                var opened = _connections.Where(c => !c.IsClosed).ToArray();
                if (opened.Length > 0)
                {
                    return(TaskHelper.ToTask(opened));
                }
                var alreadyOpening = _openingConnections;
                if (alreadyOpening != null && alreadyOpening.Length > 0)
                {
                    return(Task.Factory.ContinueWhenAny(alreadyOpening, t =>
                    {
                        if (t.Status == TaskStatus.RanToCompletion)
                        {
                            return new[] { t.Result };
                        }
                        if (t.Exception != null)
                        {
                            throw t.Exception.InnerException;
                        }
                        throw new TaskCanceledException("Could not get an opened connection because the Task was cancelled");
                    }, TaskContinuationOptions.ExecuteSynchronously));
                }
                //There isn't a connection available yet, enter semaphore
                _poolModificationSemaphore.Wait();
            }
            //Semaphore entered
            //Remove closed connections from the pool
            var toRemove = _connections.Where(c => c.IsClosed).ToArray();

            foreach (var c in toRemove)
            {
                _connections.Remove(c);
            }
            var opening = new List <Task <Connection> >();

            if (_openingConnections != null)
            {
                opening.AddRange(_openingConnections);
            }
            while (_connections.Count + opening.Count < coreConnections)
            {
                opening.Add(CreateConnection());
            }
            if (opening.Count == 0)
            {
                if (_connections.Count == 0)
                {
                    return(TaskHelper.FromException <Connection[]>(new DriverInternalError("Could not create a connection and no connections found in pool")));
                }
                _poolModificationSemaphore.Release();
                return(TaskHelper.ToTask(_connections.ToArray()));
            }
            var openingArray = opening.ToArray();

            _openingConnections = openingArray;
            //Clean up when all open task finished
            var allCompleted = Task.Factory.ContinueWhenAll(openingArray, tasks =>
            {
                _connections.AddRange(tasks.Where(t => t.Status == TaskStatus.RanToCompletion).Select(t => t.Result).ToArray());
                if (_connections.Count == coreConnections)
                {
                    Logger.Info("{0} connection(s) to host {1} {2} created successfully", coreConnections, _host.Address, _connections.Count < 2 ? "was" : "were");
                    _host.BringUpIfDown();
                }
                _openingConnections  = null;
                var connectionsArray = _connections.ToArray();
                _poolModificationSemaphore.Release();
                if (connectionsArray.Length == 0 && tasks.All(t => t.Status != TaskStatus.RanToCompletion))
                {
                    //Pool could not be created
                    Logger.Info("Connection pool to host {0} could not be created", _host.Address);
                    //There are multiple problems, but we only care about one
                    // ReSharper disable once PossibleNullReferenceException
                    throw tasks.First().Exception.InnerException;
                }
                return(connectionsArray);
            }, TaskContinuationOptions.ExecuteSynchronously);

            //yield the first connection available
            return(Task.Factory.ContinueWhenAny(openingArray, t =>
            {
                if (t.Status == TaskStatus.RanToCompletion)
                {
                    return new[] { t.Result };
                }
                if (t.Exception != null)
                {
                    throw t.Exception.InnerException;
                }
                throw new TaskCanceledException("Could not get an opened connection because the Task was cancelled");
            }, TaskContinuationOptions.ExecuteSynchronously)
                   .ContinueWith(t =>
            {
                if (t.Status != TaskStatus.RanToCompletion)
                {
                    //The first connection failed
                    //Wait for all to complete
                    return allCompleted;
                }
                return TaskHelper.ToTask(t.Result);
            }, TaskContinuationOptions.ExecuteSynchronously).Unwrap());
        }