Azure编程笔记(5):长时间的异步操作带来的问题

??

内容提要

Azure Storage里很多操作需要花费很长的时间。为了提高效率,这些耗时的操作是以异步的方式响应的。也就是说调用这些操作对应的函数虽然结束,但背后的操作可能还要持续一段时间。如果没有合理的对待从函数返回到操作真正结束这段时间,我们的程序就有可能出现问题。下面以删除CloudTable为例讨论这类问题。

问题描述

我们写一个简单的单元测试的Class来重现这个问题。首先我们定义两个函数,分别用来初始化测试函数和清理测试函数所留下的数据。函数InitializeTest初始化一个名叫TestTable的CloudTable。如果该表不存在,则创建它。函数CleanupTest删除该表格。

private const string tableName = "TestTable";
private CloudTable table;
CloudTableClient tableClient;

[TestInitialize]
public void InitializeTest()
{
    string connectionString = Constant.connectionString;
    CloudStorageAccount storageAccount = Utilities.GetStorageAccount(connectionString);

    tableClient = storageAccount.CreateCloudTableClient();
    this.table = tableClient.GetTableReference(tableName);
    this.table.CreateIfNotExists();
}

[TestCleanup]
public void CleanupTest()
{
    if(table != null)
    {
        table.DeleteIfExists();
    }
}

为了使简化我们将要讨论的问题,我们添加两个看起来会永远通过的测试用例:

[TestMethod]
public void TestMethod1()
{
    Assert.IsTrue(true);
}

[TestMethod]
public void TestMethod2()
{
    Assert.IsTrue(true);
}

当我们在Visual Studio中运行上述两个测试用例时,却发现只有一个测试用例能够通过测试,另外一个测试用例则会抛出如下的异常:

问题根源

运行上述测试用例,第一个测试用例顺利完成,但在初始化第二个测试用例调用CreateIfNotExist试图创建CloudTable的时候出错了。从上面的异常信息的截图中我们可以看出,该异常的错误代码是TableBingDeleted。

这个错误代码告诉我们的信息是,在清理第一个测试用例的数据时,调用DeleteIfExists删除CloudTable虽然顺利返回,但实际上删除操作并没有立即结束,只是在后台简单地设一个标签说该CloudTable需要被删除就返回了而已。真正删除一个CloudTable可能需要40秒甚至更多的时间。如果我们在DeleteIfExists结束之后立即调用CreateIfNotExist重新创建同一个CloudTable的时候,该表格实际上还存在并处在正被删除的状态,因此再次创建的操作无法完成。

解决问题

在本文的例子中,我们删除CloudTable仅仅是因为需要清理前一个测试遗留的数据。我们删除数据可以不删除整个CloudTable,而清除CloudTable里的所有数据。删除CloudTable里的数据(通过TableOperation.Delete)是同步的,如果函数返回,Azure会保证数据一定被删除。

如果我们一定要删除整个CloudTable,那么在创建CloudTable的时候就得做好错误处理。下面是一段调用CloudTable.CreateIfNotExists的参考代码:

[TestInitialize]
public void InitializeTest()
{
    string connectionString = Constant.connectionString;
    CloudStorageAccount storageAccount = Utilities.GetStorageAccount(connectionString);

    tableClient = storageAccount.CreateCloudTableClient();
    table = tableClient.GetTableReference(tableName);

    int retryTimes = 0;
    int maxRetryTimes = 20;
    StorageException lastException = null;

    while (retryTimes < maxRetryTimes)
    {
        try
        {
            table.CreateIfNotExists();
            break;
        }
        catch (StorageException excep)
        {
            var extendedInformation = excep.RequestInformation.ExtendedErrorInformation;
            var errorCode = extendedInformation.ErrorCode;
            if (errorCode == "TableBeingDeleted")
            {
                retryTimes++;
                lastException = excep;

                Thread.Sleep(5 * 1000); // Sleep 5 seconds
            }
            else
            {
                throw;
            }
        }
    }

    if(retryTimes == maxRetryTimes && lastException != null)
    {
        throw lastException;
    }
}

Azure编程笔记(5):长时间的异步操作带来的问题,布布扣,bubuko.com

时间: 2024-08-02 10:56:44

Azure编程笔记(5):长时间的异步操作带来的问题的相关文章

Azure编程笔记(2):重复CloudTable的修改操作

内容提要 对MicrosoftAzure的CloudTable进行操作,有很多种操作失败的可能,比如网络连接异常,比如短时间内发送的请求数太多.很多时候我们在失败之后过一段时间再重试,就能操作成功.但是有些失败不是简单重试就解决的.本文讲述的在修改操作时候发生的PreconditionFailed错误就是一个例子. 问题描述 在上一篇博客里,我们定义一个类型Account,用来模拟社交网站的账户基本信息.这里我们继续用这个类型为例子来讲述多个线程同时发送更新请求时可能发生的问题. 我们首先用如下

