Node.js 当需要比较更新的时间戳时,如何解决Firebase实时数据库服务器端时间戳的波动性?
在阅读之后,我的印象是,一旦对象访问数据库,时间戳占位符将计算一次并保持不变,但我的情况并非如此:Node.js 当需要比较更新的时间戳时,如何解决Firebase实时数据库服务器端时间戳的波动性?,node.js,firebase,firebase-realtime-database,Node.js,Firebase,Firebase Realtime Database,在阅读之后,我的印象是,一旦对象访问数据库,时间戳占位符将计算一次并保持不变,但我的情况并非如此: // Example on Node: > const db = f.FIREBASE_APP.database(); > const timestamp = f.FIREBASE_APP.database.ServerValue.TIMESTAMP; > const ref = db.ref('/test'); > ref.on( ... 'child_added
// Example on Node:
> const db = f.FIREBASE_APP.database();
> const timestamp = f.FIREBASE_APP.database.ServerValue.TIMESTAMP;
> const ref = db.ref('/test');
> ref.on(
... 'child_added',
... function(snapshot) {
..... console.log(`Timestamp from listener: ${snapshot.val().timestamp}`);
..... }
... )
> var child_key = "";
> ref.push({timestamp: timestamp}).then(
... function(thenable_ref) {
..... child_key = thenable_ref.key;
..... }
... );
Timestamp from listener: 1534373384299
> ref.child(child_key).once('value').then(
... function(snapshot) {
..... console.log(`Timestamp after querying: ${snapshot.val().timestamp}`);
..... }
... );
> Timestamp after querying: 1534373384381
> 1534373384299 < 1534373384381
true
底线是,如果需要依赖不可变的服务器端时间戳,请记住这一点,或者解决它。当您执行ref.push{timestamp:timestamp}时,Firebase客户端会立即对客户端上的时间戳进行估计,并在本地为此触发事件。然后它将命令发送到服务器
Firebase客户端从服务器接收到响应后,将检查实际时间戳是否与其估计值不同。如果确实不同,客户将触发对账事件
通过在设置值之前附加值侦听器,您可以最容易地看到这一点。您将看到它与服务器的初始估计值和最终值一起启动
另见:
警告:在浪费了一天之后,最终的解决方案是根本不使用Firebase服务器时间戳,如果您必须在类似于下面的用例中比较它们的话。当事件足够快时,第二次“值”更新可能根本不会触发 Frank在回答中描述的双重更新条件的一个解决方案是,获取最终的服务器时间戳值为1,以嵌入一个on“event”。。。“添加的子项”中的侦听器。。。和2删除“事件”上的。。。在特定用例允许的情况下,侦听器将立即启动
> const db = f.FIREBASE_APP.database();
> const ref = db.ref('/test');
> const timestamp = f.FIREBASE_APP.database.ServerValue.TIMESTAMP;
> ref.on(
'child_added',
function(child_snapshot) {
console.log(`Timestamp in 'child_added': ${child_snapshot.val().timestamp}`);
ref.child(child_snapshot.key).on(
'value',
function(child_value_snapshot) {
// Do a timestamp comparison here and remove `on('value',...)`
// listener here, but keep in mind:
// + it will fire TWICE when new child is added
// + but only ONCE for previously added children!
console.log(`Timestamp in embedded 'event': ${child_value_snapshot.val().timestamp}`);
}
)
}
)
// One child was already in the bank, when above code was invoked:
Timestamp in 'child_added': 1534530688785
Timestamp in embedded 'event': 1534530688785
// Adding a new event:
> ref.push({timestamp: timestamp});null;
Timestamp in 'child_added': 1534530867511
Timestamp in embedded 'event': 1534530867511
Timestamp in embedded 'event': 1534530867606
在我的CQRS/ES案例中,事件被写入/event_存储路径,“添加的子事件”侦听器在新事件出现时更新累积状态,其中每个事件都有一个ServerValue.TIMESTAMP。侦听器比较新事件和状态的时间戳,以确定是否应应用新事件或是否已应用新事件。这在服务器重新启动以构建内存中的内部状态时最为重要,但这里有一个关于如何处理单/双点火的简短概述:
event_store.on(
'child_added',
function(event_snapshot) {
const event_ref = event_store.child(event_id)
event_ref.on(
'value',
function(event_value_snapshot){
const event_timestamp = event_value_snapshot.val().timestamp;
if ( event_timestamp <= state_timestamp ) {
// === 1 =======
event_ref.off();
// =============
} else {
var next_state = {};
if ( event_id === state.latest_event_id ) {
next_state["timestamp"] = event_timestamp;
Object.assign(state, next_state);
db.ref("/state").child(stream_id).update(state);
// === 2 =======
event_ref.off();
// =============
} else {
next_state = event_handler(event_snapshot, state);
next_state["latest_event_id"] = event_id;
Object.assign(state, next_state);
}
}
}
);
}
);
当服务器重新启动时,在'child_added'上。。。遍历/event_存储中已存在的所有事件,并附加“value”,。。。动态地在所有子项上执行,并将事件的时间戳与当前状态的时间戳进行比较
如果事件的时间早于当前状态event_timestamp这不是你在这里展示作品的方式。数据在写入后不会在数据库中自动更改,直到再次写入。如果它真的像这样工作,它将完全打破人们对听众工作方式的期望。@DougStevenson我正要测试Frank的解释,但他所说的是有道理的,因为在随后的查询中,值确实是不同的,即在听众上,以及之后。我正试图尽可能习惯地使用Firebase的功能,但这个问题确实让我感到困惑,我需要解决它。我只花了大约10分钟,没有多少人会用这种方式使用DB,但这仍然是一个令人不快的惊喜。如果我做错了什么,请告诉我。啊,是的,弗兰克是对的。首先得到一个估计值,然后是实际值。在那之后就不应该改变了。你是说这是故意的,什么也做不了?在这种情况下不应该记录这一点吗?可能还有其他情况下,比较服务器端时间戳是否相等很重要,但这会破坏所有这些情况。实现方式是否相同?
event_store.on(
'child_added',
function(event_snapshot) {
const event_ref = event_store.child(event_id)
event_ref.on(
'value',
function(event_value_snapshot){
const event_timestamp = event_value_snapshot.val().timestamp;
if ( event_timestamp <= state_timestamp ) {
// === 1 =======
event_ref.off();
// =============
} else {
var next_state = {};
if ( event_id === state.latest_event_id ) {
next_state["timestamp"] = event_timestamp;
Object.assign(state, next_state);
db.ref("/state").child(stream_id).update(state);
// === 2 =======
event_ref.off();
// =============
} else {
next_state = event_handler(event_snapshot, state);
next_state["latest_event_id"] = event_id;
Object.assign(state, next_state);
}
}
}
);
}
);