我有一个从Azure Service Bus主题订阅触发的Azure函数,我们称其为“处理文件信息”函数。
订阅上的消息包含要处理的文件信息。与此类似:
{
"uniqueFileId": "adjsdakajksajkskjdasd",
"fileName":"mydocument.docx",
"sourceSystemRef":"System1",
"sizeBytes": 1024,
... and other data
}
该函数执行以下两个操作-
检查单个文件存储表中是否存在该文件。如果存在,请更新该文件。如果是新文件,请将文件添加到存储表中(按每个系统 | 每个fileId 进行存储)。
捕获有关文件大小字节的度量,并存储在第二个存储表中,该度量称为度量(不断增加字节,按每年/每月存储在系统 | 中< / strong>)。
下图简要说明了我的方法:
individualFileInfo 表与 fileMetric 之间的区别在于,单个表每个文件有一条记录,而度量标准表则每月存储一条记录,并且该记录保持不变更新(增加)以收集通过该函数传递的总字节。
fileMetrics表中的数据存储如下:
Azure功能在扩展方面非常出色,在我的设置中,我一次最多可以运行6个这些功能。假定要处理的每个文件消息都是唯一的-在没有竞争条件的情况下,更新 individualFileInfo 表中的记录(或插入)可以很好地工作。
但是,事实证明更新 fileMetric 表是有问题的,因为所有6个函数都立即触发,它们都打算一次更新指标表(不断增加新文件计数器或增加现有文件计数器)。
我已经尝试过使用etag进行乐观更新,以及从存储更新中返回412响应时进行一点递归重试(下面的代码示例)。但是我似乎无法避免这种竞争状况。有没有人对如何解决此约束或遇到类似问题提出任何建议?
在存储 fileMetric 更新的功能中执行的示例代码:
internal static async Task UpdateMetricEntry(IAzureTableStorageService auditTableService,
string sourceSystemReference, long addNewBytes, long addIncrementBytes, int retryDepth = 0)
{
const int maxRetryDepth = 3; // only recurively attempt max 3 times
var todayYearMonth = DateTime.Now.ToString("yyyyMM");
try
{
// Attempt to get existing record from table storage.
var result = await auditTableService.GetRecord<VolumeMetric>("VolumeMetrics", sourceSystemReference, todayYearMonth);
// If the volume metrics table existing in storage - add or edit the records as required.
if (result.TableExists)
{
VolumeMetric volumeMetric = result.RecordExists ?
// Existing metric record.
(VolumeMetric)result.Record.Clone()
:
// Brand new metrics record.
new VolumeMetric
{
PartitionKey = sourceSystemReference,
RowKey = todayYearMonth,
SourceSystemReference = sourceSystemReference,
BillingMonth = DateTime.Now.Month,
BillingYear = DateTime.Now.Year,
ETag = "*"
};
volumeMetric.NewVolumeBytes += addNewBytes;
volumeMetric.IncrementalVolumeBytes += addIncrementBytes;
await auditTableService.InsertOrReplace("VolumeMetrics", volumeMetric);
}
}
catch (StorageException ex)
{
if (ex.RequestInformation.HttpStatusCode == 412)
{
// Retry to update the volume metrics.
if (retryDepth < maxRetryDepth)
await UpdateMetricEntry(auditTableService, sourceSystemReference, addNewBytes, addIncrementBytes, retryDepth++);
}
else
throw;
}
}
Etag跟踪冲突,如果此代码获得412 Http响应,它将重试,最多3次(尝试缓解此问题)。我的问题是我不能保证在函数的所有实例之间都更新表存储。
感谢任何提前提示!
答案 0 :(得分:1)
您可以将工作的第二部分放入第二个队列和功能,甚至可以触发文件更新。
由于其他操作听起来可能要花费大部分时间,因此它也可以从第二步中消除一些热量。
然后您可以仅关注该功能来解决所有剩余的比赛条件。您可以使用会话来有效地限制并发。在您的情况下,系统ID可能是会话密钥。如果使用该选项,则一次只有一个Azure Function处理来自一个系统的数据,从而有效地解决了您的竞争状况。
https://dev.to/azure/ordered-queue-processing-in-azure-functions-4h6c
编辑:如果您不能使用会话来逻辑上锁定资源,则可以通过blob存储使用锁定:
https://www.azurefromthetrenches.com/acquiring-locks-on-table-storage/