Firebase Cloud Function Cron Update - javascript

i have function i am using to do updates to my databased based on Cron Job. It looks like this ( worth saying i had a lot of help here )
exports.minute_job =
functions.pubsub.topic('minute-tick').onPublish((event) => {
var ref = admin.database().ref("comments")
ref.once("value").then((snapshot) => {
var updates = {};
snapshot.forEach(commentSnapshot => {
var comment = commentSnapshot.val();
var currentRating = comment.rating - comment.lastRating;
var newScore = ((Math.abs(comment.internalScore) * 0.95) + currentRating) * -1;
if(newScore < 0.000001) { newScore = 0.000001}
updates[commentSnapshot.key + "/lastRating"] = comment.rating;
updates[commentSnapshot.key + "/internalScore"] = newScore;
});
ref.update(updates);
})
});
Its all working perfectly, except i am getting this warning from Firebase logs:
"Function returned undefined, expected Promise or value"
Thanks for any help

Since your Cloud Function doesn't return a value, the Google Cloud Functions engine contain doesn't know when the code is finished. In many cases this means that GCF will simply terminate the contain of your function right after the closing }) has executed. But at that point, your code is likely still loading data from the database, and it definitely hasn't update the database yet.
The solution is to return a promise, which is just an object that will signal when you're done with the database. The good news is that both once() and update() already return promises, so you can just return those:
exports.minute_job =
functions.pubsub.topic('minute-tick').onPublish((event) => {
var ref = admin.database().ref("comments")
return ref.once("value").then((snapshot) => {
var updates = {};
snapshot.forEach(commentSnapshot => {
var comment = commentSnapshot.val();
var currentRating = comment.rating - comment.lastRating;
var newScore = ((Math.abs(comment.internalScore) * 0.95) + currentRating) * -1;
if(newScore < 0.000001) { newScore = 0.000001}
updates[commentSnapshot.key + "/lastRating"] = comment.rating;
updates[commentSnapshot.key + "/internalScore"] = newScore;
});
return ref.update(updates);
})
});
Now Google Cloud Functions know that your code is still working after the }), because you returned a promise. And then when your update() is done, it resolves the promise it return and Google Cloud Functions can close the container (or at the very least: stop charging you for its usage).

Related

Cannot set inner property of null

I'm trying to make an app that will help me with my workouts and help me constantly lift more so that I can get stronger. I keep getting the error "Cannot set inner property of null" on the very bottom when I try to output the variables to the HTML. It's the code under the last section of comments that I am getting the error. Can some give me some guidance on how to fix this?
//Get the variables from the user. This will be the previous exercise or the exercise that the user has performed
const userSet = document.getElementById("set-user-1");
const userReps = document.getElementById("reps-user-1");
const userWeight = document.getElementById("weight-user-1");
var futureSet;
var futureReps;
var futureWeight;
//Define the functions that need to be done between the previous exercise and the next exercise
function getNewSet(previousSet) {
return previousSet;
}
function getNewRep(previousReps){
if(previousReps < 12) {
return previousReps + 2;
} else {
previousReps = 6;
return previousReps;
}
}
function getNewWeight(previousReps, previousWeight) {
if(previousReps < 12){
return previousWeight;
} else {
previousWeight = previousWeight + 10;
return previousWeight;
}
}
//Make a function that runs all the functions with the user input
function getNewWorkout() {
futureSet = getNewSet(parseInt(userSet));
futureReps = getNewRep(parseInt(userReps));
futureWeight = getNewWeight(parseInt(userReps, userWeight));
return futureSet;
return futureReps;
return futureWeight;
}
//Output will go to the future exercise dividers
document.getElementById("future-sets").innerHTML = futureSet;
document.getElementById("future-reps").innerHTML = futureReps;
document.getElementById("future-weight").innerHTML = futureWeight;
The error is in the last two lines of this function. A function can only return one value, it might be worth putting all the values in an array
and then returning it.
//Make a function that runs all the functions with the user input
function getNewWorkout() {
futureSet = getNewSet(parseInt(userSet));
futureReps = getNewRep(parseInt(userReps));
futureWeight = getNewWeight(parseInt(userReps, userWeight));
return futureSet;
//error here
return futureReps;
return futureWeight;
}
So you can update your code as follows to return object,
function getNewWorkout() {
const futureSet = getNewSet(parseInt(userSet));
const futureReps = getNewRep(parseInt(userReps));
const futureWeight = getNewWeight(parseInt(userReps, userWeight));
return {futureSet, futureReps, futureWeight};
}
Which you can access through object.
This kind of error comes when you load the script before DOM is ready. You can try to load the script at the end of the HTML, usually in footer. This should fix your error.
Also, please call the function 'getNewWorkout()' in your script to get expected output.

