/// <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())); }
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; }); })); }
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()); }
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); }
/// <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()); }