访问 CosmosDB 的机器人导致 "PreconditionFailed" 错误
Bots accessing CosmosDB cause "PreconditionFailed" error
我有几个机器人在访问 CosmosDB。他们通过来自客户端的 URL 参数获取 userId。
我的问题是在不同的实例中同时使用同一个机器人。
假设我开始与用户 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);
我获取用户ID如下,等于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;
}
}
我对数据库的读写如下:
// 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]);
}
如果用户是新用户,这是我保存到数据库的 "empty" 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
在此先感谢您的帮助!
这是因为您将所有内容都存储在构造函数中,机器人只调用一次。两个用户同时使用 this.changes
和 this.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' })
备注
关于您最初尝试的方式,通过在构造函数中实例化 this.userId
:
您实际上可以在 C# 中解决这个问题,因为在 C# SDK 中,构造函数会在每条消息上调用(JS/TS 它仅在机器人首次启动时调用)。 BotFramework 的 JS 和 C# SDK 之间存在这个主要区别,因为 C# 是多线程的,而 JS 是单线程的。通过在 C# 中对每条消息调用构造函数,您可以跨线程分散负载。这在 JS/TS.
中是不可能的
话虽这么说,但我仍然会避免这种情况,因为最好在代码实践中尽可能不使用 "global" 变量。
我有几个机器人在访问 CosmosDB。他们通过来自客户端的 URL 参数获取 userId。
我的问题是在不同的实例中同时使用同一个机器人。
假设我开始与用户 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);
我获取用户ID如下,等于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;
}
}
我对数据库的读写如下:
// 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]);
}
如果用户是新用户,这是我保存到数据库的 "empty" 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
在此先感谢您的帮助!
这是因为您将所有内容都存储在构造函数中,机器人只调用一次。两个用户同时使用 this.changes
和 this.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' })
备注
关于您最初尝试的方式,通过在构造函数中实例化 this.userId
:
您实际上可以在 C# 中解决这个问题,因为在 C# SDK 中,构造函数会在每条消息上调用(JS/TS 它仅在机器人首次启动时调用)。 BotFramework 的 JS 和 C# SDK 之间存在这个主要区别,因为 C# 是多线程的,而 JS 是单线程的。通过在 C# 中对每条消息调用构造函数,您可以跨线程分散负载。这在 JS/TS.
中是不可能的话虽这么说,但我仍然会避免这种情况,因为最好在代码实践中尽可能不使用 "global" 变量。