Method does not catch firestore timeout error - javascript

I have following code where I cannot catch Firestore timeout error when internet is slow. It just returns empty array.
static getEspeciallyProducts() {
return new Promise(async (resolve, reject) => {
try {
const db = Firebase.firestore(),
products = [],
activeRef = await db.collection("products")
.orderBy("id")
.withConverter(ProductConverter)
.get();
for (let doc of activeRef.docs) {
const data = doc.data();
if (_.isObject(data))
products.push(data);
}
resolve(products);
} catch (error) {
reject(error);
}
});
}
However, my another method catches an error:
static getProductById(id) {
return new Promise(async (resolve, reject) => {
try {
const docRef = Firebase.firestore().collection("products").doc(id),
doc = await docRef.withConverter(ProductConverter).get();
if (doc.exists)
resolve(doc.data());
else
resolve(null);
} catch (error) {
reject(error);
}
});
}

Firestore doesn't throw errors in the event that the internet connection is slow or down. It either:
Silently retries until the connection is available, or
Gives up and falls back to the local persistence layer.
If it does #2, you will see a message in the console log about that. But the code making a query will not just fail. Failure are only given for errors that cannot be recovered, such as security rule violations and exceeded limits.
If you want code to fail in the event of a network problem, consider using the REST API instead.

Related

How to connect to a MongoDB asynchronously?

I have a MongoDB database. I'm using Javascript and Node.js (using Mangoose) to access this database. I need to return an array with the name of all the collections in the database. So I use a code that follows.
let connection = mongoose.createConnection(process.env.MONGODB_URI + "/" + dbname);
// Wait 10 seconds for Mongoose to establish the connection.
await new Promise(r => setTimeout(r, 10000));
return connection.db.collections()
.then(stations=>stations.map(stations=>stations.collectionName))
.catch(reason => {
console.error("Error : "+reason);
return null;
});
The code above is working correctly. Now, I'm trying to do the same process asynchronously. For this, I am using the following code.
async function station_list_all(dbname){
return new Promise((resolve,reject) => {
try {
let connection = mongoose.createConnection(process.env.MONGODB_URI + "/" + dbname);
resolve(connection);
}catch(err){
reject(new Error("DB not found!"));
}
})
.then(connection => connection.db)
.then(db => db.collections())
.then(stations=>stations.map(station=>station.collectionName))
.catch(err => {
console.error("Error : "+err);
return null;
});
}
Unfortunately, instead of the collection names, I get the message: Error : TypeError: Cannot read property 'collections' of undefined.
I think db is returning undefined, but... Shouldn't the code wait until db has a valid value?
Try with async await:
try {
await mongoose.connect('mongo url with db name');
// other process
} catch (error) {
handleError(error);
}
Or you can connect using callback:
try {
mongoose.connect('mongo url with db name').then(()=>{
// other process
});
} catch (error) {
handleError(error);
}
In Same way you can try with promise also. There is no need use settimeout.
For More Ref Please visit: Mongoose Connections

Error "Given transaction number * does not match" in mongodb and nodejs

I want to modify two schema while adding data. For that I used ACID transaction of mongodb with nodejs as follow. But, when I run program it displays the error like
(node:171072) UnhandledPromiseRejectionWarning: MongoError: Given transaction number 3 does not match any in-progress transactions. The active transaction number is 2
at MessageStream.messageHandler (/home/user/Projects/project/node_modules/mongodb/lib/cmap/connection.js:272:20)
at MessageStream.emit (events.js:375:28)
at MessageStream.emit (domain.js:470:12)
addData = async(request: Request, response: Response) => {
const session = await stockSchema.startSession()
try {
const userData = request.body
let data = {}
const transaction = await session.withTransaction(async() => {
try {
userData.products.map(async(item: any) => {
await inventorySchema.findOneAndUpdate({ _id: item.materialID }, { $inc: {qty: -item.qty }}, { session });
});
data = new stockSchema(userData);
await data.save({ session });
} catch (error) {
await session.abortTransaction()
throw new Error("Could not create data. Try again.");
}
});
if (transaction) {
session.endSession()
return returnData(response, data, 'Data created successfully.');
} else {
throw new Error("Could not create data. Try again.");
}
} catch (error: any) {
session.endSession();
return Error(response, error.message, {});
}
}
So you might have figured out the answer to this already, but anyway, after months of frustration, and no clear answer on the internet, i finally figured it out.
The problem with your code above is that you are passing session into a database operation (the .findOneAndUpdate function above) that is running within .map . Meaning, your 'transaction session' is being used concurrently, which is what is causing the error. read this: https://www.mongodb.com/community/forums/t/concurrency-in-mongodb-transactions/14945 (it explains why concurrency with transactions creates a bit of a mess.)
Anyway, instead of .map, use a recursive function that fires each DB operation one after another rather than concurrently, and all your problems will be solved.
You could use a function something like this:
const executeInQueue = async ({
dataAry, //the array that you .map through
callback, //the function that you fire inside .map
idx = 0,
results = [],
}) => {
if (idx === dataAry.length) return results;
//else if idx !== dataAry.length
let d = dataAry[idx];
try {
let result = await callback(d, idx);
results.push(result);
return executeInQueue({
dataAry,
callback,
log,
idx: idx + 1,
results,
});
} catch (err) {
console.log({ err });
return results.push("error");
}
};