NODE.JS App gets stuck getting 2.5 million of records through and api

I've got the code that retrieves couple of millions of rows from a db and a couple of millions through an api.
let closedOrdersStartDate;
preparedOrdersPromise = tickApiConnector.obtainToken().then(function () {
return ordersController.getAllTrades(0, [], 0, accountIdsToRun);
}).then(function (trades) {
closedOrdersStartDate = new Date();
return Promise.all([trades, fthsApiConnector.getData('closed_orders', '&sort=id', 10000, 0)]);
}).then(function (tradesAndClosedOrderIds) {
//stuck before getting there
console.log('now processing orders time spent from starting getting closed_orders till now is: ' +
((new Date().getTime() - closedOrdersStartDate.getTime())/ 1000) + ' seconds');
return ordersController.processOrders(tradesAndClosedOrderIds[0], tradesAndClosedOrderIds[1]);
});
The app gets stuck after calling that getData() function.
getData: async function (entityName, getParams = '', perRequest = 10000, skip = 0) {
if(getParams.indexOf('&take=') !== -1) {
return fthsApiRequest(entityName, getParams);
}
const totalCount = await fthsApiRequest(entityName, getParams + '&getCount');
let result = [];
let count = 0;
while(count < totalCount) {
result = result.concat(await fthsApiRequest(entityName, getParams + '&take=' + perRequest + '&skip=' + count));
count += perRequest;
}
return result;
}
The function executes till the last request(I see it in logs) and after that the script gets unresponsable. I thought that it could be a memory leak and I've rewritten that getData() function in different ways but still, there's enough memory on the server and the script doesn't consume even a bit of it. Still I get 100% of CPU load in a while after that last iteration of getData() is rant. After that the app gets stuck forever.
I've tried profiling it. And there are thousands of Code move event for unknown code: 0x2a5c24bfb4c0, I'm not sure what that means, but there could be a clue in that. Here's the V8.log
The possible problem maybe in block :
while(count < totalCount) { result = result.concat(await fthsApiRequest(entityName, getParams + '&take=' + perRequest + '&skip=' + count)); count += perRequest; }
Ensure that the api give response. And on the last statement :
return result;
In async function the better use :
return Promise.resolve(result);

Limit number of records in firebase

Every minute I have a script that push a new record in my firebase database.
What i want is delete the last records when length of the list reach a fixed value.
I have been through the doc and other post and the thing I have found so far is something like that :
// Max number of lines of the chat history.
const MAX_ARDUINO = 10;
exports.arduinoResponseLength = functions.database.ref('/arduinoResponse/{res}').onWrite(event => {
const parentRef = event.data.ref.parent;
return parentRef.once('value').then(snapshot => {
if (snapshot.numChildren() >= MAX_ARDUINO) {
let childCount = 0;
let updates = {};
snapshot.forEach(function(child) {
if (++childCount <= snapshot.numChildren() - MAX_ARDUINO) {
updates[child.key] = null;
}
});
// Update the parent. This effectively removes the extra children.
return parentRef.update(updates);
}
});
});
The problem is : onWrite seems to download all the related data every time it is triggered.
This is a pretty good process when the list is not so long. But I have like 4000 records, and every month it seems that I screw up my firebase download quota with that.
Does anyone would know how to handle this kind of situation ?
Ok so at the end I came with 3 functions. One update the number of arduino records, one totally recount it if the counter is missing. The last one use the counter to make a query using the limitToFirst filter so it retrieve only the relevant data to remove.
It is actually a combination of those two example provided by Firebase :
https://github.com/firebase/functions-samples/tree/master/limit-children
https://github.com/firebase/functions-samples/tree/master/child-count
Here is my final result
const MAX_ARDUINO = 1500;
exports.deleteOldArduino = functions.database.ref('/arduinoResponse/{resId}/timestamp').onWrite(event => {
const collectionRef = event.data.ref.parent.parent;
const countRef = collectionRef.parent.child('arduinoResCount');
return countRef.once('value').then(snapCount => {
return collectionRef.limitToFirst(snapCount.val() - MAX_ARDUINO).transaction(snapshot => {
snapshot = null;
return snapshot;
})
});
});
exports.trackArduinoLength = functions.database.ref('/arduinoResponse/{resId}/timestamp').onWrite(event => {
const collectionRef = event.data.ref.parent.parent;
const countRef = collectionRef.parent.child('arduinoResCount');
// Return the promise from countRef.transaction() so our function
// waits for this async event to complete before it exits.
return countRef.transaction(current => {
if (event.data.exists() && !event.data.previous.exists()) {
return (current || 0) + 1;
} else if (!event.data.exists() && event.data.previous.exists()) {
return (current || 0) - 1;
}
}).then(() => {
console.log('Counter updated.');
});
});
exports.recountArduino = functions.database.ref('/arduinoResCount').onWrite(event => {
if (!event.data.exists()) {
const counterRef = event.data.ref;
const collectionRef = counterRef.parent.child('arduinoResponse');
// Return the promise from counterRef.set() so our function
// waits for this async event to complete before it exits.
return collectionRef.once('value')
.then(arduinoRes => counterRef.set(arduinoRes.numChildren()));
}
});
I have not tested it yet but soon I will post my result !
I also heard that one day Firebase will add a "size" query, that is definitely missing in my opinion.

Throttle / Debounce number of calls per second

I'm working with an API that only allows you to make 200 calls per second (1000ms) using a promise request library like request-promise or axios how can you debounce / throttle the requests that are made to a URL / server using rx.js? I noticed a throttle method in the rx documentation, but it won't calculate calls per second.
This is a function that wraps a promise and will queue them to account for a API-rate limit. I'm looking for similar functionality with Rx.
var Promise = require("bluebird")
// http://stackoverflow.com/questions/28459812/way-to-provide-this-to-the-global-scope#28459875
// http://stackoverflow.com/questions/27561158/timed-promise-queue-throttle
module.exports = promiseDebounce
function promiseDebounce(fn, delay, count) {
var working = 0, queue = [];
function work() {
if ((queue.length === 0) || (working === count)) return;
working++;
Promise.delay(delay).tap(function () { working--; }).then(work);
var next = queue.shift();
next[2](fn.apply(next[0], next[1]));
}
return function debounced() {
var args = arguments;
return new Promise(function(resolve){
queue.push([this, args, resolve]);
if (working < count) work();
}.bind(this));
}
}
So I had a similar issue the other day for rate limiting access to a resource. I came across this repo bahmutov/node-rx-cycle. Here is the example in a Plunker Demo I put together to demonstrate this. It takes input from a text input field and prepends it to a <ul>. Each <li> is only prepended every 1000ms, and any others are queued.
// Impure
const textInput = document.querySelector('.example-input');
const prependToOutput = function(item) {
const ul = document.querySelector('.example-output');
const li = document.createElement('li');
li.appendChild(document.createTextNode(item));
ul.insertBefore(li, ul.firstChild);
};
// Pure
const eventTargetValue = function(ele) { return ele.target.value; };
const textInputKeyUpStream = Rx.Observable
.fromEvent(textInput, 'keyup')
.map(eventTargetValue);
// Stream
rateLimit(textInputKeyUpStream, 1000)
.timeInterval()
.map(function(ti) { return ti.value + ' (' + ti.interval + 'ms)'; })
.subscribe(prependToOutput);
Hope this helps.

How do I get the gender from a particular user when updating a different table? Azure mobile services

