Ошибка кода ошибки облачного ключа: 9 FAILED_PRECONDITION - PullRequest
1 голос
/ 23 октября 2019

Начал получать эту ошибку, когда моя облачная функция находится под большой нагрузкой, срабатывающая более 5 раз / сек.

Error: 9 FAILED_PRECONDITION: This transaction has been invalidated by 
a later transaction in the same session. 

Я выполняю транзакцию и на чтение, и на запись с клиентом nodejs.

Документация гласит:

Operation was rejected because the system is not in a state required 
for the operation's execution.

Транзакция завершается и сохраняет данные в Spanner, поэтому транзакция не отклоняется. Кто-нибудь знает, что это значит?

Вот мой код:

'use strict';

const { Spanner } = require('@google-cloud/spanner');

const constants = require('./constants');
const tableNames = constants.tableNames;


const spanner = new Spanner({ projectId: constants.projectId });

exports.spannerSync = async (data, context) => {

    const instance = spanner.instance(constants.instanceId);
    const database = instance.database(constants.databaseId);

    try {
        let profileExists;
        const [qOneRows] = await exists(attrA, attrB, database);

        if (qOneRows.length !== 0 && qOneRows !== undefined) {
            profileExists = true;
        }

        upsertNewProfile(attrA, attrB, profileExists, database);

    } catch (error) {
        console.log(error);
       
    }

};

function exists(attrA, attrB, database) {
    const query = {
        sql: {
            DML_PROFILE_EXISTS
        },
        params: {
            attr: attrA,
            attrB: attrB
        }
    };

    return database.run(query);
}

function deleteProfile(attrA, attrB, transaction) {
    const query = {
        sql: DML_DELETE_PROFILE,
        params: {
            attr: attrA,
            attrB: attrB
        }
    };
    return transaction.runUpdate(query);
}

function existsInTableB(attrB, attrA, database) {
    const query = {
      sql: EXISTS_DML_QUERY,
      params: {
        attrA: attrA,
        attrB: attrB
      }
    }
    return database.run(query);

}

async function upsertNewProfile(
    attrA, attrB,
    profileExists,
    database
) {
    let inActive = attrA.status;

    database.runTransaction(async (err, transaction) => {
        if (err) {
            console.error('Error running transaction:  ' + err);
            return;
        }
        if (profileExists) {
         try {
            const deleted = await deleteProfile(attrA, attrB, 
            transaction);
          } catch (error) {
            console.log("ERROR   " + error);
           
        }

        }

        transaction.upsert(tableNames.A, attrA);

        transaction.upsert(tableNames.B, attrB);

        transaction.upsert(tableNames.C, attrB.attrA);

        transaction.upsert(tableNames.D, attrB.attrB);

        transaction.upsert(tableNames.E, attrB.attrC);

        transaction.upsert(tableNames.F, attrB.attrD);

        transaction.upsert(tableNames.G, attrB.attrE);

        transaction.upsert(tableNames.H, attrB.attrF);

        transaction.upsert(tableNames.I, attrB.attrG);

        transaction.upsert(tableNames.J, attrB.attrH);

        transaction.upsert(tableNames.F, attrB.attrI);
        
        if (inActive) {
            let [pidExistInBL] = await existsInTableB(attrA, attrB, database);
            if (pidExistInBL.length == 0) {
                transaction.upsert(tableNames.B, getObject(attrA, attrB));
            }
        }

        try {

            transaction.commit((error) => {

                if (!error) {
                    console.log("Tables where successfully updated");
                 
                } else {
                    console.log("ERROR   " + error);
                }
            });

        } catch (error) {
            console.log("ERROR   " + error);
           
        } finally {

            transaction.end();
        }
    });

}

Извините, что try / catch потерялся при записи кода в SO. Это есть в коде, и я улавливаю ошибку, но не откатываю транзакцию, если deleteProfile не удается.

Есть также некоторые другие детали, которые я не упомянул ?. Также появилась еще одна ошибка, которая говорит о том, что я достиг предела своей квоты cpu для облачных функций, и функция настроена на повторную попытку (с тех пор я продлила квоту), и проблема исчезла.

Итак, я подозреваю, что когда облачная функция достигает предела квоты и происходит сбой deleteProfile, функции повторяются снова и снова. Потому что количество выполнений и сеансов Spanner стремительно возрастает при достижении предела квоты. Все еще немного сбитый с толку, если он повторяет и открывает больше сеансов, а затем транзакция в каждой облачной функции повторно запрашивает запрос, будет ли это разумным объяснением для одновременного наблюдения пиковых значений загрузки ЦП Spanner?

Затем я должен также применить откат транзакции в предложении catch для deleteProfile, верно?

1 Ответ

3 голосов
/ 23 октября 2019

Cloud Spanner может одновременно выполнять только одну транзакцию в сеансе . Полученное сообщение об ошибке указывает, что вы запустили вторую транзакцию в сеансе, в котором уже была активная транзакция.

Сообщение об ошибке возвращается, когда вы пытаетесь выполнить оператор / запрос для первой транзакции, котораяболее не действительно. Вторая транзакция, как правило, будет успешной.

Похоже, вы столкнулись с какой-то проблемой состояния гонки / утечки сеанса. У вас есть пример кода или некоторые дополнительные сведения о коде, который генерирует эту ошибку?


РЕДАКТИРОВАТЬ после образца кода

Интересно, может ли эта проблема возникнуть, если

const deleted = await deleteProfile(attrA, attrB, transaction)

завершается неудачей, так как это приведет к прекращению транзакции без явного завершения транзакции. Если это повторяющаяся ошибка, новая транзакция будет автоматически повторена в том же сеансе. Поскольку первоначальная транзакция не была явно завершена, эта транзакция может генерировать описанное вами сообщение об ошибке, в то время как повторная транзакция будет выполнена успешно.

Вы можете попытаться поместить этот оператор в блок try-catch, аналогичный вашемузафиксировать и посмотреть, если это решит.

...