Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nested autoruns on server is sending ready message before all autoruns are ready #47

Open
filipenevola opened this issue Jun 24, 2019 · 4 comments

Comments

@filipenevola
Copy link

Hi, when I use nested autoruns to avoid rerunning everything on every change but with this, on the server, the ready message arrives in the client before both autorun blocks are ready and the documents are published.

If that is a bug I would be glad to help with a PR but maybe I need some directions to get started.

@mitar
Copy link
Member

mitar commented Jun 24, 2019

Hmm. Maybe first provide a short example/test?

@filipenevola
Copy link
Author

filipenevola commented Jun 24, 2019

Sure.

The idea, in summary, is to publish all the meetings and people in those meetings (memberships), and also related data (location of the meeting, people in the meeting, roles, etc).

I didn't reduce the code to avoid you thinking this could be done in a different way due to lack of details. Of course, you can find better ways to do it and I'll be glad to try 😉

export function meetingsRelatedCursors(
  meetingsCursor,
  { communityId, personId: currentPersonId, accountId }
) {
  const {
    labelsIds: labelsIdsWithDuplicates,
    meetingsIds: meetingsIdsWithDuplicates,
    locationsIds: locationsIdsWithDuplicates,
  } = meetingsCursor.fetch().reduce(
    (acc, meeting) => ({
      labelsIds: [...acc.labelsIds, ...(meeting.labelsIds || [])],
      meetingsIds: [...acc.meetingsIds, meeting._id],
      locationsIds: [...acc.locationsIds, meeting.locationId],
    }),
    {
      labelsIds: [],
      meetingsIds: [],
      locationsIds: [],
    }
  );
  const labelsIds = [...new Set(labelsIdsWithDuplicates)];
  const meetingsIds = [...new Set(meetingsIdsWithDuplicates)];
  const locationsIds = [...new Set(locationsIdsWithDuplicates)];

  const locationsCursor = Locations.find({
    _id: { $in: locationsIds },
  });

  const labelsCursor = Labels.find({ _id: { $in: labelsIds } });

  const labelSetsIds = labelsCursor.map(({ labelSetId }) => labelSetId);
  const labelSetsCursor = LabelSets.find({ _id: { $in: labelSetsIds } });

  const defaultRolesCursor = MeetingRoles.findBy('community', {
    communityId,
    isDefault: false,
  });
  const defaultRolesIds = defaultRolesCursor.map(({ _id }) => _id);

  const $or = [
    currentPersonId && { personId: currentPersonId },
    { rolesIds: { $in: defaultRolesIds } },
  ].filter(Boolean);

  this.autorun(() => {
    const meetingMembershipsCursor = MeetingMemberships.find({
      meetingId: { $in: meetingsIds },
      $or,
    });
    const {
      peopleIds: peopleIdsWithDuplicates,
      rolesIds: rolesIdsWithDuplicates,
    } = meetingMembershipsCursor.fetch().reduce(
      (acc, membership) => ({
        peopleIds: [...acc.peopleIds, membership.personId],
        rolesIds: [...acc.rolesIds, ...(membership.rolesIds || [])],
      }),
      {
        peopleIds: [],
        rolesIds: [],
      }
    );
    const peopleIds = [...new Set(peopleIdsWithDuplicates)];
    const rolesIds = [...new Set(rolesIdsWithDuplicates)];

    const peopleCursor = People.find(
      {
        accountId,
        _id: { $in: peopleIds },
      },
      { fields: { _id: 1 } }
    );
    const profilesCursor = PeopleProfiles.find(
      {
        communityId,
        personId: { $in: peopleIds },
      },
      {
        fields: {
          communityId: 1,
          firstName: 1,
          lastName: 1,
          companyName: 1,
          personId: 1,
          title: 1,
          photoId: 1,
        },
      }
    );

    const imagesIds = profilesCursor
      .map(({ photoId }) => photoId)
      .filter(Boolean);
    const imagesCursor = Images.find(
      {
        communityId,
        _id: { $in: imagesIds },
      },
      {
        fields: {
          url: 1,
        },
      }
    );
    const rolesCursor = MeetingRoles.find({
      _id: { $in: rolesIds },
    });
    return [
      meetingMembershipsCursor,
      peopleCursor,
      profilesCursor,
      imagesCursor,
      rolesCursor,
    ];
  });
  return [meetingsCursor, labelsCursor, labelSetsCursor, locationsCursor];
}


Meteor.publish(
  'meetings/meetings#appBySearch',
  securedScope(
    currentPersonScope(function appBySearch(terms) {
      this.autorun(() => {
        const meetingsCursor = Meetings.findBy('search', terms);

        return meetingsRelatedCursors.call(this, meetingsCursor, terms);
      });
    })
  )
);

Then before the meetings are in the client my code already receives the ready message. Later in the next tracker reaction, the meetings are available but the problem is that I show for a few milliseconds "No meetings available" for the user. I can delay this message but the time to delay will depend on the internet speed from the client then, for now, I'm not using two autoruns.

The problem that I was solving with these nested autoruns is to avoid querying the Meetings collection very often as it does not change too much, but Memberships collection changes all the time when people interact with our app then it's important to be in a separated autorun block.

Also, see that meetingsRelatedCursors is used by other publications.

  • findBy is our homemade find that applies some transformations in the terms of the query, in the end, it will return a regular collection find()

@mitar
Copy link
Member

mitar commented Sep 20, 2019

Sorry. I was writing my PhD thesis so I was away for a bit.

Yes, I think the problem is that code currently marks whole publish as ready when the ready is called the first time from wherever. And for internal autoruns that happens once all returned cursors are published for the first time. But I do not see in your code where you are publishing anything more after those cursors. So ready should still happen only after [meetingsCursor, labelsCursor, labelSetsCursor, locationsCursor] are published for the first time.

I suggest you create a simple test case and add it to tests, which should be currently failing because of the behavior you are noticing. Keep it as simple as possible.

@filipenevola
Copy link
Author

Thanks, we will work on that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants