Microsoft Bot Builder 4.1.5 , C#
Description
Currently I am using Enterprise Bot Template v4 SDK C# for developing the bot. I am using Cosmos DB for user state management. Everything works fine with enterprise bot template , but when configure the bot on MS Teams channel , at that point of time Bot fails to write the state data to Cosmos DB and gives an error
What is actually happening..
Cosmos DB(document DB) handles every individual row/data uniquely by "id" column. Cosmos DB is having a limitation of the length of the "Id" which is 255 characters.
Now, when bot tries to store the user state for MS Teams channel in cosmos db , at that point of time Enterprise bot template/ bot framework v4 automatically creates an unique id which has length more than 255 characters. So this gives an exception in code.
Please someone look into this issue as this is a blocking issue for ms teams.
As a workaround of this issue i managed to store the state in blob storage and deployed my bot.
Steps to reproduce the behavior:
It should save the state.
Message: {"Errors":["The input name 'msteams2fconversations2fa:1de_GDChOYejATRlAuinJGMmbe2YZQ3SdEB7EPvQ_WOIUSTuU64GB5HP9BX8ol2oo1WPck-s-RpJWbMo3X9fayx-OFmyF3LnBgs7jDYj79WniZvCMqbZZy8M5RPLh2MXm2fusers2f29:1RNNKZBhoBQm0hQVbvY5NwyFWEsXABeNQUxZEHPwUYkjfn_whINgbuk6cnw21K1zFSF_ozHXBkBRJfXTLfMgbXA' is invalid. Ensure to provide a unique non-empty string less than '255' characters."]}
ActivityId: ad22f7b1-1572-406f-9440-b77f328239a0, Request URI: /apps/c8f5225c-4725-4992-9a92-6dd3f3d1510b/services/82820cba-3446-437f-8667-0a3eac3be57f/partitions/db603d54-4640-4e74-a0bc-aa19ce2198a3/replicas/131862185541974409p/, RequestStats:
RequestStartTime: 2018-11-29T12:08:33.5809299Z, Number of regions attempted: 1
, SDK: Microsoft.Azure.Documents.Common/2.1.0.0, Windows/10.0.17134 documentdb-netcore-sdk/1.9.1
[bug]
Closing as a duplicate of #27
Reopening here as this is the better repo to address the issue.
Confirming that this does look like it is a bug.
Azure Cosmos DB does indeed have a limit of 255 bytes for Row Keys:
https://docs.microsoft.com/en-us/azure/cosmos-db/faq#table
It was previously implimented in v3 to truncate the key:
https://github.com/Microsoft/BotBuilder-Azure/blob/master/CSharp/Library/Microsoft.Bot.Builder.Azure/DocumentDbBotDataStore.cs#L300
It does not look like this is implimented in v4:
https://github.com/Microsoft/botbuilder-dotnet/blob/master/libraries/Microsoft.Bot.Builder.Azure/CosmosDbStorage.cs
Thanks @dmvtech for the details. Is there any workaround which i can implement until this is fixed in new version of bot builder sdk.
@sw-ms-roshanparmar Not that I know of at this point. I will update if I come up with one.
Hi @sw-ms-roshanparmar.
Sorry for the delay. Please find a workaround below that you should be able to use until this is fixed otherwise.
I wasn't able to fully test with a large conversation ID, but I just changed my MAX_KEY_LENGTH to be 50 and was able to confirm the general functionality works. What you need to do; is add a new class to your bot and inherit from the BotState class (just as the ConversationState class does here. Then, modify the GetStorageKey() method to check for large conversationId and then truncate it if so.
namespace Microsoft.BotBuilderSamples
{
public class DMVConversationState : BotState
{
/// <summary>
/// Initializes a new instance of the <see cref="ConversationState"/> class.
/// </summary>
/// <param name="storage">The storage provider to use.</param>
public DMVConversationState(IStorage storage)
: base(storage, nameof(ConversationState))
{
}
internal const int MAX_KEY_LENGTH = 254;
protected override string GetStorageKey(ITurnContext turnContext)
{
var channelId = turnContext.Activity.ChannelId ?? throw new ArgumentNullException("invalid activity-missing channelId");
var conversationId = turnContext.Activity.Conversation?.Id ?? throw new ArgumentNullException("invalid activity-missing Conversation.Id");
if((channelId == "msteams") && (conversationId.Length > MAX_KEY_LENGTH)) //not necessary to check for *just* Teams
{
var hash = conversationId.GetHashCode().ToString("x");
conversationId = conversationId.Substring(0, MAX_KEY_LENGTH - hash.Length) + hash;
}
return $"{channelId}/conversations/{conversationId}";
}
}
}
Then update your Startup and Bot.cs code to change references from ConversationState to your new class and you should be good to go.
Thanks @dmvtech ,
This solved my problem. However, I have to tweak the code little bit to incorporate the PrivateConverstionState Id in my implementation.
Thanks.
@sw-ms-roshanparmar I'm not sure there's an easy answer to this. The workaround that @dmvtech provided is something I was about to recommend.
The core problem is that we cons up the keys, and require them to be unique. If we truncate the key, as was done in v3, then guarantees around uniqueness are gone.
We could hash the key if Length > 255. That would probably provide a better uniqueness guarantee.
The problem is this only applies to CosmosDB. Other data providers all have different restrictions.
For now, I think the if (length >255) then hash() seems like the best overall mechanism. PR coming shortly.
@sw-ms-roshanparmar I've got PR #1370 pending that resolves this.
Most helpful comment
Hi @sw-ms-roshanparmar.
Sorry for the delay. Please find a workaround below that you should be able to use until this is fixed otherwise.
I wasn't able to fully test with a large conversation ID, but I just changed my MAX_KEY_LENGTH to be 50 and was able to confirm the general functionality works. What you need to do; is add a new class to your bot and inherit from the BotState class (just as the ConversationState class does here. Then, modify the GetStorageKey() method to check for large conversationId and then truncate it if so.
Then update your Startup and Bot.cs code to change references from ConversationState to your new class and you should be good to go.