Azure编程笔记(3):用Fiddler调试Azure的应用程序

 内容提要 Azure的服务是通过RESTfulAPI提供的.虽然Azure针对很多编程语言都提供了SDK,但这些SDK也只是RESTfulAPI的一层封装.在调用SDK或者RESTfulAPI出错时,我们需要使用调试工具来分析并解决问题.Fiddler是一款功能强大的免费工具,我们可以使用Fiddler来调试Azure的应用程序.本文展示如何用Fiddler调试一个常见的访问Storage的问题. 问题描述 在前面的两篇博客中,我们模拟社交网站定义了一个Account类型.本文我们继续以A

Azure编程笔记(4):管理Cloud Service的证书

?? 我们在Microsoft Azure中部署CloudService的时候,可能会用到证书.通常在两种情况下需要用到证书.一是把证书安装在服务器端.此时证书用来建立HTTPS/SSL连接,以便保护传输中的数据.二是把证书部署在客户端.此时客户端发起连接请求时,它会把证书信息添加到请求中.服务器端收到请求之后,会验证其中的证书是不是合法的证书.这种情况下证书是用来验证用户的.接下来我们分两种情况来讨论如何管理证书. 把证书安装在服务器端 假设我们用ASP .NET的Web API开发一个Web

Azure编程笔记(1):序列化复杂类型的TableEntity字段

内容提要 在使用MicrosoftAzure的CloudTable存储数据时,我们先要把数据定义成TableEntity的子类.如果TableEntity中包含复杂类型(比如容器类型如List等.或者自定义类型)的字段,这些字段需要自己做序列化才能保存到CloudTable中去. 问题描述 我们模拟社交网站定义一个类型Account,它包含一个账户的基本信息如果邮箱.姓名.生日等等,同时它还包含一个账户的好友列表: public class Account : TableEntity { pub

storysnail的Windows串口编程笔记

storysnail的Windows串口编程笔记 作者 He YiJun – storysnail<at>gmail.com 团队 ls 版权 转载请保留本声明! 本文档包含的原创代码根据General Public License,v3 发布 GPLv3 许可证的副本可以在这里获得:http://www.gnu.org/licenses/gpl.html 本文档根据GNU Free Documentation License 1.3发布 GFDL1.3许可证的副本可以在这里获得:http:/

python核心编程--笔记

python核心编程--笔记 的解释器options: 1.1 –d   提供调试输出 1.2 –O   生成优化的字节码(生成.pyo文件) 1.3 –S   不导入site模块以在启动时查找python路径 1.4 –v   冗余输出(导入语句详细追踪) 1.5 –m mod 将一个模块以脚本形式运行 1.6 –Q opt 除法选项(参阅文档) 1.7 –c cmd 运行以命令行字符串心事提交的python脚本 1.8 file   以给定的文件运行python脚本 2 _在解释器中表示最后

Bash Shell脚本编程笔记总结(一)

本文是上课笔记总结,涉及细节知识点会在以后文章说明! bash脚本编程: 脚本程序:解释器解释执行: shell: 交互式接口:编程环境: shell: 能够提供一些内部命令,并且能通过PATH环境变量找到外部命令:把命令提交给内核启动为进程: 编程环境: 流程控制语句: 顺序执行: 循环执行: 选择执行: 条件测试:真.假 $? 命令的状态结果: 0: 真 1-255: 假 过程式的编程语言的元素:变量.流程.函数.数组 变量:局部变量.本地变量.环境变量.位置参数变量.特殊变量 变量: 数值

storysnail的Linux串口编程笔记

storysnail的Linux串口编程笔记 作者 He YiJun – storysnail<at>gmail.com 团队 ls 版权 转载请保留本声明! 本文档包含的原创代码根据General Public License,v3 发布 GPLv3 许可证的副本可以在这里获得:http://www.gnu.org/licenses/gpl.html 本文档根据GNU Free Documentation License 1.3发布 GFDL1.3许可证的副本可以在这里获得:http://w

标C编程笔记day07 常用标准库介绍、字符串输入处理、字符指针的使用

常用标准库: assert.h:断言,包含assert宏.可以进行自我检查 ctype.h:字符处理,字符的分类,大小转换 errno.h:错误信息处理 float.h:浮点数特性 limits.h:整数的大小,提供了描述整数类型的宏 lcale.h:本地化 math.h:数学函数 setjmp.h:跳转 signal.h:信号处理 stdarg.h:可变长参数处理 stddef.h:经常使用的类型的定义 stdio.h:输入输出 string.h:字符串处理 stdlib.h:其他函数,字符串