Firebase Cloud Functions wait for variables - firebase

I wanna write img_thumb_url to firebase database. In func1A, my code i wrote below where I expect it can breakthrough the while loop when those global variable room and msg_key are not null but what I found it always undefined although those variables become defined with string value when func2B successfully trigggered.
...
while(1){
if(room!=null && msg_key!=null){
console.log('room is ', room, '. msg_key is ', msg_key);
console.log('break from the loop');
break;
}
}
admin.initializeApp();
return admin.database().ref('/msgs/' + room + '/chat/' + msg_key + '/attachment/photo/thumbnail/url2').set(img_thumb_url);
})
But the path above ' '/msgs/' + room + '/chat/' + msg_key + '/attachment/photo/thumbnail/url2 ' depends on variable room and msg_key retrieved from other function func2B which is here,
exports.func2B = functions.database.ref('/msgs/{roomName}/chat/{pushid}')
.onCreate((snapshot, context) => {
// Grab the current value of what was written to the Realtime Database.
const msg = snapshot.val();
if (msg.attachPhotoUrl == null) {
console.log('No photo attachment found');
return null;
}
if (msg_key != null) {
console.log('msg_key no longer null');
return null;
}
console.log('writing url2 for thumbnail in firebase database...');
msg_key = msg.key;
room = msg.roomName;
console.log('room is ',room);
console.log('msg_key is ',msg_key);
console.log('img_thumb_url: ',img_thumb_url);
return ....
});
I'm not sure whether this is the proper way of method but I don't think it is as simple as that. Please help me how to resolve. How can i get that assigned value variable room and msg_key in func2B to func1A?

Related

Firebase cloud functions not catching with fast database updates

Hello I am trying to build a multiplayer game.
I have got a working queue:
.ref('Multiplayer/Queue/{queueCategory}/Players/{playerid}')
.onCreate((snapshot, context) => {
const root = snapshot.ref.root;
var category = context.params.queueCategory;
const gameDir = "Multiplayer/Active/" + category;
var gameID = snapshot.ref.root.child(gameDir).push().key;
root.child("Multiplayer/Queue/" + category + "/Players").once("value").then(players => {
var secondplayer: DataSnapshot | null = null;
functions.logger.log(players.numChildren());
players.forEach(player => {
if(player.val() === "placeholder" && player.key !== context.params.playerid) {
secondplayer = player;
}
});
functions.logger.log(secondplayer);
if(secondplayer === null) return null;
root.child("Multiplayer/Queue/" + category + "/Players").transaction(function (matchmaking) {
//check if player joined differnet game
if (matchmaking === null || matchmaking === undefined || secondplayer === null || matchmaking[context.params.playerid] !== "placeholder" || matchmaking[secondplayer?.key || 1] !== "placeholder") return matchmaking;
matchmaking[context.params.playerid] = gameDir + "/" + gameID + "/player1";
matchmaking[secondplayer.key || -1] = gameDir + "/" +gameID + "/player2";
return matchmaking;
}).then(result => {
var playerval: string = result.snapshot.child(context.params.playerid).val();
var pPath = playerval.split('/');
pPath.pop();
playerval = pPath.join('/');
functions.logger.log("playervalue: " + playerval);
functions.logger.log("gamedir: " + gameDir + gameID);
if(playerval !== gameDir + "/" + gameID) return;
var game = {
gamestate: "init",
category: category,
Players: {
"player1": "",
"player2": ""
}
}
root.child(gameDir + "/" + gameID).set(game).then(snap => {
return null;
}).catch(error => {
console.log(error);
})
}).catch(error => {
console.log(error);
})
return null;
}).catch(error => {
console.log(error);
})
});
This script pairs up players and changes the value of the player in queue to the new gameroom
dir
. Everything works, except when to many players join the queue at once (
breaks down at roughly 1player/sec). I suspect the problem is in this part of the code:
var secondplayer: DataSnapshot | null = null;
functions.logger.log(players.numChildren());
players.forEach(player => {
if(player.val() === "placeholder" && player.key !== context.params.playerid) {
secondplayer = player;
}
});
functions.logger.log(secondplayer);
if(secondplayer === null) return null;
If to many players join the second player will be overlapping with other instances of the functions and ultimately the will terminate after the second player value has been set.
How can I fix this?
Please help me
Cloud functions do not guarantee order of execution. There is a Firecast explaining parallel execution. You can take a look at transactions but your use case doesn't seem to be straightforward as incrementing or decrementing a value.
Cloud functions (any serverless functions) may not be the best choice for all the cases.
You will have to make sure the user is deleted from the node immediately once is player is matched. Using transactions you can match and remove 2 players immediately. So when a third user who was about to match as well will be added to queue instead of in the queue.
You may need queues running on your server (if the reads and writes are so high speed) or refactor the logic that matches your players. Cloud Compute may be a better choice for this. You may be able to use something like OpenMatch with that.
I remember stacking up all the users trying to match in an array and then running a cron job every 2 seconds to make pairs of them. Then I had used realtime database to emit changes to relevant users. Although this may not not be possible in cloud functions as each functions runs independently of each other.

Checking a child's entries for a match

