Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Migrated bot state provider but calls to state.botframework.com are still being made

I migrated to a Table Azure Provider for managing Microsoft bot framework state.

In my telemetry, I see dependencies call being made to my new Table Azure Storage however I still see a lot of calls being made to state.botframework.com and some have the usual random slow response time.

This does not seem normal as I would have expect all calls to be directed to my new private state provider:

Example: https://state.botframework.com/v3/botstate/facebook/users/999999999999

Example of call to new private state provider: https://xxxxxxxxxx.table.core.windows.net:443/botdata(PartitionKey='facebook:private',RowKey='XXXXXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXX

Other potential relevant information:

  • The application was live for a while using Microsoft State provider before switching to a table azure storage
  • I do not persist any mission critical information in the bot state only the state of a dialog with a user; and I can loose that without significant impact.
  • The bot sends notification using a resumption cookie saved in a custom SQL database.
  • This is how the Table Azure Provider is registered in a Autofac Module:
protected override void Load(ContainerBuilder builder)
{
    base.Load(builder);

    //Register custom datastore
    builder
        .RegisterKeyedType<TableBotDataStore, IBotDataStore<BotData>>()
        .Keyed<IBotDataStore<BotData>>(AzureModule.Key_DataStore)
        .WithParameter((pi, c) => pi.Name == "connectionString",
                            (pi, c) => 
        ConfigurationManager.ConnectionStrings["X"].ConnectionString)
        .SingleInstance();

    builder.RegisterAdapterChain<IBotDataStore<BotData>>
            (
                typeof(TableBotDataStore),
                typeof(CachingBotDataStore)

            )
            .InstancePerLifetimeScope();
}
  • I have the following method that check for saved state version compared to the running service version. This code was introduced because sometimes a user would have a serialized dialog state that was not compatible with the changes made in a dialog with a new release.
public static async Task CheckClientVersion(Activity activity)
{    
    StateClient stateClient = activity.GetStateClient();
    BotData userData = stateClient.BotState.GetUserData(activity.ChannelId, activity.From.Id);
    if (userData?.GetProperty<string>("version")?.CompareTo(Assembly.GetExecutingAssembly().GetName().Version.ToString()) != 0)
    {

        string[] result = await stateClient.BotState.DeleteStateForUserAsync(activity.ChannelId, activity.From.Id, CancellationToken.None);
        userData = stateClient.BotState.GetUserData(activity.ChannelId, activity.From.Id);
        userData.SetProperty<string>("version", Assembly.GetExecutingAssembly().GetName().Version.ToString());
        await stateClient.BotState.SetUserDataAsync(activity.ChannelId, activity.From.Id, userData);
    }
}
like image 376
Greg Avatar asked Oct 17 '22 01:10

Greg


1 Answers

Is your second Code Snippet is that from a Dialog or MessagesController? the issue is you are using activity.GetStateClient which Always calls the default state client rather than your own custom one.

In order to Accomplish what you want code similar to below must be used if you are manipulating state in the MessagesController

public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
            {
                if (activity.Type == ActivityTypes.Message)
                {

                    var message = activity as IMessageActivity;
                    using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message))
                    {
                        var botDataStore = scope.Resolve<IBotDataStore<BotData>>();
                        var key = Address.FromActivity(message);

                        var userData = await botDataStore.LoadAsync(key, BotStoreType.BotUserData, CancellationToken.None);

                        userData.SetProperty("key 1", "value1");
                        userData.SetProperty("key 2", "value2");

                        await botDataStore.SaveAsync(key, BotStoreType.BotUserData, userData, CancellationToken.None);
                        await botDataStore.FlushAsync(key, CancellationToken.None);
                    }
                    await Conversation.SendAsync(activity, () => new Dialogs.RootDialog());
                }
            }   

To answer your follow up question:

using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message))
            {
                var token = new CancellationToken();
                var botData = scope.Resolve<IBotData>();
                await botData.LoadAsync(token);

                var stack = scope.Resolve<IDialogStack>();
                stack.Reset();

                botData.UserData.Clear(); 
                botData.ConversationData.Clear();
                botData.PrivateConversationData.Clear();
                await botData.FlushAsync(token);

                var botToUser = scope.Resolve<IBotToUser>();
                await botToUser.PostAsync(message.CreateReply($"{timerMessage}  Conversation aborted."));
            }
like image 94
D4RKCIDE Avatar answered Oct 21 '22 07:10

D4RKCIDE