Облачный код - "Тайм-аут выполнения" во время сохраненияВсе вызовы после сохранения

ситуация

  • У меня есть приложение для обмена сообщениями Android, обеспечивающее следующие функции
    • отправить сообщение прямым сообщением одному выбранному получателю
    • создать публичное объявление, которое получают все пользователи, использующие приложение (кроме автора)
    • каждый пользователь видит на своем телефоне список сообщений, которые он получил
    • каждое сообщение либо непрочитано, прочитано, либо удалено
  • Я использую Parse.com в качестве бэк-энда

Текущая реализация

На Android клиенте

  1. При появлении нового сообщения, новое сообщение по запросу MessageRequest класс создан
  2. Если сообщение должно быть прямым, messageRequest имеет тип 0, иначе тип 1
  3. Если сообщение является прямым, в объекте messageRequest хранится получатель
  4. Объект 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 строка для этого пользователя с правильным статусом.

При отображении общедоступных сообщений пользователю вы будете делать два запроса:

  1. Запрос для PublicMessageвероятно с некоторым диапазоном дат
  2. Запрос для 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...
Другие вопросы по тегам