【发布时间】:2017-08-30 15:12:41
【问题描述】:
我正在将 Firebase 用于群组协作应用程序(如 Whatsapp),并且我正在使用云函数来确定哪些电话联系人也在使用我的应用程序(再次类似于 Whatsapp)。 Cloud Function 运行良好,直到我开始在 Functions Log 中看到以下日志以进行某些调用。
Function execution took 60023 ms, finished with status: 'timeout'
我做了一些调试,发现对于这个特定的用户,他的手机通讯录上有很多联系人,因此很明显,找出哪些联系人正在使用该应用程序所需的工作也增加了,以至于花了60多秒。下面是云函数的代码
// contactsData is an array of contacts on the user's phone
// Each contact can contain one more phone numbers which are
// present in the phoneNumbers array. So, essentially, we need
// to query over all the phone numbers in the user's contact book
contactsData.forEach((contact) => {
contact.phoneNumbers.forEach((phoneNumber) => {
// Find if user with this phoneNumber is using the app
// Check against mobileNumber and mobileNumberWithCC
promises.push(ref.child('users').orderByChild("mobileNumber").
equalTo(phoneNumber.number).once("value").then(usersSnapshot => {
// usersSnapshot should contain just one entry assuming
// that the phoneNumber will be unique to the user
if(!usersSnapshot.exists()) {
return null
}
var user = null
usersSnapshot.forEach(userSnapshot => {
user = userSnapshot.val()
})
return {
name: contact.name,
mobileNumber: phoneNumber.number,
id: user.id
}
}))
promises.push(ref.child('users').orderByChild("mobileNumberWithCC").
equalTo(phoneNumber.number).once("value").then(usersSnapshot => {
// usersSnapshot should contain just one entry assuming
// that the phoneNumber will be unique to the user
if(!usersSnapshot.exists()) {
return null
}
var user = null
usersSnapshot.forEach(userSnapshot => {
user = userSnapshot.val()
})
return {
name: contact.name,
mobileNumber: phoneNumber.number,
id: user.id
}
}))
});
});
return Promise.all(promises)
}).then(allContacts => {
// allContacts is an array of nulls and contacts using the app
// Get rid of null and any duplicate entries in the returned array
currentContacts = arrayCompact(allContacts)
// Create contactsObj which will the user's contacts that are using the app
currentContacts.forEach(contact => {
contactsObj[contact.id] = contact
})
// Return the currently present contacts
return ref.child('userInfos').child(uid).child('contacts').once('value')
}).then((contactsSnapshot) => {
if(contactsSnapshot.exists()) {
contactsSnapshot.forEach((contactSnapshot) => {
previousContacts.push(contactSnapshot.val())
})
}
// Update the contacts on firease asap after reading the previous contacts
ref.child('userInfos').child(uid).child('contacts').set(contactsObj)
// Figure out the new, deleted and renamed contacts
newContacts = arrayDifferenceWith(currentContacts, previousContacts,
(obj1, obj2) => (obj1.id === obj2.id))
deletedContacts = arrayDifferenceWith(previousContacts, currentContacts,
(obj1, obj2) => (obj1.id === obj2.id))
renamedContacts = arrayIntersectionWith(currentContacts, previousContacts,
(obj1, obj2) => (obj1.id === obj2.id && obj1.name !== obj2.name))
// Create the deletedContactsObj to store on firebase
deletedContacts.forEach((deletedContact) => {
deletedContactsObj[deletedContact.id] = deletedContact
})
// Get the deleted contacts
return ref.child('userInfos').child(uid).child('deletedContacts').once('value')
}).then((deletedContactsSnapshot) => {
if(deletedContactsSnapshot.exists()) {
deletedContactsSnapshot.forEach((deletedContactSnapshot) => {
previouslyDeletedContacts.push(deletedContactSnapshot.val())
})
}
// Contacts that were previously deleted but now added again
restoredContacts = arrayIntersectionWith(newContacts, previouslyDeletedContacts,
(obj1, obj2) => (obj1.id === obj2.id))
// Removed the restored contacts from the deletedContacts
restoredContacts.forEach((restoredContact) => {
deletedContactsObj[restoredContact.id] = null
})
// Update groups using any of the deleted, new or renamed contacts
return ContactsHelper.processContactsData(uid, deletedContacts, newContacts, renamedContacts)
}).then(() => {
// Set after retrieving the previously deletedContacts
return ref.child('userInfos').child(uid).child('deletedContacts').update(deletedContactsObj)
})
以下是一些示例数据
// This is a sample contactsData
[
{
"phoneNumbers": [
{
"number": "12324312321",
"label": "home"
},
{
"number": "2322412132",
"label": "work"
}
],
"givenName": "blah5",
"familyName": "",
"middleName": ""
},
{
"phoneNumbers": [
{
"number": "1231221221",
"label": "mobile"
}
],
"givenName": "blah3",
"familyName": "blah4",
"middleName": ""
},
{
"phoneNumbers": [
{
"number": "1234567890",
"label": "mobile"
}
],
"givenName": "blah1",
"familyName": "blah2",
"middleName": ""
}
]
// This is how users are stored on Firebase. This could a lot of users
"users": {
"id1" : {
"countryCode" : "91",
"id" : "id1",
"mobileNumber" : "1231211232",
"mobileNumberWithCC" : "911231211232",
"name" : "Varun"
},
"id2" : {
"countryCode" : "1",
"id" : "id2",
"mobileNumber" : "2342112133",
"mobileNumberWithCC" : "12342112133",
"name" : "Ashish"
},
"id3" : {
"countryCode" : "1",
"id" : "id3",
"mobileNumber" : "123213421",
"mobileNumberWithCC" : "1123213421",
"name" : "Pradeep Singh"
}
}
在这种特殊情况下,contactsData 包含 1046 条目,其中一些条目有两个 phoneNumbers。因此,假设我需要检查总共有 1500 电话号码。我正在为数据库中的用户创建查询以与mobileNumber 和mobileNumberWithCC 进行比较。因此,在 Promise 完成之前,该函数将进行总共 3000 查询,我猜测完成所有这些查询需要 60 多秒,因此 Cloud Function 超时。
我的几个问题是:
- 是否所有这些查询都需要超过 60 秒?鉴于它在 Firebase 基础架构中运行,我希望它能够更快地完成。
- 有没有办法增加函数的超时限制?我目前正在使用 Blaze 计划。
我还将感谢上述功能的任何替代实施建议,以缓解问题。谢谢!
【问题讨论】:
-
我很困惑。您正在查询所有这些数据,但您没有对它做任何事情。我所看到的是你正在从 then() 返回一个对象。通常你应该从 then() 返回另一个 promise 或 实际上在链式 then() 中使用它。
-
@DougStevenson 为了简单起见,我跳过了那段代码。基本上,上面的代码将返回一个正在使用我的应用程序的联系人数组。我将这些联系人上传到应用程序读取的 Firebase,以使用应用程序填充联系人列表。我还执行其他操作,例如找出自上次同步以来所有已删除、恢复和重命名的联系人,然后更新在应用程序中使用此类联系人的组。我已经稍微更新了代码以反映这一点。根据我的记录,函数在
Promise.all调用完成之前超时。 -
好吧,在任何情况下查询数千个项目似乎都是多余的,尤其是如果您只打算使用一个项目。您应该找到一种方法来构建数据和查询,以便在开始时只返回您需要的少数项目。
-
@DougStevenson 我不确定你为什么认为我会使用单个项目。我对所有这些一千个查询的结果感兴趣。例如。假设我有 100,000 个用户使用我的应用程序,我存储在数据库的
users分支中,并且每个用户存储的信息非常少。现在一个用户启动了这个应用程序,他在这个通讯录中有 1000 个联系人,我需要弄清楚这 1000 个联系人中有哪些正在使用这个应用程序。所以,我至少要运行 1000 个查询。如果所有这 1000 个联系人都在使用该应用程序,我将使用所有这些查询的结果。 -
在这段代码中:
usersSnapshot.forEach(userSnapshot => { user = userSnapshot.val() })您正在迭代 usersSnapshot 中的所有内容,但只记住该集合中的单个用户,无论最后出现什么。如果您添加一些关于您在每个阶段要完成的任务的 cmets 以及一些数据样本,也许会有所帮助,因为这很难想象您在这里构建的所有内容。
标签: javascript firebase firebase-realtime-database google-cloud-functions