Облачный код - "Тайм-аут выполнения" во время сохраненияВсе вызовы после сохранения
ситуация
- У меня есть приложение для обмена сообщениями Android, обеспечивающее следующие функции
- отправить сообщение прямым сообщением одному выбранному получателю
- создать публичное объявление, которое получают все пользователи, использующие приложение (кроме автора)
- каждый пользователь видит на своем телефоне список сообщений, которые он получил
- каждое сообщение либо непрочитано, прочитано, либо удалено
- Я использую Parse.com в качестве бэк-энда
Текущая реализация
На Android клиенте
- При появлении нового сообщения, новое сообщение по запросу
MessageRequest
класс создан - Если сообщение должно быть прямым, messageRequest имеет тип 0, иначе тип 1
- Если сообщение является прямым, в объекте messageRequest хранится получатель
- Объект messageRequest хранится на parse.com
На бэк-энде parse.com
в afterSave
MessageRequest проверяется, является ли сообщение прямым или общедоступным и основано на этом
- в случае прямого сообщения - один новый объект сообщения
Message
класс создан и сохранен - в случае публичного объявления - для каждого пользователя, кроме автора, создается новый объект сообщения и добавляется в список сообщений, затем список сохраняется
В обоих случаях данные, такие как содержимое, тип и т. Д., Копируются из объекта messageRequest во вновь создаваемый объект (ы) сообщений.
Причина создания отдельного сообщения для каждого пользователя заключается в том, что каждый пользователь может иметь его в другом состоянии (не прочитано, прочитано, удалено).
Столбец состояния, представляющий непрочитанное, прочитанное, удаленное состояние, устанавливается (непрочитанным) для объекта сообщения.
проблема
Когда я звоню ParseObject.saveAll
метод в afterSave
MessageRequest, я получаю тайм-аут выполнения - ошибка тайм-аута запроса
Я думаю, что причина в том, что существуют определенные ограничения по времени, в течение которых запрос должен выполняться в облачном коде. В моем случае я создаю около 100 сообщений для 1 MessageRequest
Это не так уж много для меня, но, возможно, я ошибаюсь.
Исходный код
var generateAnnouncement = function(messageRequest, recipients) {
var messageList = [];
for (var i = 0; i < recipients.length; i++) {
var msg = new Message();
msg.set("type", 1);
msg.set("author", messageRequest.get("author"));
msg.set("content", messageRequest.get("content"));
msg.set("recipient", recipients[i]);
msg.set("status", 0)
messageList.push(msg);
}
Parse.Object.saveAll(messageList).then(function(list) {
}, function(error) {
console.error(error.message);
});
}
Parse.Cloud.afterSave("MessageRequest", function(request) {
var mr = request.object;
var type = mr.get("type");
if (type == 0) {
generateDirectMessage(mr);
} else {
var query = new Parse.Query(Parse.User);
query.notEqualTo("objectId", mr.get("author").id);
query.find().then(function(allUsersExceptAuthor) {
generateAnnouncement(mr, allUsersExceptAuthor);
}, function(error) {
console.error(error.message);
});
}
});
Как бы вы предложили решить эту проблему?
Дополнительные мысли
- Моя единственная другая идея, как решить эту проблему, - это иметь только один объект сообщения и два столбца с именами, например, visibleBy и deleteFor, которые будут содержать списки пользователей, которые уже просмотрели сообщение или удалили сообщение для них.
В этом случае я не очень уверен в производительности запросов
Кроме того, я знаю, что многие из вас думают, почему он не использует таблицу для разделения отношения M:N между MessageRequest(который в этом случае может фактически называться Message) и User?
- Мой ответ заключается в том, что у меня было это решение, но с ним было сложнее работать в коде Android, больше указателей, больше включений в запросы и т. Д.
- Более того, мне все равно пришлось бы создавать одинаковое количество объектов, представляющих статус для каждого пользователя в бэк-энде на parse.com, поэтому я думаю, что проблема с тайм-аутом выполнения в конце концов будет такой же
Обновление - макет, представляющий "Входящие" пользователя
В папке "Входящие" пользователь видит как прямые сообщения, так и публичные объявления. Они отсортированы в хронологическом порядке.
Обновление № 2 - использование массивов для определения, кто просматривал, а кто помечен как удаленный
- У меня есть только один
Message
объект, через тип я идентифицирую, является ли он прямым или публичным - Два столбца массива были добавлены
viewedBy
- содержит пользователей, которые уже просмотрели сообщениеdeletedFor
- содержащие пользователей, которые пометили сообщение как удаленное для них
Тогда мой запрос для всех сообщений, не удаленных в данный момент вошедшим в систему пользователем, выглядит следующим образом
//direct messages for me
ParseQuery<Message> queryDirect = ParseQuery.getQuery(Message.class);
queryDirect.whereEqualTo("type", 0);
queryDirect.whereEqualTo("recipient", ParseUser.getCurrentUser());
//public announcements
ParseQuery<Message> queryAnnouncements = ParseQuery.getQuery(Message.class);
queryAnnouncements.whereEqualTo("type", 1);
//I want both direct and public
List<ParseQuery<Message>> queries = new ArrayList<ParseQuery<Message>>();
queries.add(queryDirect);
queries.add(queryAnnouncements);
ParseQuery<Message> queryMessages = ParseQuery.or(queries);
//... but only those which I haven't deleted for myself
queryMessages.whereNotEqualTo("deletedFor", ParseUser.getCurrentUser());
//puting them in correct order
queryMessages.addDescendingOrder("createdAt");
//and attaching the author ParseUser object (to get e.g. his name or URL to photo)
queryMessages.include("author");
queryMessages.findInBackground(new FindCallback<Message>() {/*DO SOMETHING HERE*/});
1 ответ
Я бы предложил изменить вашу схему, чтобы лучше поддерживать публичные сообщения.
У вас должна быть одна копия общедоступного сообщения, поскольку нет необходимости изменять само сообщение.
Затем вы должны сохранить только статус для каждого пользователя, если он не является непрочитанным. Это был бы другой стол.
Когда приходит MessageRequest с типом 1, создайте новый PublicMessage
, не создавайте никаких строк статуса, поскольку все будут использовать статус по умолчанию "непрочитанный". Это делает ваш afterSave
обработчик работает чисто, так как он всегда создает только один новый объект, либо Message
или PublicMessage
,
Поскольку каждый пользователь читает сообщение или удаляет его, создайте новый PublicMessageStatus
строка для этого пользователя с правильным статусом.
При отображении общедоступных сообщений пользователю вы будете делать два запроса:
- Запрос для
PublicMessage
вероятно с некоторым диапазоном дат - Запрос для
PublicMessageStatus
с фильтром наuser
соответствие текущего пользователя иmatchesQuery('publicMessage', publicMessageQuery)
ограничение с использованием клона первого запроса
Затем на стороне клиента вам нужно будет объединить эти два параметра, чтобы скрыть / удалить те, у которых статус "удален", и пометить тех, у кого статус "прочитан" соответственно.
Обновление на основе обратной связи
Вы можете выбрать вместо этого использовать один Message
класс для публичных / личных сообщений, а также MessageStatus
класс для обработки статуса.
Публичный против частного будет основан на Message.recipient
быть пустым или нет.
Чтобы получить все сообщения для текущего пользователя:
// JavaScript sample since you haven't specified a language
// assumes Underscore library available
var Message = Parse.Object.extend('Message');
var MessageStatus = Parse.Object.extend('MessageStatus');
var publicMessageQuery = new Parse.Query(Message);
publicMessageQuery.doesNotExist('recipient');
publicMessageQuery.notEqualTo('author', currentUser);
var privateMessageQuery = new Parse.Query(Message);
privateMessageQuery.equalTo('recipient', currentUser);
var messagesQuery = new Parse.Query.or(publicMessageQuery, privateMessageQuery);
messagesQuery.descending('createdAt');
// set any other filters to apply to both queries
var messages = [];
messageQuery.find().then(function(results) {
messages = _(results).map(function (message) {
return { message: message, status: 'unread', messageId: message.objectId };
});
var statusQuery = new Parse.Query(MessageStatus);
statusQuery.containedIn('message', results);
statusQuery.equalTo('user', currentUser);
// process status in order so last applies
statusQuery.ascending('createdAt');
return
}).then(function(results) {
_(results).each(function (messageStatus) {
var messageId = messageStatus.get('message').objectId;
_(messages).findWhere({ messageId: messageId }).status = messageStatus.get('status');
});
});
// optionally filter messages that are deleted
messages = _(messages).filter(function(message) { return message.status !== 'deleted'; });
// feed messages array to UI...