У меня есть список в базе данных реального времени Firebase, структурированный следующим образом:
list: {
change: {
rate: integer,
last: Date
},
lock: string,
elements: {
...
},
size: integer
}
Клиенты только добавляют элементы в список. Существует облачная функция, запускаемая событием onCreate
на дочернем элементе. Он транзакционно увеличивает list.size
в базе данных, а затем выполняет вычисления для элементов, которые могут включать удаление всех или некоторых дочерних элементов. И есть еще одна облачная функция, запускаемая событием onDelete
для дочерних элементов, которое только транзакционно уменьшает list.size
в базе данных. Проблема в том, что счетчик через некоторое время становится неточным. Иногда он показывает больше элементов, которые есть в списке, иногда меньше. Ниже приведен [адаптированный] код:
exports.element_add = functions.database.ref('/list/elements/{element}).onCreate(
(snapshot, context) => {
const elements = snapshot.ref.parent;
return new Promise(
(resolve, reject) => {
const start = new Date().getTime();
async.waterfall(
[
// read and write to list.change
// ...
(next) => {
// increment list size
_changeSize(elements.parent, +1, 1, next)
},
// acquire lock if required and do calculations on list elements
// ...
],
// finally
function (e) {
// release the lock
// ...
// handle errors
if (e)
return reject(e);
resolve();
}
);
});
});
exports.element_remove = functions.database.ref('/list/elements/{element}).onDelete(
(snapshot, context) => {
const elements = snapshot.ref.parent;
return new Promise(
function (resolve, reject) {
_changeSize(elements.parent, -1, 1,
function (e) {
if (e)
return reject(e);
resolve();
}
)
}
)
});
function _changeSize(list, delta, attempt, done) {
list.child('size').transaction(
// update
(size) => {
if (size === null)
return null;
return size + delta;
},
// finally
(e, committed, snapshot) {
if (e) {
console.error(`size transaction error: ${e}, retrying in 1 sec`);
// try again
return setTimeout(_changeSize, 1000, list, delta, attempt + 1, done);
}
console.log(committed ? `new list size is ${snapshot.val()}` : 'list size was not updated');
// return new size if committed
done(null, committed ? snapshot.val() : undefined);
},
// do not apply locally
false
);
}
За исключением двух вышеупомянутых облачных функций, ни одна другая сущность не работает с list.size
. Вопрос в том, почему значение list.size
не совпадает с c с list.elements
?
Обновление 2020-02-26 : по-видимому, некоторые из событий onCreate / onDelete быть принятым более одного раза! Мы наблюдали их прибытие даже в 3 раза между экземплярами облачной функции. Это нормальное поведение или что-то не так?