Arango beginTransaction () не откатывается с помощью trx.abort ()

У меня проблемы с arangodb.beginTransaction(). В моих тестах создание транзакции, вызов функции через trx.run() API и затем прерывание trx не приводит к откату изменений данных в базе данных. Я не понимаю, что происходит, и документация крайне скудна. Это довольно зловещая, но очень расплывчатая документация:

Если данная функция содержит асинхронную логику, в транзакции будет запущена только синхронная часть функции. Например. при использовании async / await в транзакции будет выполняться только код до первого await.

А как насчет вложенного async / await внутри вызываемой функции async? Например, если у меня есть это:

async runQuery(query) {
    try {
      const cursor = await this.arangodb.query(query)
      return cursor.all()
    } catch (error) {
      logger.error('ArangoDB query failed', { stack: error })
      throw error
    }
}

async touchDocument(id, revision) {
    const type = await this.getObjectTypeFromId(id)
    const collection = await this.getCollection(type, false, true)
    const idCollection = await this.getCollection(ID_COLLECTION, false, true)
    const touchAql = aql`
      LET permanentDocId = DOCUMENT(${idCollection}, ${id}).docId
      LET permanentDoc = MERGE( DOCUMENT(permanentDocId),  { _rev : ${revision} })
      UPDATE permanentDoc WITH permanentDoc in ${collection} OPTIONS { ignoreRevs: false } 
      RETURN NEW
    `
    return this.runQuery(touchAql)
}

trx.run(() => this.touchDocument(parentId, parentRevision))

Будет this.arangodb.query() выполняться внутри транзакции или нет?


person robross0606    schedule 21.01.2020    source источник
comment
В документации HTTP API есть дополнительная информация, намекающая на то, что database.query() может вообще не поддерживаться в потоках транзакций: https://www.arangodb.com/docs/stable/http/transaction-stream-transaction.html   -  person robross0606    schedule 21.01.2020


Ответы (1)


Я автор arangojs. Для потомков я хотел бы уточнить, что этот ответ касается arangojs 6, который является текущим выпуском arangojs на момент написания этой статьи, и версии, в которой впервые была добавлена ​​поддержка потоковых транзакций. API может измениться в будущих версиях, хотя в настоящее время это не планируется.

Из-за того, как работают транзакции (начиная с arangojs 6), вам необходимо обратить особое внимание на предостережение, упомянутое в документация для run метода:

Если данная функция содержит асинхронную логику, в транзакции будет запущена только синхронная часть функции. Например. при использовании async / await в транзакции будет выполняться только код до первого await. Обратите внимание на примеры ниже.

Другими словами, async функции, скорее всего, не будут вести себя правильно, если вы просто заключите их в trx.run. Я бы рекомендовал передавать объект транзакции каждой функции и заключать вызовы методов в эти функции в trx.run.

Например:

async runQuery(query, trx) {
    try {
      const cursor = await trx.run(() => this.arangodb.query(query))
      return trx.run(() => cursor.all())
    } catch (error) {
      logger.error('ArangoDB query failed', { stack: error })
      throw error
    }
}

async touchDocument(id, revision, trx) {
    const type = await this.getObjectTypeFromId(id, trx)
    const collection = await this.getCollection(type, false, true, trx)
    const idCollection = await this.getCollection(ID_COLLECTION, false, true, trx)
    const touchAql = aql`
      LET permanentDocId = DOCUMENT(${idCollection}, ${id}).docId
      LET permanentDoc = MERGE( DOCUMENT(permanentDocId),  { _rev : ${revision} })
      UPDATE permanentDoc WITH permanentDoc in ${collection} OPTIONS { ignoreRevs: false } 
      RETURN NEW
    `
    return this.runQuery(touchAql, trx)
}

this.touchDocument(parentId, parentRevision, trx)

Причина этого в том, что trx.run устанавливает весь драйвер в «режим транзакции», выполняет данную функцию, а затем отключает «режим транзакции» после ее выполнения, чтобы несвязанный код случайно не запускался в транзакции.

Недостатком этого подхода является то, что если функция является асинхронной и содержит несколько операторов await, в транзакции будет выполняться только код, ведущий к первому await и включающий его. В вашем коде это означает, что "режим транзакции" отключен после возврата this.getObjectTypeFromId(id). Если сам этот метод содержит несколько await выражений, снова только первое из них будет частью транзакции (и так далее).

Я надеюсь, что это немного проясняет путаницу.

person Alan Plum    schedule 22.01.2020
comment
Спасибо. Я пришел к такому же выводу путем тестирования. Этот вопрос должен быть действительно прямо рассмотрен в документации. Подойдет пример, подобный приведенному выше, со сложным стеком и рекомендуемым шаблоном. - person robross0606; 22.01.2020
comment
Кроме того, язык, который вы только что использовали для перехода к и включения первого await, отличается от языка документации. И в том числе - очень важное уточнение. - person robross0606; 22.01.2020