How to make sure that the onUpdate method of the firebase database only runs after the onCreate method completes

I am trying to sync Firebase database data with a Google spreadsheet. For this, I have two methods that run onCreate and onUpdate in the database in a specific path. The problem is that sometimes the method called by the onUpdate callback is executed before the method called by the onCreate callback. How can I make sure the onCreate method is complete before doing something in onUpdate Method?
By the way, here is the code. Maybe I got some advice about the code in general, because now is the most difficult time to work with the Firebase and JavaScript functions.
exports.appendrecordtospreadsheet = functions.database.ref(`${CONFIG_DATA_PATH}/{USERID}/{GAMENUMBER}`).onCreate(
(snap) => {
var firstRecord = [];
console.log(snap.ref.parent.key);
const key = snap.ref.parent.key;
firstRecord.unshift(key);
firstRecord.push(snap.val()[0])
return appendPromise({
spreadsheetId: CONFIG_SHEET_ID,
range: 'A:Z',
valueInputOption: 'USER_ENTERED',
insertDataOption: 'INSERT_ROWS',
resource: {
values: [firstRecord],
},
});
});
function appendPromise(requestWithoutAuth)
{
return new Promise(async (resolve, reject) =>
{
const client = await getAuthorizedClient();
const sheets = google.sheets('v4');
const request = requestWithoutAuth;
request.auth = client;
return sheets.spreadsheets.values.append(request, (err, response) => {
if (err) {
console.log(`The API returned an error: ${err}`);
return reject(err);
}
return resolve(response.data);
});
});
}
exports.UpdateRecordInSpreadSheet = functions.database.ref(`${CONFIG_DATA_PATH}/{USERID}/{GAMENUMBER}`).onUpdate(
(change) => {
const key = change.after.ref.parent.key;
const updatedRecord = change.after.val();
updatedRecord.unshift(key);
return updatePromise({
spreadsheetId: CONFIG_SHEET_ID,
range: 'A:X',
valueInputOption: 'USER_ENTERED',
resource: {
values: [updatedRecord],
},
});
});
function updatePromise(requestWithoutAuth)
{
return new Promise(async (resolve, reject) =>
{
const client = await getAuthorizedClient();
const sheets = google.sheets('v4');
const updateRequest = requestWithoutAuth;
updateRequest.auth = client;
const getRequest = {
spreadsheetId: CONFIG_SHEET_ID,
range: 'A:A'
};
getRequest.auth = client;
return sheets.spreadsheets.values.get(getRequest, (err, response) => {
if (err) {
console.log(`The API returned an error: ${err}`);
return reject(err);
}
else {
const index = response.data.values[0].lastIndexOf(updateRequest.resource.values[0][0]) + 1;
updateRequest.range = `A${index}:Z${index}`;
return sheets.spreadsheets.values.update(updateRequest, (err, response) => {
if (err) {
console.log(`The API returned an error: ${err}`);
return reject(err);
}
return resolve(response.data);
});
}
});
});
}
Cloud Functions doesn't guarantee order of events. In fact, two events might be processed at the same time. You will need to account for that in your code. Typically it requires use of a transaction to make sure that two concurrently running bits of code do not collide with each other.
There are no workarounds to this - it is just the way Cloud Functions works, and it ensures the product is able to scale up demand without losing events.

Async function does not wait for await function to end

