Ejemplo n.º 1
0
        // called every fixed interval (lets say one second)
        public void logResourceUsage()
        {
            ResourceUsageRecord createdResourceUsageRecord = new ResourceUsageRecord();

            createdResourceUsageRecord.setRecordedTaskInfos(computeContext.computeBudgetedTasks.list.Select(v => new RecordedTaskInfo(v.budget.priority, v.humanReadableUniqueName)));
            createdResourceUsageRecord.sumOfbudgetPriorities = computeContext.computeBudgetedTasks.list.Select(v => (float)v.budget.priority).Aggregate((sum, current) => sum + current);
            records.Add(createdResourceUsageRecord);
        }
Ejemplo n.º 2
0
        public UsageRecord(ResourceUsageRecord record, Tenant tenant, Guid batchId)
        {
            var meter = Meter.MeterMappings[record.UsageType];

            this.PartitionKey = batchId.ToString();
            this.RowKey       = $"{record.EngagementAccount}:{record.UsageType.ToString()}";

            this.SubscriptionId           = Guid.Parse(tenant.SubscriptionId);
            this.EventId                  = Guid.NewGuid();
            this.EventDateTime            = DateTime.UtcNow;
            this.Quantity                 = record.Quantity / meter.MeterUnit;
            this.MeterId                  = meter.MeterId;
            this.ResourceUri              = tenant.ResourceId;
            this.Location                 = tenant.Location;
            this.TagsDictionary           = tenant.Tags;
            this.AdditionalInfoDictionary = new Dictionary <string, string>
            {
                { nameof(tenant.SKU), tenant.SKU },
                { nameof(tenant.State), tenant.State.ToString() }
            };

            this.SerializeCollections();
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="PSResourceUsageRecord" /> class.
 /// </summary>
 /// <param name="usageRecord">The base usage record for this instance.</param>
 public PSResourceUsageRecord(ResourceUsageRecord usageRecord)
 {
     this.CopyFrom(usageRecord);
 }
        public async Task <bool> OnMessageReportUpdatedAsync(string messageId, string customMessageId, List <ReportDetail> reports, ConnectorIdentifier connector)
        {
            if (string.IsNullOrEmpty(messageId) && !string.IsNullOrEmpty(customMessageId))
            {
                // Get id mapping, retry 3 times if cannot found (in case racing with case #2 who has not insert the mapping yet)
                int retry = 3;
                while (retry > 0)
                {
                    messageId = (await MessageIdMappingTableEntity.GetAsync(idMappingTable, customMessageId))?.MessageId;
                    if (!string.IsNullOrEmpty(messageId))
                    {
                        break;
                    }

                    retry--;
                    await Task.Delay(1000);
                }

                if (string.IsNullOrEmpty(messageId))
                {
                    SmsProviderEventSource.Current.Warning(SmsProviderEventSource.EmptyTrackingId, this, nameof(this.OnMessageReportUpdatedAsync), OperationStates.Dropped, $"Does not find record for customMessageId={customMessageId}");
                    return(false);
                }
            }

            // Get record from summary table
            var entity = await MessageSummaryTableEntity.GetAsync(this.summaryTable, messageId);

            if (entity == null)
            {
                SmsProviderEventSource.Current.Warning(SmsProviderEventSource.EmptyTrackingId, this, nameof(this.OnMessageReportUpdatedAsync), OperationStates.Dropped, $"Does not find record for messageId={messageId}");
                return(false);
            }

            // Calculating the unit according to provider
            // This is for long message that will be charged as multiple messages
            var metadata = await this.credentialManager.GetMetadata(connector.ConnectorName);

            var units = BillingHelper.CalculateBillingUnits(entity.MessageBody, metadata);

            // Report might be ealier than dispatch result, so make sure units is updated
            if (entity.Units == null)
            {
                entity.Units          = units;
                entity.LastUpdateTime = DateTime.UtcNow;
                await MessageSummaryTableEntity.InsertOrMergeAsync(this.summaryTable, entity);
            }

            // Update detail table
            if (reports != null && reports.Count > 0)
            {
                await this.UpdateMessageDetailsAsync(entity.EngagementAccount, entity, reports);
            }

            // Get account detail
            var account = await this.store.GetAccountAsync(entity.EngagementAccount);

            var succeed = reports.Where(r => r.State == MessageState.DELIVERED).Count();
            var failed  = reports.Where(r => r.State != MessageState.DELIVERED && r.State != MessageState.UNKNOWN).Count();

            // Push metric and billing usage
            if (Enum.TryParse(entity.MessageCategory, out MessageCategory category))
            {
                if (succeed > 0)
                {
                    this.metricManager.LogDeliverSuccess(succeed * units, entity.EngagementAccount, account?.SubscriptionId ?? string.Empty, category);

                    if (Constants.MessageCategoryToUsageTypeMappings.TryGetValue(category, out ResourceUsageType usageType))
                    {
                        var usage = new ResourceUsageRecord();
                        usage.EngagementAccount = entity.EngagementAccount;
                        usage.UsageType         = usageType;
                        usage.Quantity          = succeed * units;

                        await this.billingAgent.StoreBillingUsageAsync(new List <ResourceUsageRecord> {
                            usage
                        }, CancellationToken.None);

                        SmsProviderEventSource.Current.Info(SmsProviderEventSource.EmptyTrackingId, this, nameof(this.OnMessageReportUpdatedAsync), OperationStates.Succeeded, $"Usage pushed to billing service account={entity.EngagementAccount} usageType={usageType} quantity={succeed}x{units}");
                    }
                }

                if (failed > 0)
                {
                    this.metricManager.LogDeliverFailed(failed * units, entity.EngagementAccount, account?.SubscriptionId ?? string.Empty, category);
                }
            }
            else
            {
                SmsProviderEventSource.Current.Warning(SmsProviderEventSource.EmptyTrackingId, this, nameof(this.OnMessageReportUpdatedAsync), OperationStates.Failed, $"Failed to parse MessageCategory={entity.MessageCategory} MessageId={entity.MessageId}");
            }

            return(true);
        }
Ejemplo n.º 5
0
        public async Task <bool> OnReportUpdatedAsync(string engagementAccount, Report report)
        {
            try
            {
                // Get record in in-progress table
                var inProgressEntity = await ReportInProgressTableEntity.GetAsync(reportInProgressTable, engagementAccount, report.MessageIdentifer.MessageId);

                if (inProgressEntity == null)
                {
                    // Record should be already in history table
                    var historyTable = await GetHistoryTableAsync(engagementAccount);

                    var historyEntity = await MessageHistoryTableEntity.GetAsync(historyTable, engagementAccount, report.MessageIdentifer.MessageId);

                    if (historyEntity == null)
                    {
                        EmailProviderEventSource.Current.Warning(EmailProviderEventSource.EmptyTrackingId, this, nameof(this.OnReportUpdatedAsync), OperationStates.Dropped, $"Does not find record for messageId={report.MessageIdentifer.MessageId}");
                        return(false);
                    }

                    // TODO: if after 24h we get new number of target shall we charge?
                    historyEntity.Targets        = report.TotalTarget;
                    historyEntity.Delivered      = report.TotalDelivered;
                    historyEntity.Opened         = report.TotalOpened;
                    historyEntity.Clicked        = report.TotalClicked;
                    historyEntity.LastUpdateTime = DateTime.UtcNow;

                    await MessageHistoryTableEntity.InsertOrMergeAsync(historyTable, historyEntity);

                    return(true);
                }

                var lastTarget = inProgressEntity.Targets;

                // Update the record in in-progress table
                inProgressEntity.Targets        = report.TotalTarget;
                inProgressEntity.Delivered      = report.TotalDelivered;
                inProgressEntity.Opened         = report.TotalOpened;
                inProgressEntity.Clicked        = report.TotalClicked;
                inProgressEntity.LastUpdateTime = DateTime.UtcNow;

                var updated = await ReportInProgressTableEntity.TryUpdateAsync(reportInProgressTable, inProgressEntity);

                // New target sent, do the charge (only if update succeed)
                if (updated && lastTarget < report.TotalTarget)
                {
                    try
                    {
                        var delta = report.TotalTarget - lastTarget;

                        // Get account detail
                        var account = await this.store.GetAccountAsync(engagementAccount);

                        metricManager.LogDeliverSuccess(delta, engagementAccount, account?.SubscriptionId ?? string.Empty);

                        var usage = new ResourceUsageRecord();
                        usage.EngagementAccount = engagementAccount;
                        usage.UsageType         = ResourceUsageType.EmailMessage;
                        usage.Quantity          = delta;

                        await this.billingAgent.StoreBillingUsageAsync(new List <ResourceUsageRecord> {
                            usage
                        }, CancellationToken.None);

                        EmailProviderEventSource.Current.Info(EmailProviderEventSource.EmptyTrackingId, this, nameof(this.OnReportUpdatedAsync), OperationStates.Succeeded, $"Usage pushed to billing service account={engagementAccount} quantity={usage.Quantity}");
                    }
                    catch (Exception ex)
                    {
                        // We should monitor for billing failure
                        EmailProviderEventSource.Current.CriticalException(EmailProviderEventSource.EmptyTrackingId, this, nameof(this.OnReportUpdatedAsync), OperationStates.Failed, $"Failed at pushing to billing service account={engagementAccount}", ex);
                    }
                }

                // If the record in in-progress for 24h, treat it as completed and move it to history table
                if (inProgressEntity.Timestamp.AddHours(Constants.ReportInProgressIntervalByHours) < DateTime.UtcNow)
                {
                    // Record should be already in history table
                    var historyTable = await GetHistoryTableAsync(engagementAccount);

                    var historyEntity = new MessageHistoryTableEntity(inProgressEntity);

                    await MessageHistoryTableEntity.InsertOrMergeAsync(historyTable, historyEntity);

                    await ReportInProgressTableEntity.DeleteAsync(reportInProgressTable, inProgressEntity);
                }

                return(true);
            }
            catch (Exception ex)
            {
                EmailProviderEventSource.Current.ErrorException(EmailProviderEventSource.EmptyTrackingId, this, nameof(this.OnReportUpdatedAsync), OperationStates.Failed, string.Empty, ex);
                return(false);
            }
        }