I have a table called Subscription and another table called Client I need the gender of the Client who owns the subscription every time I make an update. Here's my update script:
function update(item, user, request) {
var subscriptionId = item.id;
var subscriptionActivitiesTable = tables.getTable("SubscriptionActivity");
var userTable = tables.getTable("User");
var activityTable = tables.getTable("Activity");
var userGender = userTable.where({id: item.UserId}).select('Gender').take(1).read();
console.log(userGender);
activityTable.where({PlanId:item.PlanId, Difficulty: item.Difficulty}).read({
success: function(results){
var startDate = item.StartDate;
results.forEach(function(activity)
{
var testDate = new Date(startDate.getFullYear(),startDate.getMonth(), startDate.getDate());
testDate.setDate(testDate.getDate() + activity.Sequence + (activity.Week*7));
subscriptionActivitiesTable.insert({SubscriptionId: subscriptionId,
ActivityId: activity.id, ShowDate: new Date(testDate.getFullYear(),
testDate.getMonth(), testDate.getDate()), CreationDate: new Date()});
})
}
});
var planWeeks = 12;//VER DE DONDE SACAMOS ESTE NUMERO
var idealWeight = 0;
if (userGender === "Male")
{
idealWeight = (21.7 * Math.pow(parseInt(item.Height)/100,2));
}
else
{
idealWeight = (23 * Math.pow(parseInt(item.Height)/100,2));
}
var metabolismoBasal = idealWeight * 0.95 * 24;
var ADE = 0.1 * metabolismoBasal;
var activityFactor;
if (item.Difficulty === "Easy")
{
activityFactor = 1.25;
}
else if(item.Difficulty === "Medium")
{
activityFactor = 1.5;
}
else
{
activityFactor = 1.75;
}
var caloricRequirement = ((metabolismoBasal + ADE)*activityFactor);
activityTable.where(function(item, caloricRequirement){
return this.PlanId === item.PlanId && this.Type != "Sport" &&
this.CaloricRequirementMin <= caloricRequirement &&
this.CaloricRequirementMax >= caloricRequirement;}, item, caloricRequirement).read({
success: function(results)
{
var startDate = item.StartDate;
results.forEach(function(activity)
{
for (var i=0;i<planWeeks;i++)
{
var testDate = new Date(startDate.getFullYear(),startDate.getMonth(), startDate.getDate());
testDate.setDate(testDate.getDate() + activity.Sequence + (i*7));
subscriptionActivitiesTable.insert({SubscriptionId: subscriptionId,
ActivityId: activity.id, ShowDate: new Date(testDate.getFullYear(),
testDate.getMonth(), testDate.getDate()), CreationDate: new Date()});
}
})
}
})
request.execute();
}
I tried the code above and clientGender is undefined. As you can see I want to use the gender to set the idealWeight.
The read() method expects a function to be passed in on the success parameter - it doesn't return the result of the query like you'd think.
Try something like this instead:
function update(item, user, request) {
var clientTable = tables.getTable("Client");
var clientGender = 'DEFAULT';
clientTable.where({id: item.ClientId}).select('Gender').take(1).read({
success: function(clients) {
if (clients.length == 0) {
console.error('Unable to find client for id ' + item.ClientId);
} else {
var client = client[0];
clientGender = client.Gender;
// since we're inside the success function, we can continue to
// use the clientGender as it will reflect the correct value
// as retrieved from the database
console.log('INSIDE: ' + clientGender);
}
}
});
// this is going to get called while the clientTable query above is
// still running and will most likely show a value of DEFAULT
console.log('OUTSIDE: ' + clientGender);
}
In this sample, the client table query is kicked off, with a callback function provided in the success parameter. When the query is finished, the callback function is called, and the resulting data is displayed to the log. Meanwhile - while the query is still running, that is - the next statement after the where/take/select/read fluent code is run, another console.log statment is executed to show the value of the clientGender field outside the read function. This code will run while the read statement is still waiting on the database. Your output should look something like this in the WAMS log:
* INSIDE: Male
* OUTSIDE: Default
Since the log shows the oldest entries at the bottom, you can see that the OUTSIDE log entry was written sometime before the INSIDE log.
If you're not used to async or functional programming, this might look weird, but as far as I've found, this is now node works. Functions nested in functions nested in functions can get kind of scary, but if you plan ahead, it probably won't be too bad :-)

Categories

Resources