/// <summary> /// Either inserts or updates a silo metrics row. /// </summary> /// <param name="deploymentId">The deployment ID.</param> /// <param name="siloId">The silo ID.</param> /// <param name="gateway">The gateway information.</param> /// <param name="siloAddress">The silo address information.</param> /// <param name="hostName">The host name.</param> /// <param name="siloMetrics">The silo metrics to be either updated or inserted.</param> /// <returns></returns> internal Task UpsertSiloMetricsAsync(string deploymentId, string siloId, IPEndPoint gateway, SiloAddress siloAddress, string hostName, ISiloPerformanceMetrics siloMetrics) { return(ExecuteAsync(dbStoredQueries.UpsertSiloMetricsKey, command => { var columns = new DbStoredQueries.Columns(command); columns.DeploymentId = deploymentId; columns.HostName = hostName; columns.SiloMetrics = siloMetrics; columns.SiloAddress = siloAddress; columns.GatewayAddress = gateway.Address; columns.GatewayPort = gateway.Port; columns.SiloId = siloId; return columns; })); }
/// <summary> /// Inserts the given statistics counters to the Orleans database. /// </summary> /// <param name="deploymentId">The deployment ID.</param> /// <param name="hostName">The hostname.</param> /// <param name="siloOrClientName">The silo or client name.</param> /// <param name="id">The silo address or client ID.</param> /// <param name="counters">The counters to be inserted.</param> internal Task InsertStatisticsCountersAsync(string deploymentId, string hostName, string siloOrClientName, string id, List <ICounter> counters) { var queryTemplate = dbStoredQueries.InsertOrleansStatisticsKey; //Zero statistic values mean either that the system is not running or no updates. Such values are not inserted and pruned //here so that no insert query or parameters are generated. counters = counters.Where(i => !"0".Equals(i.IsValueDelta ? i.GetDeltaString() : i.GetValueString())).ToList(); if (counters.Count == 0) { return(Task.CompletedTask); } //Note that the following is almost the same as RelationalStorageExtensions.ExecuteMultipleInsertIntoAsync //the only difference being that some columns are skipped. Likely it would be beneficial to introduce //a "skip list" to RelationalStorageExtensions.ExecuteMultipleInsertIntoAsync. //The template contains an insert for online. The part after SELECT is copied //out so that certain parameters can be multiplied by their count. Effectively //this turns a query of type (transaction details vary by vendor) //BEGIN TRANSACTION; INSERT INTO [OrleansStatisticsTable] <columns> SELECT <variables>; COMMIT TRANSACTION; //to BEGIN TRANSACTION; INSERT INTO [OrleansStatisticsTable] <columns> SELECT <variables>; UNION ALL <variables> COMMIT TRANSACTION; //where the UNION ALL is multiplied as many times as there are counters to insert. int startFrom = queryTemplate.IndexOf("SELECT", StringComparison.Ordinal) + "SELECT".Length + 1; //This +1 is to have a space between SELECT and the first parameter name to not to have a SQL syntax error. int lastSemicolon = queryTemplate.LastIndexOf(";", StringComparison.Ordinal); int endTo = lastSemicolon > 0 ? queryTemplate.LastIndexOf(";", lastSemicolon - 1, StringComparison.Ordinal) : -1; var template = queryTemplate.Substring(startFrom, endTo - startFrom); //HACK: The oracle query template ends with FROM DUAL, other providers queries don´t. //It has to stay in the query template, but needs to removed from the template variable, because //the otherwise the Replace which used for the union generation below would generate an invalid query template = template.Replace(" FROM DUAL", String.Empty); var parameterNames = template.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries) .Select(i => i.Trim()).ToArray(); var collectionOfParametersToBeUnionized = new List <string>(); var parametersToBeUnioned = new string[parameterNames.Length]; for (int counterIndex = 0; counterIndex < counters.Count; ++counterIndex) { for (int parameterNameIndex = 0; parameterNameIndex < parameterNames.Length; ++parameterNameIndex) { //The column names in InsertStatisticsMultiupdateColumns are without parameter placeholders, //but parameterNames are with placeholders. As the placeholder is provider dependent, we are ignoring //the placeholder when looking for columns. if (InsertStatisticsMultiupdateColumns.Any(x => parameterNames[parameterNameIndex].EndsWith(x))) { //These parameters change for each row. The format is //@StatValue0, @StatValue1, @StatValue2, ... @sStatValue{counters.Count}. parametersToBeUnioned[parameterNameIndex] = $"{parameterNames[parameterNameIndex]}{counterIndex}"; } else { //These parameters remain constant for every and each row. parametersToBeUnioned[parameterNameIndex] = parameterNames[parameterNameIndex]; } } collectionOfParametersToBeUnionized.Add($"{string.Join(",", parametersToBeUnioned)}"); } var storageConsts = DbConstantsStore.GetDbConstants(storage.InvariantName); var query = queryTemplate.Replace(template, string.Join(storageConsts.UnionAllSelectTemplate, collectionOfParametersToBeUnionized)); return(ExecuteAsync(query, command => { var columns = new DbStoredQueries.Columns(command) { DeploymentId = deploymentId, HostName = hostName, Name = siloOrClientName, Id = id }; columns.Counters = counters; return columns; })); }