Firebase:在开始事务之前检查引用是否存在足够好

时间:2018-08-29 17:06:52

标签: firebase firebase-realtime-database transactions

我想知道是否足以测试引用是否存在 在此参考上开始交易之前? 例如:通过使用.once('value')和snapshot.exists()

我的意思是,如果支票不在交易之内,那么是否存在其他用户在支票之后,在事务处理执行程序功能之前删除引用的风险?

====已编辑,以包含最少的完整代码=====

这是我在实时数据库中的数据:

activeOffers
    -LKohyZ58cnzn0vCnt9p
        details
            direction: "city"
            seatsCount: 2
            timeToGo: 5
        uid: "-ABSIFJ0vCnt9p8387a"    ---- offering user

这是我的代码流:

===== index.js =====

entries = require('./entries');

/// cloud function
exports.TEST_askOfferSeats = functions.https.onCall((data, context) => {
    console.log('data: ' + JSON.stringify(data));
    return entries.askSeats(data);
});

这是邮递员发送的我的测试数据:

{
 "data": 
  {
     "uid": "-FGKKSDFGK12387sddd",    ---- the requesting/asking user
     "id": "-LKpCACQlL25XTWJ0OV_",
     "details":
     {
          "direction": "city",
          "seatsCount": 1,
          "timeToGo": 5
     }
  }
}

===== entry.js =======

exports.askSeats = function(data) {
const TAG = '[askSeats]: ';

var entryRef = db.ref('activeOffers/' + data.id);
return globals.exists(entryRef)
    .then((found)=>{
        if (found) {
            return dealSeats(entryRef, data);
        } else {
            return 'Offer not found [' + data.id + ']';
        }
    });
}

===== globals.js ======

exports.exists = (ref)=>{
    return ref.once('value')
        .then((snapshot)=>{
            return (snapshot.exists());
        });
}

===== entry.js =====

dealSeats = function(entryRef, data) {
    const TAG = '[dealSeats]: ';
    return entryRef.transaction((entry)=>{
        if (entry) {
            if ((entry.deals) && (entry.deals[data.uid])) {
                throw new Error('You've already made a deal.');
            } else if (entry.details.seatsCount >= data.details.seatsCount) {
                entry.details.seatsCount -= data.details.seatsCount;
                var deal = [];
                deal.status = 'asked';
                deal.details = data.details;
                if (!entry.deals) {
                    entry.deals = {};
                }
                entry.deals[data.uid] = deal;
            } else {
                throw new Error('Not enought seats.');
            }
        }
        return entry;
    })
    .then((success)=>{
        return success.snapshot.val();
    })
    .catch((error)=>{
        return Promise.reject(error);
    });
}

顺便说一句:这是“抛出新错误(......)”是中断交易的正确方法吗?

=========更新为最终来源===

感谢道格·史蒂文森。

因此,这是我最后能正常工作的资源。如果有人发现潜在问题,请告诉我。谢谢。

dealSeats = function(entryRef, data) {
    const TAG = '[dealSeats]: ';
    var abortReason;

    return entryRef.transaction((entry)=>{
        if (entry) {
            if ((entry.deals) && (entry.deals[data.uid])) {
                abortReason = 'You already made a reservation';
                return; // abort transaction
            } else if (entry.details.seatsCount >= data.details.seatsCount) {
                entry.details.seatsCount -= data.details.seatsCount;
                var deal = [];
                deal.status = 'asked';
                deal.details = data.details;
                if (!entry.deals) {
                    entry.deals = {};
                }
                entry.deals[data.uid] = deal;
                // Reservation is made
            } else {
                abortReason = 'Not enought seats';
                return; // abort transaction
            }
        }
        return entry;
     })
    .then((result)=>{ // resolved
        if (!result.committed) { // aborted
            return abortReason;
        } else {
            let value = result.snapshot.val();
            if (value) {
                return value;
            } else {
                return 'Offer does not exists';
            }
        }
    })
     .catch((reason)=>{ // rejected
        return Promise.reject(reason);
    });
}

1 个答案:

答案 0 :(得分:2)

如果在事务之前读取了一个值,然后在事务内部再次读取了该值,则绝对不能保证事务内部的第二次读取将产生与事务之前在外部进行的初始读取相同的结果。可以在执行交易时对其进行修改。

如果您要进行真正的原子更新,请仅在事务本身内检查参与事务的值,并确定在事务处理程序中要做什么。