访问CosmosDB的机器人导致“PreconditionFailed”错误

问题描述 投票:1回答:1

我有几个机器人访问CosmosDB。他们通过客户端的URL参数获取userId。

在同一时间在不同实例中使用相同的bot时出现我的问题。

假设我开始与用户ID“1”进行对话,输入一些数据,如姓名,年龄,性别。在对话过程中,我打开另一个用户ID为“2”的网站。用户2输入名称。用户1完成配置文件创建。提示用户2年龄。突然,来自用户2的用户ID变为“1”,并且配置文件数据与来自用户1的用户数据相同。

我不时会收到以下错误:

{ code: 412,
body: ‘{“code”:“PreconditionFailed”,“message”:“Operation cannot be performed because one of the specified precondition is not met., \r\nRequestStartTime: 2019-03-07T20:57:19.6998897Z, RequestEndTime: 2019-03-07T20:57:19.7098745Z, Number of regions attempted: 1\r\nResponseTime: 2019-03-07T20:57:19.7098745Z, StoreResult: StorePhysicalAddress: rntbd://cdb-ms-prod-westeurope1-fd21.documents.azure.com:16817/apps/9bc5d0cc-9b7c-4b1d-9be2-0fa2654271c4/services/3507a423-5215-48ca-995a-8763ec527db8/partitions/940cd512-aa34-4c93-9596-743de41037da/replicas/131964420031154286p/, LSN: 172, GlobalCommittedLsn: 171, PartitionKeyRangeId: 0, IsValid: True, StatusCode: 412, SubStatusCode: 0, RequestCharge: 1.24, ItemLSN: -1, SessionToken: 172, UsingLocalLSN: False, TransportException: null, ResourceType: Document, OperationType: Replace\r\n, Microsoft.Azure.Documents.Common/2.2.0.0”}’,
activityId: ‘fbdaeedb-a73f-4052-bd55-b1417b10583f’ }

因此,机器人似乎以某种方式混淆了用户ID。

我将UserState和ConversationState保存在本地存储中,因为我不需要将它们保存在我的数据库中:

// Local browser storage
const memoryStorageLocal = new MemoryStorage();

// ConversationState and UserState
const conversationState = new ConversationState(memoryStorageLocal);
const userState = new UserState(memoryStorageLocal);

我获取如下的userID,它等于URL参数

for (var idx in turnContext.activity.membersAdded) {
    if (turnContext.activity.membersAdded[idx].id !== turnContext.activity.recipient.id) {
        console.log("User added");
        this.userID = turnContext.activity.membersAdded[idx].id;
    }
}

我读取和写入DB如下:

// Read userData object
try {
    user = await this.memoryStorage.read([this.userID]);
    console.log("User Object read from DB: " + util.inspect(user, false, null, true /* enable colors */));
}
catch(e) {
    console.log("Reading user data failed");
    console.log(e);
}

// If user is new, create UserData object and save it to DB and read it for further use
if(isEmpty(user)) {
    await step.context.sendActivity("New User Detected");
    this.changes[this.userID] = this.userData;
    await this.memoryStorage.write(this.changes);
    user = await this.memoryStorage.read([this.userID]);
}

如果用户是新用户,这是我保存到数据库的“空”userData对象:

this.userData = {

    name: "",
    age: "",
    gender: "",
    education: "",
    major: "",

    riskData: {
        roundCounter: "",
        riskAssessmentComplete: "",
        riskDescription: "",
        repeat: "",
        choices: "",
    },

    investData: {
        repeat: "",
        order: "",
        choice: "",
        follow: "",
        win1: "",
        win2: "",
        loss1: "",
        loss2: "",
    },

    endRepeat: "",
    eTag: '*',
} 

对我而言,实际问题似乎不是我对CosmosDB的读写方式,而是因为用户ID和用户数据在同时使用时混淆了,因此没有不同的机器人实例。

我怎样才能做到这一点?你可以在这里找到我的机器人代码:

https://github.com/FRANZKAFKA13/roboadvisoryBot

在此先感谢您的帮助!

parallel-processing botframework azure-cosmosdb etag
1个回答
1
投票

这是因为你在构造函数中存储了所有东西,只有机器人调用一次。两个用户同时使用this.changesthis.userId的相同实例。当一个用户更改它时,它也会更改为另一个用户。它基本上使它们成为任何用户都有能力改变的全局变量。

相反,您可以使用step.options将所需的变量传递给对话框。

例如,

调用对话框:

await dc.beginDialog('welcome', userId)

在对话框中使用:

this.userId = step.options // It may also be step.options.userId, if you pass it in as part of a larger object, like .beginDialog('welcome', { userId: '123', userName: 'xyz' })

Note

关于你最初尝试这个的方式,通过在构造函数中实例化this.userId

你可以在C#中实现这一点,因为在C#SDK中,每个消息都会调用构造函数(JS / TS只在首次启动机器人时调用它)。 BotFramework的JS和C#SDK之间存在这种主要差异,因为C#是多线程的,JS是单线程的。通过在C#中的每条消息上调用构造函数,您可以跨线程分散负载。这在JS / TS中是不可能的。

话虽这么说,我仍然会避免这种情况,因为在可能的情况下不使用“全局”变量是更好的代码实践。

© www.soinside.com 2019 - 2024. All rights reserved.