private Task <PreparedStatement> SetPrepareTableInfo(PreparedStatement ps)
        {
            const string msgRoutingNotSet = "Routing information could not be set for query \"{0}\"";
            var          column           = ps.Metadata.Columns.FirstOrDefault();

            if (column == null || column.Keyspace == null)
            {
                //The prepared statement does not contain parameters
                return(TaskHelper.ToTask(ps));
            }
            if (ps.Metadata.PartitionKeys != null)
            {
                //The routing indexes where parsed in the prepared response
                if (ps.Metadata.PartitionKeys.Length == 0)
                {
                    //zero-length partition keys means that none of the parameters are partition keys
                    //the partition key is hard-coded.
                    return(TaskHelper.ToTask(ps));
                }
                ps.RoutingIndexes = ps.Metadata.PartitionKeys;
                return(TaskHelper.ToTask(ps));
            }
            return(Cluster.Metadata.GetTableAsync(column.Keyspace, column.Table).ContinueWith(t =>
            {
                if (t.Exception != null)
                {
                    Logger.Error("There was an error while trying to retrieve table metadata for {0}.{1}. {2}", column.Keyspace, column.Table, t.Exception.InnerException);
                    return ps;
                }
                var table = t.Result;
                if (table == null)
                {
                    Logger.Info(msgRoutingNotSet, ps.Cql);
                    return ps;
                }
                var routingSet = ps.SetPartitionKeys(table.PartitionKeys);
                if (!routingSet)
                {
                    Logger.Info(msgRoutingNotSet, ps.Cql);
                }
                return ps;
            }));
        }
Exemple #2
0
        public override Task <FunctionMetadata> GetFunction(string keyspaceName, string functionName, string signatureString)
        {
            var query = string.Format(SelectFunctions, keyspaceName, functionName, signatureString);

            return(Cc
                   .QueryAsync(query, true)
                   .Then(rs =>
            {
                var row = rs.FirstOrDefault();
                if (row == null)
                {
                    return TaskHelper.ToTask <FunctionMetadata>(null);
                }
                var argumentTypes = row.GetValue <string[]>("argument_types") ?? new string[0];
                var parseTasks = new Task <ColumnDesc> [1 + argumentTypes.Length];
                parseTasks[0] = DataTypeParser.ParseTypeName(_udtResolver, row.GetValue <string>("keyspace_name"), row.GetValue <string>("return_type"));
                for (var i = 0; i < argumentTypes.Length; i++)
                {
                    parseTasks[1 + i] = DataTypeParser.ParseTypeName(_udtResolver, row.GetValue <string>("keyspace_name"), argumentTypes[i]);
                }
                return Task.Factory.ContinueWhenAll(parseTasks, tasks =>
                {
                    var ex = tasks.Select(t => t.Exception).FirstOrDefault(e => e != null);
                    if (ex != null)
                    {
                        throw ex.InnerException;
                    }
                    return new FunctionMetadata
                    {
                        Name = row.GetValue <string>("function_name"),
                        KeyspaceName = row.GetValue <string>("keyspace_name"),
                        Signature = argumentTypes,
                        ArgumentNames = row.GetValue <string[]>("argument_names") ?? new string[0],
                        Body = row.GetValue <string>("body"),
                        CalledOnNullInput = row.GetValue <bool>("called_on_null_input"),
                        Language = row.GetValue <string>("language"),
                        ReturnType = tasks[0].Result,
                        ArgumentTypes = tasks.Skip(1).Select(t => t.Result).ToArray()
                    };
                });
            }));
        }
        internal Task <TableMetadata> GetTableMetadataAsync(string tableName)
        {
            TableMetadata tableMetadata;

            if (_tables.TryGetValue(tableName, out tableMetadata))
            {
                //The table metadata is available in local cache
                return(TaskHelper.ToTask(tableMetadata));
            }
            return(_parent.SchemaParser
                   .GetTable(Name, tableName)
                   .ContinueSync(table =>
            {
                if (table == null)
                {
                    return null;
                }
                //Cache it
                _tables.AddOrUpdate(tableName, table, (k, o) => table);
                return table;
            }));
        }
