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

Firebase:在启动事务之前检查引用是否存在是否足够好,firebase,firebase-realtime-database,transactions,Firebase,Firebase Realtime Database,Transactions,我想知道它是否足以测试引用是否存在 在我开始此引用的事务之前? e、 g:通过使用.once('value')和snapshot.exists() 我的意思是,如果支票在交易之外,是否存在其他用户在支票之后和交易执行器功能之前删除引用的风险 ===编辑以包含最小完整代码==== entries = require('./entries'); /// cloud function exports.TEST_askOfferSeats = functions.https.onCall((data,

我想知道它是否足以测试引用是否存在 在我开始此引用的事务之前? e、 g:通过使用.once('value')和snapshot.exists()

我的意思是,如果支票在交易之外,是否存在其他用户在支票之后和交易执行器功能之前删除引用的风险

===编辑以包含最小完整代码====

entries = require('./entries');

/// cloud function
exports.TEST_askOfferSeats = functions.https.onCall((data, context) => {
    console.log('data: ' + JSON.stringify(data));
    return entries.askSeats(data);
});
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 + ']';
        }
    });
}
exports.exists = (ref)=>{
    return ref.once('value')
        .then((snapshot)=>{
            return (snapshot.exists());
        });
}
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);
    });
}
这是我在实时数据库中的数据:

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);
});
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 + ']';
        }
    });
}
exports.exists = (ref)=>{
    return ref.once('value')
        .then((snapshot)=>{
            return (snapshot.exists());
        });
}
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);
    });
}
这是我的测试数据,由邮递员发送:

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

entries = require('./entries');

/// cloud function
exports.TEST_askOfferSeats = functions.https.onCall((data, context) => {
    console.log('data: ' + JSON.stringify(data));
    return entries.askSeats(data);
});
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 + ']';
        }
    });
}
exports.exists = (ref)=>{
    return ref.once('value')
        .then((snapshot)=>{
            return (snapshot.exists());
        });
}
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);
    });
}
==globals.js====

entries = require('./entries');

/// cloud function
exports.TEST_askOfferSeats = functions.https.onCall((data, context) => {
    console.log('data: ' + JSON.stringify(data));
    return entries.askSeats(data);
});
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 + ']';
        }
    });
}
exports.exists = (ref)=>{
    return ref.once('value')
        .then((snapshot)=>{
            return (snapshot.exists());
        });
}
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);
    });
}
==entries.js===

entries = require('./entries');

/// cloud function
exports.TEST_askOfferSeats = functions.https.onCall((data, context) => {
    console.log('data: ' + JSON.stringify(data));
    return entries.askSeats(data);
});
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 + ']';
        }
    });
}
exports.exists = (ref)=>{
    return ref.once('value')
        .then((snapshot)=>{
            return (snapshot.exists());
        });
}
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);
    });
}
顺便问一下:这是不是“抛出新错误(……”)是打破交易的正确方法

==========使用最终源更新===

entries = require('./entries');

/// cloud function
exports.TEST_askOfferSeats = functions.https.onCall((data, context) => {
    console.log('data: ' + JSON.stringify(data));
    return entries.askSeats(data);
});
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 + ']';
        }
    });
}
exports.exists = (ref)=>{
    return ref.once('value')
        .then((snapshot)=>{
            return (snapshot.exists());
        });
}
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);
    });
}

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


如果您想要真正的原子更新,请只检查事务本身中参与事务的值,并决定在事务处理程序中执行什么操作。

既然事务已经触发了对值的读取,为什么要使用自己的额外代码来执行读取?对我来说,这听起来有点像一个问题:你想用额外的读取来完成什么?那么你的意思是在我的例子中,我在另一个问题()中描述了我在导出中有额外的代码。askSeats(…)函数在调用dealSeats(entryRef,…)函数之前检查entryRef是否存在我的transactio逻辑在哪里?跳过此现有检查并直接转到dealSeats(entryRef,…)可以吗。事务处理是否正确?请包含您在这个问题中询问的最小完整代码。好的,我已经包含了与此流程相关的代码和数据。是的。它的价值是明确的,但是引用本身的存在又如何呢?事务中的空值(在处理第一个“null case^后)类似于不存在的refwrence/pathI不理解。如果由Firestore SDK创建,引用对象永远不会为null。我已经包含了“最小完整源代码”。请你看看我的意思好吗。我使用实时数据库。同样有效吗?如果交易中的“If(entry)”足以检查具有给定id的报价是否存在,那就太好了。最好用您期望的方式来更新您的问题。