// Could flush on a timer. private async Task FlushIntancesAsync() { // Before writing, give items a chance to refresh var itemsSnapshot = Update(); // Write entries var instances = Array.ConvertAll(itemsSnapshot, item => InstanceTableEntity.New(item)); var recentInvokes = Array.ConvertAll(itemsSnapshot, item => RecentPerFuncEntity.New(_machineName, item)); FunctionDefinitionEntity[] functionDefinitions; lock (_lock) { functionDefinitions = _funcDefs.ToArray(); _funcDefs.Clear(); } Task t1 = WriteBatchAsync(instances); Task t2 = WriteBatchAsync(recentInvokes); Task t3 = WriteBatchAsync(functionDefinitions); await Task.WhenAll(t1, t2, t3); // After we write to table, remove all completed functions. lock (_lock) { foreach (var completedId in _completedFunctions) { _activeFuncs.Remove(completedId); } _completedFunctions.Clear(); } }
// Could flush on a timer. private async Task FlushIntancesAsync() { FunctionInstanceLogItem[] itemsSnapshot; Guid[] completedSnapshot; FunctionDefinitionEntity[] functionDefinitionSnapshot; // Get snapshots while under the lock. lock (_lock) { itemsSnapshot = _activeFuncs.Values.ToArray(); completedSnapshot = _completedFunctions.ToArray(); functionDefinitionSnapshot = _funcDefs.ToArray(); _funcDefs.Clear(); } // Before writing, give items a chance to refresh foreach (var item in itemsSnapshot) { item.Refresh(_flushInterval); } // Write entries var instances = itemsSnapshot.Select(item => InstanceTableEntity.New(item)); var recentInvokes = itemsSnapshot.Select(item => RecentPerFuncEntity.New(_machineName, item)); Task t1 = WriteBatchAsync(instances); Task t2 = WriteBatchAsync(recentInvokes); Task t3 = WriteBatchAsync(functionDefinitionSnapshot); await Task.WhenAll(t1, t2, t3); // After we write to table, remove all completed functions. lock (_lock) { foreach (var completedId in completedSnapshot) { _activeFuncs.Remove(completedId); _completedFunctions.Remove(completedId); } } }
public async Task AddAsync(FunctionInstanceLogItem item, CancellationToken cancellationToken = default(CancellationToken)) { item.Validate(); { lock (_lock) { StartBackgroundFlusher(); if (_container == null) { _container = new ContainerActiveLogger(_containerName, _instanceTable); } if (_instanceLogger == null) { int size = GetContainerSize(); _instanceLogger = new CloudTableInstanceCountLogger(_containerName, _instanceTable, size); } } if (item.IsCompleted()) { _container.Decrement(item.FunctionInstanceId); _instanceLogger.Decrement(item.FunctionInstanceId); } else { _container.Increment(item.FunctionInstanceId); _instanceLogger.Increment(item.FunctionInstanceId); } } lock (_lock) { if (_seenFunctions.Add(item.FunctionName)) { _funcDefs.Add(FunctionDefinitionEntity.New(item.FunctionName)); } } // Both Start and Completed log here. Completed will overwrite a Start entry. lock (_lock) { _instances.Add(InstanceTableEntity.New(item)); _recents.Add(RecentPerFuncEntity.New(_containerName, item)); } if (item.IsCompleted()) { // For completed items, aggregate total passed and failed within a time bucket. // Time aggregate is flushed later. // Don't flush until we've moved onto the next interval. { var newEntity = TimelineAggregateEntity.New(_containerName, item.FunctionName, item.StartTime, _uniqueId); lock (_lock) { // If we already have an entity at this time slot (specified by rowkey), then use that so that // we update the existing counters. var existingEntity = _timespan.GetFromRowKey(newEntity.RowKey); if (existingEntity == null) { _timespan.Add(newEntity); existingEntity = newEntity; } Increment(item, existingEntity); } } } // Flush every 100 items, maximize with tables. Task t1 = FlushIntancesAsync(false); Task t2 = FlushTimelineAggregateAsync(); await Task.WhenAll(t1, t2); }
public async Task AddAsync(FunctionInstanceLogItem item, CancellationToken cancellationToken = default(CancellationToken)) { item.Validate(); { lock (_lock) { StartBackgroundFlusher(); if (_container == null) { _container = new ContainerActiveLogger(_containerName, _instanceTable); } if (_instanceLogger == null) { int size = GetContainerSize(); _instanceLogger = new CloudTableInstanceCountLogger(_containerName, _instanceTable, size); } } if (item.IsCompleted()) { _container.Decrement(item.FunctionInstanceId); _instanceLogger.Decrement(item.FunctionInstanceId); } else { _container.Increment(item.FunctionInstanceId); _instanceLogger.Increment(item.FunctionInstanceId); } } lock (_lock) { if (_seenFunctions.Add(item.FunctionName)) { _funcDefs.Add(FunctionDefinitionEntity.New(item.FunctionName)); } } if (!item.IsCompleted()) { return; } lock (_lock) { _instances.Add(InstanceTableEntity.New(item)); _recents.Add(RecentPerFuncEntity.New(_containerName, item)); } // Time aggregate is flushed later. // Don't flush until we've moved onto the next interval. { var rowKey = TimelineAggregateEntity.RowKeyTimeInterval(item.FunctionName, item.StartTime, _uniqueId); lock (_lock) { TimelineAggregateEntity x; if (!_timespan.TryGetValue(rowKey, out x)) { // Can we flush the old counters? x = TimelineAggregateEntity.New(_containerName, item.FunctionName, item.StartTime, _uniqueId); _timespan[rowKey] = x; } Increment(item, x); } } // Flush every 100 items, maximize with tables. Task t1 = FlushIntancesAsync(false); Task t2 = FlushTimelineAggregateAsync(); await Task.WhenAll(t1, t2); }