Microsoft.Azure.Cosmos.Table LocationMode.SecondaryOnly RA-GRS 异常 此操作只能针对主存储位置执行

Posted

技术标签:

【中文标题】Microsoft.Azure.Cosmos.Table LocationMode.SecondaryOnly RA-GRS 异常 此操作只能针对主存储位置执行【英文标题】:Microsoft.Azure.Cosmos.Table LocationMode.SecondaryOnly RA-GRS Exception This operation can only be executed against the primary storage location 【发布时间】:2020-06-08 05:15:25 【问题描述】:

在解析使用 SAS 令牌的连接字符串时,我很难让 Microsoft.Azure.Cosmos.Table 自动初始化 SecondaryUri。

所以我最终在连接字符串中明确指定了 TableSecondaryEndpoint,这可行,但我无法查询辅助节点,因为 SDK 在尝试请求之前就抛出了异常。

在我的测试中,我发现这是 Microsoft.WindowsAzure.Storage.Table 8.7.0 中不存在的回归(Microsoft.Azure.Cosmos.Table 1.0.6 的基础)

非常欢迎高手提出这一点。谢谢。

此异常的项目代码在这里(也复制在下面): https://github.com/golfalot/SOshowAzureTableBug

详细说明这里提出的 SecondaryUri 初始化问题的侧面问题: https://github.com/Azure/azure-cosmos-table-dotnet/issues/36

using System;
using System.Collections.Generic;

using LEGACY_STORAGE = Microsoft.WindowsAzure.Storage;
using LEGACY_RETRY = Microsoft.WindowsAzure.Storage.RetryPolicies;
using LEGACY_TABLE = Microsoft.WindowsAzure.Storage.Table; //8.7.0 because this is the base for 1.0.6

using NEWEST_TABLE = Microsoft.Azure.Cosmos.Table; // version 1.0.6
using Microsoft.Azure.Cosmos.Table; // had to add this to get access CreateCloudTableClient extension method

using System.Diagnostics;

namespace SOshowAzureTableBug

    class Program
    
        // the SAS token is immaterial in reproducing the problem
        const string connectionTableSAS = "TableSecondaryEndpoint=http://127.0.0.1:10002/devstoreaccount1-secondary;TableEndpoint=http://127.0.0.1:10002/devstoreaccount1;SharedAccessSignature=immaterial";
        static void Main(string[] args)
        

            /* Legacy Table SDK */
            var storageAccountLegacy = LEGACY_STORAGE.CloudStorageAccount.Parse(connectionTableSAS);
            var tableClientLegacy = storageAccountLegacy.CreateCloudTableClient();
            Debug.Assert(tableClientLegacy.StorageUri.SecondaryUri != null); // demonstrate SecondaryUri initialised

            var tableRequestOptionsLegacy = new LEGACY_TABLE.TableRequestOptions ()  LocationMode = LEGACY_RETRY.LocationMode.SecondaryOnly ;
            tableClientLegacy.DefaultRequestOptions = tableRequestOptionsLegacy;

            var tableLegacy = tableClientLegacy.GetTableReference("foo"); // don't need table to exist to show the issue
            var retrieveOperation = LEGACY_TABLE.TableOperation.Retrieve(string.Empty, string.Empty, new List<string>()  "bar" );

            var tableResult = tableLegacy.Execute(retrieveOperation);
            Console.WriteLine("Legacy PASS");


            /* Newset Table SDK */
            var storageAccountNewest = NEWEST_TABLE.CloudStorageAccount.Parse(connectionTableSAS);
            var tableClientNewest = storageAccountNewest.CreateCloudTableClient(new TableClientConfiguration());
            Debug.Assert(tableClientNewest.StorageUri.SecondaryUri != null); // demonstrate SecondaryUri initialised

            var tableRequestOptionsNewest = new NEWEST_TABLE.TableRequestOptions()  LocationMode = NEWEST_TABLE.LocationMode.SecondaryOnly ;
            tableClientNewest.DefaultRequestOptions = tableRequestOptionsNewest;

            var tableNewset = tableClientNewest.GetTableReference("foo"); // don't need table to exist to show the issue
            var retrieveOperationNewset = NEWEST_TABLE.TableOperation.Retrieve(string.Empty, string.Empty, new List<string>()  "bar" );

            /* throws Microsoft.Azure.Cosmos.Table.StorageException
             * Exception thrown while initializing request: This operation can only be executed against the primary storage location
             */
            var tableResultNewset = tableNewset.Execute(retrieveOperationNewset);

            Console.WriteLine("Press any key to exit");
            Console.Read();
        
    


【问题讨论】:

您可以尝试以下方法:const string connectionTableSAS = "TableSecondaryEndpoint=http://127.0.0.1:10002/devstoreaccount1-secondary;TableEndpoint=http://127.0.0.1:10002/devstoreaccount1;SharedAccessSignature=st=2020-02-24T12%3A47%3A31Z&amp;se=2020-02-25T12%3A47%3A31Z&amp;sp=r&amp;sv=2018-03-28&amp;tn=ratingreflocationa20200217aandmarketvalueb20200217bxml&amp;sig=wc7tW52nstzdGMzlQuaRuakShJ%2BHmpbv8jbMlnn1lug%3D";? @GauravMantri 嗨。没有变化,同样的例外。不过谢谢。为了澄清,在针对模拟器创建示例之前,我在真实存储帐户上遇到了同样的问题。我不认为 Secondary 的价值在这个问题上很重要。 我也能重现这个问题。但是我找到了一种解决方法。这可以接受吗? @GauravMantri 请教育我! :-) 添加了答案。请检查。 HTH。 【参考方案1】:

我相信您遇到了 SDK 的错误。

当我尝试下面的代码时,我得到了和你一样的错误:

        var account = CloudStorageAccount.Parse(connectionString);

        var requestOptions = new TableRequestOptions()
        
            LocationMode = LocationMode.SecondaryOnly
        ;
        var client = account.CreateCloudTableClient();
        client.DefaultRequestOptions = requestOptions;
        var table = client.GetTableReference("myTable");
        var op = TableOperation.Retrieve("", "");
        var result1 = table.Execute(op);

我反编译了库代码,找到了罪魁祸首源代码:

if (commandLocationMode == CommandLocationMode.PrimaryOnly)
                
                    if (restCMD.LocationMode == LocationMode.SecondaryOnly)
                    
                        throw new InvalidOperationException("This operation can only be executed against the primary storage location.");//This is the error that gets thrown.
                    
                    Logger.LogInformational(executionState.OperationContext, "This operation can only be executed against the primary storage location.", Array.Empty<object>());
                    executionState.CurrentLocation = StorageLocation.Primary;
                    restCMD.LocationMode = LocationMode.PrimaryOnly;
                

但是,如果我没有在客户端级别设置 DefaultRequestOptions 并在下面的 Execute 方法中指定它,我不会收到错误消息,但这是因为命中了主要端点而不是次要端点(我检查了在提琴手)。

        var account = CloudStorageAccount.Parse(connectionString);

        var requestOptions = new TableRequestOptions()
        
            LocationMode = LocationMode.SecondaryOnly
        ;
        var client = account.CreateCloudTableClient();
        var table = client.GetTableReference("myTable");
        var op = TableOperation.Retrieve("", "");
        var result1 = table.Execute(op, requestOptions);

解决方法

如果您的目标是从辅助位置查询实体,那么您可以在CloudTable 上使用ExecuteQuery 方法,如下所示。这行得通(我再次检查了 Fiddler)。

        var account = CloudStorageAccount.Parse(connectionString);

        var requestOptions = new TableRequestOptions()
        
            LocationMode = LocationMode.SecondaryOnly
        ;
        var client = account.CreateCloudTableClient();
        client.DefaultRequestOptions = requestOptions;
        var table = client.GetTableReference("myTable");
        TableQuery query = new TableQuery();
        var result = table.ExecuteQuery(query).ToList();

【讨论】:

好作品!我强制使用 SecondaryOnly 的最初原因是为了测试地理故障转移(您可能已经猜到了)。你似乎在这个领域很有能力,你建议我向 SDK 所有者提出这个错误的最佳方式是什么?我问是因为github.com/Azure/azure-cosmos-table-dotnet/issues 上似乎没有太多活动@ 如果您在 Twitter 上,只需标记那里的团队。我相信他们的句柄是@AzureCosmosDB。还有一封反馈电子邮件:AskCosmosDB@microsoft.com。我发现它们反应灵敏。 谢谢。只是为了结束循环,电子邮件返回并自动回复说我刚刚完成的 azure-cosmosdb 标记。 是的,我们关闭了该电子邮件。请在这个 github repo 上提出问题。 github.com/Azure/azure-cosmos-table-dotnet/issues 谢谢。 @MarkBrown 谢谢!该问题于 6 天前提交:github.com/Azure/azure-cosmos-table-dotnet/issues/36 :)

以上是关于Microsoft.Azure.Cosmos.Table LocationMode.SecondaryOnly RA-GRS 异常 此操作只能针对主存储位置执行的主要内容,如果未能解决你的问题,请参考以下文章