Exemple #4
0
        private Task <T> ParseTableOrView <T>(Func <Row, T> newInstance, Task <IEnumerable <Row> > getTableTask, Task <IEnumerable <Row> > getColumnsTask)
            where T : DataCollectionMetadata
        {
            var tableMetadataRow = getTableTask.Result.FirstOrDefault();

            if (tableMetadataRow == null)
            {
                return(TaskHelper.ToTask <T>(null));
            }
            var columns        = new Dictionary <string, TableColumn>();
            var partitionKeys  = new List <Tuple <int, TableColumn> >();
            var clusteringKeys = new List <Tuple <int, Tuple <TableColumn, SortOrder> > >();
            //Read table options
            var options = new TableOptions
            {
                isCompactStorage  = false,
                bfFpChance        = tableMetadataRow.GetValue <double>("bloom_filter_fp_chance"),
                caching           = "{" + string.Join(",", tableMetadataRow.GetValue <IDictionary <string, string> >("caching").Select(kv => "\"" + kv.Key + "\":\"" + kv.Value + "\"")) + "}",
                comment           = tableMetadataRow.GetValue <string>("comment"),
                gcGrace           = tableMetadataRow.GetValue <int>("gc_grace_seconds"),
                localReadRepair   = tableMetadataRow.GetValue <double>("dclocal_read_repair_chance"),
                readRepair        = tableMetadataRow.GetValue <double>("read_repair_chance"),
                compactionOptions = tableMetadataRow.GetValue <SortedDictionary <string, string> >("compaction"),
                compressionParams =
                    tableMetadataRow.GetValue <SortedDictionary <string, string> >("compression")
            };
            var columnsMetadata = getColumnsTask.Result;

            Task <Tuple <TableColumn, Row> >[] columnTasks = columnsMetadata
                                                             .Select(row =>
            {
                return(DataTypeParser.ParseTypeName(_udtResolver, tableMetadataRow.GetValue <string>("keyspace_name"), row.GetValue <string>("type")).
                       ContinueSync(type => Tuple.Create(new TableColumn
                {
                    Name = row.GetValue <string>("column_name"),
                    Keyspace = row.GetValue <string>("keyspace_name"),
                    Table = row.GetValue <string>("table_name"),
                    TypeCode = type.TypeCode,
                    TypeInfo = type.TypeInfo
                }, row)));
            }).ToArray();
            return(Task.Factory.ContinueWhenAll(columnTasks, tasks =>
            {
                var ex = tasks.Select(t => t.Exception).FirstOrDefault(e => e != null);
                if (ex != null)
                {
                    throw ex.InnerException;
                }
                foreach (var t in tasks)
                {
                    var col = t.Result.Item1;
                    var row = t.Result.Item2;
                    switch (row.GetValue <string>("kind"))
                    {
                    case "partition_key":
                        partitionKeys.Add(Tuple.Create(row.GetValue <int?>("position") ?? 0, col));
                        col.KeyType = KeyType.Partition;
                        break;

                    case "clustering":
                        clusteringKeys.Add(Tuple.Create(row.GetValue <int?>("position") ?? 0,
                                                        Tuple.Create(col, row.GetValue <string>("clustering_order") == "desc" ? SortOrder.Descending : SortOrder.Ascending)));
                        col.KeyType = KeyType.Clustering;
                        break;

                    case "static":
                        col.IsStatic = true;
                        break;
                    }
                    columns.Add(col.Name, col);
                }
                if (typeof(T) == typeof(TableMetadata))
                {
                    var flags = tableMetadataRow.GetValue <string[]>("flags");
                    var isDense = flags.Contains("dense");
                    var isSuper = flags.Contains("super");
                    var isCompound = flags.Contains("compound");
                    options.isCompactStorage = isSuper || isDense || !isCompound;
                    //remove the columns related to Thrift
                    var isStaticCompact = !isSuper && !isDense && !isCompound;
                    if (isStaticCompact)
                    {
                        PruneStaticCompactTableColumns(clusteringKeys, columns);
                    }
                    else if (isDense)
                    {
                        PruneDenseTableColumns(columns);
                    }
                }
                var result = newInstance(tableMetadataRow);
                result.SetValues(columns,
                                 partitionKeys.OrderBy(p => p.Item1).Select(p => p.Item2).ToArray(),
                                 clusteringKeys.OrderBy(p => p.Item1).Select(p => p.Item2).ToArray(),
                                 options);
                return result;
            }));
        }
        /// <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)
            {
                Compressor = new LZ4Compressor();
            }
            else if (Options.Compression == CompressionType.Snappy)
            {
                Compressor = new SnappyCompressor();
            }

            //Init TcpSocket
            _tcpSocket.Init();
            _tcpSocket.Error   += CancelPending;
            _tcpSocket.Closing += () => CancelPending(null, null);
            //Read and write event handlers are going to be invoked using IO Threads
            _tcpSocket.Read           += ReadHandler;
            _tcpSocket.WriteCompleted += WriteCompletedHandler;
            return(_tcpSocket
                   .Connect()
                   .Then(_ => Startup())
                   .ContinueWith(t =>
            {
                if (t.IsFaulted && t.Exception != null)
                {
                    //Adapt the inner exception and rethrow
                    var ex = t.Exception.InnerException;
                    var protocolVersion = _serializer.ProtocolVersion;
                    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 >= 3 && 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);
                    }
                    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());
        }