Azure &引用;指定的阻止列表无效";同时并行上传blob

Azure &引用;指定的阻止列表无效";同时并行上传blob,azure,blobstorage,Azure,Blobstorage,我有一个(相当大的)Azure应用程序,它与Azure blob存储并行上载(相当大的)文件 在几%的上传中,我遇到了一个异常: 指定的阻止列表无效。 System.Net.WebException:远程服务器返回错误:(400)请求错误。 此时,我们运行一段看起来相当无害的代码,将blob并行上载到Azure存储: public static void UploadBlobBlocksInParallel(this CloudBlockBlob blob, FileInfo file)

我有一个(相当大的)Azure应用程序,它与Azure blob存储并行上载(相当大的)文件

在几%的上传中,我遇到了一个异常:

指定的阻止列表无效。

System.Net.WebException:远程服务器返回错误:(400)请求错误。

此时,我们运行一段看起来相当无害的代码,将blob并行上载到Azure存储:

    public static void UploadBlobBlocksInParallel(this CloudBlockBlob blob, FileInfo file) 
    {
        blob.DeleteIfExists();
        blob.Properties.ContentType = file.GetContentType();
        blob.Metadata["Extension"] = file.Extension;

        byte[] data = File.ReadAllBytes(file.FullName);

        int numberOfBlocks = (data.Length / BlockLength) + 1;
        string[] blockIds = new string[numberOfBlocks];

        Parallel.For(
            0, 
            numberOfBlocks, 
            x =>
        {
            string blockId = Convert.ToBase64String(Guid.NewGuid().ToByteArray());
            int currentLength = Math.Min(BlockLength, data.Length - (x * BlockLength));

            using (var memStream = new MemoryStream(data, x * BlockLength, currentLength))
            {
                var blockData = memStream.ToArray();
                var md5Check = System.Security.Cryptography.MD5.Create();
                var md5Hash = md5Check.ComputeHash(blockData, 0, blockData.Length);

                blob.PutBlock(blockId, memStream, Convert.ToBase64String(md5Hash));
            }

            blockIds[x] = blockId;
        });

        byte[] fileHash  = _md5Check.ComputeHash(data, 0, data.Length);
        blob.Metadata["Checksum"] = BitConverter.ToString(fileHash).Replace("-", string.Empty);
        blob.Properties.ContentMD5 = Convert.ToBase64String(fileHash);

        data = null;
        blob.PutBlockList(blockIds);
        blob.SetMetadata();
        blob.SetProperties();
    }

一切都很神秘;我认为我们用来计算块列表的算法应该产生长度相同的字符串…

我们遇到了类似的问题,但是我们没有指定任何块ID,甚至没有在任何地方使用块ID。在我们的案例中,我们使用了:

using (CloudBlobStream stream = blob.OpenWrite(condition))
{
   //// [write data to stream]

   stream.Flush();
   stream.Commit();
}
这将导致指定的块列表无效。在并行加载下出错。将此代码切换为使用方法,同时将数据缓冲到内存中修复了该问题:

using (MemoryStream stream = new MemoryStream())
{
   //// [write data to stream]

   stream.Seek(0, SeekOrigin.Begin);
   blob.UploadFromStream(stream, condition);
}

显然,如果将太多数据缓冲到内存中,这可能会对内存产生负面影响,但这只是一种简化。需要注意的是,
UploadFromStream(…)
在某些情况下使用
Commit(),但检查附加条件以确定要使用的最佳方法。

当多个线程将流打开到具有相同文件名的blob中并尝试同时写入该blob时,也会发生此异常。

注意:此解决方案基于Azure JDK代码,但我认为我们可以安全地假设纯REST版本将具有非常相同的效果(实际上与任何其他语言一样)

因为我花了整整一天的时间来解决这个问题,即使这是一个极端的情况,我也会在这里留下一张便条,也许这会对某些人有所帮助

我做的一切都对。我有顺序正确的块ID,我有相同长度的块ID,我有一个干净的容器,没有以前一些块的剩余物(这三个原因是我能通过谷歌找到的唯一原因)

有一个陷阱:我一直在为commit via构建阻止列表

CloudBlockBlob.commitBlockList(Iterable<BlockEntry> blockList)
通过

BlockSearchMode.COMMITTED
在第二个论点中。事实证明,这是根本原因。有一次我把它改成了

BlockSearchMode.UNCOMMITTED
最终在单参数构造函数上实现

BlockEntry(String id)

默认情况下使用uncommitted,提交阻止列表有效,blob成功保留。

您提到过有时候会发生这种情况。这种情况是发生在特定文件中,还是随机发生的,即代码在某个文件中失败,然后在同一个文件中再次工作?有没有可能,街区的数量超过50000座。另外,您是否可以检查失败的文件,文件大小是否可以被块长度整除?如果可能的话,你能运行fiddler并跟踪失败的请求,特别是正在发送的数据吗?好主意,@Gaurav Mantri,谢谢你——不,它是“随机”发生的,对同一个文件重试似乎有效。对块大小(4M)也没有特别的依赖关系,是的,我的同事指出了其中的fencepost错误。不幸的是,我们在这件事上处于云端,所以不能胡闹。想知道你是否找到了解决这个问题的方法?我们有一个适当的重试策略,可以为我们解决这个问题,但我们仍然会得到10万次写入中可能有1次失败(据估计)。你有没有追根究底?@ScottCate-遗憾的是,没有。我们也加入了重试策略,但仍然看到(公认罕见的)失败。我现在已经转到另一个项目,所以我不知道它是否仍然发生。很好,谢谢。我最初的问题来自六年前:代码已经不存在了,唉,我为之编写代码的组织也不存在了。
BlockEntry(String id)