Боты, обращающиеся к CosmosDB, вызывают ошибку «PreconditionFailed» - PullRequest
1 голос
/ 08 марта 2019

У меня есть несколько ботов, обращающихся к CosmosDB. Они получают идентификатор пользователя через параметр URL-адреса от клиента.

Моя проблема возникает при использовании одного и того же бота в разных экземплярах одновременно.

Допустим, я начинаю разговор с идентификатором пользователя "1", введите некоторые данные, такие как имя, возраст, пол. В середине разговора я открываю другой сайт с идентификатором пользователя «2». Пользователь 2 вводит имя. Пользователь 1 завершает создание профиля. Пользователю 2 предлагается возраст. Внезапно идентификатор пользователя от пользователя 2 изменится на «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’ }

Итак, кажется, бот каким-то образом перепутал идентификаторы пользователей.

Я сохраняю UserState и ConversationState в локальном хранилище, поскольку они не нужны для сохранения в моей БД:

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

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

Я получаю идентификатор пользователя следующим образом, что соответствует параметру 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]);
}

Это «пустой» объект 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, а в том, что нет разных экземпляров бота, поскольку идентификаторы пользователя и данные пользователя смешиваются при их одновременном использовании.

Как я могу заставить эту работу? Вы можете найти мой код бота здесь:

https://github.com/FRANZKAFKA13/roboadvisoryBot

Заранее спасибо за любую помощь!

1 Ответ

1 голос
/ 08 марта 2019

Это потому, что вы храните все в своем конструкторе, который вызывается ботом только один раз.Одинаковые экземпляры 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 вызывается только когда ботсначала инициируется).Это основное различие существует между JS и C # SDK BotFramework, потому что C # является многопоточным, а JS однопоточным.Вызывая конструктор для каждого сообщения в C #, вы можете распределить нагрузку между потоками.Это невозможно в JS / TS.

При этом я бы по-прежнему избегал этого, так как в коде лучше не использовать «глобальные» переменные, когда это возможно.

...