i have an async function that do not work as expected, here is the code :
const onCreateCoachSession = async (event, context) => {
const { coachSessionID } = context.params;
let coachSession = event.val();
let opentokSessionId = 'prout';
await opentok.createSession({ mediaMode: 'relayed' }, function(
error,
session
) {
if (error) {
console.log('Error creating session:', error);
} else {
opentokSessionId = session.sessionId;
console.log('opentokSessionIdBefore: ', opentokSessionId);
const sessionId = session.sessionId;
console.log('Session ID: ' + sessionId);
coachSession.tokbox = {
archiving: true,
sessionID: sessionId,
sessionIsCreated: true,
};
db.ref(`coachSessions/${coachSessionID}`).update(coachSession);
}
});
console.log('opentokSessionIdEnd: ', opentokSessionId);
};
My function onCreateCoachSession trigger on a firebase event (it's a cloud function), but it does not end for opentok.createSession to end, i don't understand why as i put an await before.
Can anyone have an idea why my code trigger directly the last console log (opentokSessionIdEnd)
Here is a screenshot on order of console.log :
It's probably a simple problem of async/await that i missed but i cannot see what.
I thanks in advance the community for the help.
You're using createSession in callback mode (you're giving it a callback function), so it doesn't return a Promise, so it can't be awaited.
Two solutions :
1/ Use createSession in Promise mode (if it allows this, see the doc)
let session = null;
try{
session = await opentok.createSession({ mediaMode: 'relayed' })
} catch(err) {
console.log('Error creating session:', error);
}
or 2/ await a Promise
let session;
try {
session = await new Promise((resolve, reject) => {
opentok.createSession({ mediaMode: 'relayed' }, (error, session) => {
if (error) {
return reject(error)
}
resolve(session);
})
})
} catch (err) {
console.log('Error creating session:', err);
throw new Error(err);
}
opentokSessionId = session.sessionId;
console.log('opentokSessionIdBefore: ', opentokSessionId);
// ...
await means it will wait till the promise is resolved. I guess there is no promise returned in this case. you can create your own promise and handle the case

Handling network errors with axios and Twilio

I have an application that uses axios for it's ajax requests. When a user experiences a network issue (for example, their wifi goes out and they no longer have an internet connection while on my application), I want to make sure that only the first axios request is made, and if I detect there is a network issue, to not attempt any more requests, but instead to retry the same request until successful.
My application performs many requests, including a request every 2.5 seconds (in this example, getData). It also establishes a Twilio connection when the application initializes (it executes twilio() on initialization).
When a connection is lost, the following happens:
getData fails, resulting in a console message of this is a network error.
TwilioDevice.offline is executed. This results in two error messages: first a this is a network error. message (error message #2) when TwilioDevice.offline tries fetchToken(), and then a received an error. message (error message #3) after the fetchToken() fails.
Given #'s 1 and 2, how can I make sure that:
If I experience a network error, I only receive one error message instead of 3 saying that "there was a network error"
My app detects that there is a network error, then tries to re-establish a connection, then, if successful, resumes fetching data, Twilio tokens, etc.
Thanks! Code is below.
example code:
const getData = async () => {
try {
const response = await axios.get('api/data');
return response.data;
} catch (error) {
handleError(error);
}
};
const fetchToken = async () => {
try {
const data = await axios.get('api/twilio-token');
return data.token;
} catch (error) {
return handleError(error);
}
};
const handleError = error => {
if (!error.response) {
console.log("this is a network error.");
} else {
console.log("received an error.");
}
};
twilio.js:
import { Device as TwilioDevice } from 'twilio-client';
const registerEvents = () => {
TwilioDevice.ready(() => {
console.log('Twilio.Device is now ready for connections');
});
TwilioDevice.connect((conn) => {
console.log(`Connecting call with id ${conn.parameters.CallSid}`);
// code to start call
conn.disconnect((connection) => {
console.log(`Disconnecting call with id ${connection.parameters.CallSid}`);
// code to end call
});
});
TwilioDevice.error((error) => {
console.log("Twilio Error:");
console.log(error);
});
TwilioDevice.offline(async () => {
try {
const newTwilioToken = await fetchToken(); // error message #2
return TwilioDevice.setup(newTwilioToken);
} catch (error) {
return handleError(error); // error message #3
}
});
};
export const twilio = async () => {
try {
registerEvents();
const twilioToken = await fetchToken();
TwilioDevice.setup(twilioToken);
} catch (error) {
return handleError(error);
}
};
I would recommend making your fetchToken and getData methods to throw errors rather than handling it themselves so that they can be handled by their outer functions.
Something like,
const getData = async () => {
try {
const response = await axios.get('api/data');
return response.data;
} catch (error) {
throw (error);
}
};
const fetchToken = async () => {
try {
const data = await axios.get('api/twilio-token');
return data.token;
} catch (error) {
throw (error);
}
};
So that when you call twilio() that function can handle the error like retrying etc.

Categories

Resources