let ref = firebase.database().ref('players').child(playerId).child('voters');
ref.child(uid).once('value', snap => {
var key = snap.key;
console.log("snapkey: " + key + " uid: " + uid)
if (key === uid) {
console.log("Exists")
} else {
console.log("Doesn't exist")
}
});
I'm trying to see if a variable uid, which holds the users unique ID from firebase-auth is present in my database's voters
So for me, when I'm using the app, my uid is vKl6rIUuI0WsbeWVORz3twPUfnd2. So if I go to vote on this Firstname Lastname person, it should tell me I exist in the above image's scenario.
The problem is, it seems to always say it exists. The console.log for key and uid are both putting out my uid. Is it something with the ref.child(uid)...?
let ref = firebase.database().ref('/players/' + playerID + '/voters');
ref.once('value', snap => {
var value = snap.val()
console.log(value)
if (value !== null) {
console.log("Exists")
} else {
console.log("Doesn't exist")
}
});
https://firebase.google.com/docs/database/web/read-and-write#read_data_once
A snapshot will always have a key. Always. And it will be at the location you requested by reference. Whether or not there is data behind that key is irrelevant to the fact that the snapshot will always have a key.
What you need to do is check the data behind that key. Is it null? Then there's no data there. A number? That's data, and it's present.
Use .exists() method:
let ref = firebase.database().ref('players').child(playerId).child('voters');
ref.child(uid).once('value', (snap) => {
console.log(snap.exists()); // This will print true or false
});

How to get a data from firestore while using batch?

I want to perform a batch transaction in firestore. I am storing last key in other collection.
i need to get the last key then increase by 1, then create two documents using this key. How can i do this?
let lastDealKeyRef = this.db.collection('counters').doc('dealCounter')
let dealsRef = this.db.collection('deals').doc(id)
let lastDealKey = batch.get(lastDealKeyRef) // here is the problem..
batch.set(dealsRef, dealData)
let contentRef = this.db.collection('contents').doc('deal' + id)
batch.set(contentRef, {'html': '<p>Hello World</p>' + lastDealKey })
batch.commit().then(function () {
console.log('done') })
If you want to read/write data in a single operation you should be using a transaction.
// Set up all references
let lastDealKeyRef = this.db.collection('counters').doc('dealCounter');
let dealsRef = this.db.collection('deals').doc(id);
let contentRef = this.db.collection('contents').doc('deal' + id);
// Begin a transaction
db.runTransaction(function(transaction) {
// Get the data you want to read
return transaction.get(lastDealKeyRef).then(function(lastDealDoc) {
let lastDealData = lastDealDoc.data();
// Set all data
let setDeals = transaction.set(dealsRef, dealData);
let setContent = transaction.set(contentRef, {'html': '<p>Hello World</p>' + lastDealKey });
// Return a promise
return Promise.all([setDeals, setContent]);
});
}).then(function() {
console.log("Transaction success.");
}).catch(function(err) {
console.error("Transaction failure: " + err);
});
You can read more about transactions and batches here:
https://firebase.google.com/docs/firestore/manage-data/transactions

Unable to delete entry in Firebase database using remove

I'm trying to remove an entry from my Firebase database, but am not able to delete it.
Here's my database structure:
Here's what I've tried:
let subRefs = firebase.database().ref('subscriptions/' + userId).once('value').then(function(snapshot){
let objs = snapshot.val();
for (let key in objs){
if (objs[key].uid == memberId){
console.log('found'); // I see this in the console
//remove the subscription
let ref = firebase.database().ref('subscription/' + userId + '/' + key);
ref.remove().then(function() {
console.log("Remove succeeded");
}).catch(function(error) {
console.log("Remove failed");
});
}
}
});
I've also tried the following approach as suggested here in the docs, but that didn't work either.
let update = {};
update['subscription/' + userId + '/' + key] = null;
firebase.database().ref().update(update).then(function(){
console.log('remove success');
}).catch(function(err){
console.log('remove failed');
});
In both cases, I see the "remove success" log, but when I check the database, it's not actually deleted.

firebase Cloud function transaction working sporadically

I have the cloud function like so:
exports.updateNewsCount = functions.database.ref('/channels/{channelId}/news/{newsId}/')
.onWrite (event => {
const channelId = event.params.channelId;
const newsId = event.params.newsId;
let CntRef = admin.database().ref('/channelDetails/' + channelId + '/newsCnt');
if (event.data.exists() && !event.data.previous.exists()){
return CntRef.transaction(function(current){
if (current){
console.log ('current is not null');
return (current || 0) + 1;
}
else {
console.log('current is null');
return current;
}
},function(error, b, d){
if (error)
console.log(error);
else
console.log ('error is null');
if (b)
console.log('boolean is true');
else
console.log('boolean is false');
if (d)
console.log('snapshot is ' + d);
else
console.log ('snapshot is null');
}).then(()=>{});
} else if (!event.data.exists() && event.data.previous.exists()){
return CntRef.transaction(function(current){
if (current)
return (current || 1) - 1;
else
return current;
}, function(error, b, d){if (error) console.log(error); if (d) console.log(d);}).then(()=>{});
}
});
It fires consistently as I can see the log entries. However, the newsCnt field is not updated as expected. Sometimes it gets updated and sometimes not!!! What am I doing wrong here?
You should expect that a transaction be called potentially multiple times, the first time with null. That's the way transactions work. Please read the documentation here.
In particular note the following callout in that section:
Note: Because your update function is called multiple times, it must
be able to handle null data. Even if there is existing data in your
remote database, it may not be locally cached when the transaction
function is run, resulting in null for the initial value.

Resources