Начал получать эту ошибку, когда моя облачная функция находится под большой нагрузкой, срабатывающая более 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, верно?